Managing custom tools in Splunk MCP Server

Custom tools allow external Splunk applications to register their own tools with the Splunk MCP server. Once registered, these tools can be enabled and made available to AI assistants that interact with your Splunk environment.

The following actions can be taken with custom tools:
Action HTTP Method Description
Create a custom tool POST Register a single new tool.
Batch replace tool POST Atomically replace all tools for an app.
Enable or disable a tool POST Toggle whether a tool is active.
List or get tools GET View registered tools.
Update a tool PUT Edit an existing tool's definition.
Delete tools DELETE Remove one tool or all tools for an app.

Key concepts

See the following table for some key concepts on MCP Server custom tools:

Concept Description
Tool ID

A globally unique identifier in the format <external_app_id>:<tool_name>. For example my_app:search_logs.

External App ID

The Splunk app that owns the tool for example my_app ). Used to namespace tools and enables bulk operations per app.

Enabled tools A tool must be explicitly enabled before it becomes available to the MCP server. Enabling a tool also checks for name collisions with other active tools.
Built-in tools Tools shipped with the MCP server app itself. They cannot be modified or deleted through the API call.
Execution type

A tool can execute an SPL query ("type": "spl") or make an API call ("type": "api").

Authentication

All requests are authenticated using a Splunk platform authentication token belonging to a user that has the mcp_tool_admin capability. The token is passed as a bearer token in the Authorization header.

CODE
Authorization: Bearer <splunk_token>
Content-Type: application/json

Base URL

All examples provided assume the following base URL. Replace <splunk_host> and <port> with your Splunk instance values:

CODE
https://<splunk_host>:<port>/services/mcp_tools

Create a custom tool

Register a single new custom tool.

  • Method: POST

  • Content type: application/json

Request body

See the following table for request body field information:

Field Type Required Description
name string Yes Tool name. Alphanumeric but may include underscores. Must start with a letter. Used as part of the Tool ID.
title string Yes Human-readable display name shown to AI assistants.
description string Yes Explains what the tool does. Good descriptions improve AI tool selection.
inputSchema object Yes JSON Schema (type: "object") describing the tool's parameters. See Input Schema.
_meta object Yes Execution configuration, tags, examples, and app metadata. See Meta Reference.
enabled boolean No Set true to enable the tool immediately after creation. Default: false.
override boolean No When true and enabled: true, overrides any currently-enabled tool with the same name. Default: false.

Example: SPL execution tool

This example registers a tool whose execution type is SPL ("_meta.execution.type": "spl").

When invoked, the MCP server substitutes the {{...}} placeholders into the SPL template and runs the resulting query against Splunk.

JSON
curl -k -X POST \
  "https://<splunk_host>:8089/services/mcp_tools" \
  -H "Authorization: Bearer <splunk_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "search_error_logs",
    "title": "Search Error Logs",
    "description": "Searches Splunk for error log events matching a keyword within a given index.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "keyword": {
          "type": "string",
          "description": "The error keyword to search for.",
          "examples": ["OOM", "timeout", "connection refused"]
        },
        "index": {
          "type": "string",
          "description": "Splunk index to search.",
          "default": "main"
        },
        "max_results": {
          "type": "integer",
          "description": "Maximum number of results to return.",
          "default": 20,
          "minimum": 1,
          "maximum": 500
        }
      },
      "required": ["keyword"]
    },
    "_meta": {
      "external_app_id": "my_monitoring_app",
      "execution": {
        "type": "spl",
        "template": "index={{index}} {{keyword}} | head {{max_results}}",
        "row_limiter": true,
        "time_range": true
      },
      "tags": ["logs", "errors", "search"],
      "examples": [
        {
          "name": "Search for OOM errors",
          "description": "Find out-of-memory errors in the last hour",
          "expected_use": "When a user asks about memory issues",
          "arguments": {"keyword": "OOM", "index": "main"}
        }
      ]
    },
    "enabled": true
  }'

SPL execution fields

See the following table for SPL execution fields:
Note: The SPL execution fields of template, row_limiter, time_range, and guardrails must not be set for API execution tool.
Field Required Description
template Yes SPL query template. Use {{param_name}} placeholders that map to inputSchema properties.
row_limiter No, default = true When true, automatically appends a row_limit argument to cap result size and prevent runaway queries.
time_range No, default = true When true, automatically adds earliest_time / latest_time arguments for time-bounded searches.
guardrails No, default = false Enables additional safety checks on the generated SPL.

Example: API execution tool

This example registers a tool whose execution type is API ("_meta.execution.type": "api").

When invoked, the MCP server substitutes the {{...}} placeholders into the endpoint, headers, params, and body fields, and issues an HTTP request against Splunk's REST API.

Use API tools when the operation requires Splunk REST API call rather than a search operation.
PYTHON
curl -k -X POST \
  "https://<splunk_host>:8089/services/mcp_tools" \
  -H "Authorization: Bearer <splunk_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "create_saved_search",
    "title": "Create Saved Search",
    "description": "Creates a new saved search in the given Splunk app namespace.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "app": {
          "type": "string",
          "description": "Splunk app namespace that will own the saved search.",
          "default": "search"
        },
        "search_name": {
          "type": "string",
          "description": "Name to give the new saved search."
        },
        "search": {
          "type": "string",
          "description": "SPL query that the saved search will run."
        },
        "description": {
          "type": "string",
          "description": "Human-readable description of the saved search.",
          "default": ""
        }
      },
      "required": ["search_name", "search"]
    },
    "_meta": {
      "external_app_id": "my_monitoring_app",
      "execution": {
        "type": "api",
        "method": "POST",
        "endpoint": "/servicesNS/nobody/{{app}}/saved/searches",
        "headers": {
          "Accept": "application/json",
          "Content-Type": "application/x-www-form-urlencoded"
        },
        "params": {
          "output_mode": "json"
        },
        "body": {
          "name": "{{search_name}}",
          "search": "{{search}}",
          "description": "{{description}}"
        }
      },
      "tags": ["saved-search", "rest"],
      "examples": [
        {
          "name": "Create an errors saved search",
          "description": "Save an SPL query that finds error events in the last 24 hours",
          "expected_use": "When a user asks to save a search for repeated reuse",
          "arguments": {
            "app": "search",
            "search_name": "Errors in the last 24 hours",
            "search": "index=main error earliest=-24h",
            "description": "All error events from the last day"
          }
        }
      ]
    },
    "enabled": true
  }

API execution fields

See the following table of API execution fields:

Note: The API execution fields of method, endpoint, headers, params, and body must not be set for SPL execution tool
Field Required Description
method Yes HTTP method: GET, POST, PUT, PATCH, or DELETE.
endpoint Yes Target path or URL. Splunk REST paths (e.g. /services/...) are routed through the local splunkd instance.
headers, params, body No Use headers to add HTTP headers, params to include query parameters in the URL, and body to define the request payload. To insert user-provided values dynamically, use {{param_name}} inside any string value.
Note: The parameter name must match a property defined in inputSchema field.

Success response: 201 created

JSON
{
  "tool_id": "my_monitoring_app:get_saved_search"
}

Error responses

See the following table for error response information:

Status Code Reason
400 Bad Request validation_error One or more fields failed validation. The details array lists every problem.
409 Conflict name_conflict A custom tool with the same name already exists.
Example:
JSON
{
  "error": true,
  "code": "validation_error",
  "message": "Tool definition failed validation.",
  "details": [
    "'name' must start with a letter and contain only alphanumeric characters and underscores.",
    "'description' is required and must be a non-empty string."
  ]
}

Batch replace tools for an app

Atomically replace all tools registered under an external_app_id. Inserts new, updates changed, and deletes missing tools in one call with rollback on failure.

  • Method: POST

  • Content-Type: application/json

Request body

See the following table for request body field information:

Field Type Required Description
external_app_id string Yes The app namespace to replace tools for.
tools array<object> Yes Array of tool definitions. Pass [] to delete all tools for the app.

Example

Replace all tools associated with an app. The following request deletes all existing tools for my_monitoring_app and creates a new set of tools specified in the payload:

JSON
curl -k -X POST \
  "https://<splunk_host>:8089/services/mcp_tools" \
  -H "Authorization: Bearer <splunk_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "external_app_id": "my_monitoring_app",
    "tools": [
      {
        "name": "search_error_logs",
        "title": "Search Error Logs",
        "description": "Searches Splunk for error log events matching a keyword.",
        "inputSchema": {
          "type": "object",
          "properties": {
            "keyword": {"type": "string", "description": "Error keyword to search for."}
          },
          "required": ["keyword"]
        },
        "_meta": {
          "external_app_id": "my_monitoring_app",
          "execution": {
            "type": "spl",
            "template": "index=main {{keyword}} | head 20"
          },
          "tags": ["logs", "errors"]
        }
      },
      {
        "name": "get_host_metrics",
        "title": "Get Host Metrics",
        "description": "Retrieves CPU and memory metrics for a specific host.",
        "inputSchema": {
          "type": "object",
          "properties": {
            "hostname": {"type": "string", "description": "Target host name."}
          },
          "required": ["hostname"]
        },
        "_meta": {
          "external_app_id": "my_monitoring_app",
          "execution": {
            "type": "spl",
            "template": "index=metrics host={{hostname}} | stats avg(cpu_pct), avg(mem_pct) by host"
          },
          "tags": ["metrics", "performance"]
        }
      }
    ]
  }'

Success response: 200 OK

See the following table for details:

Field Description
registered_count Number of tools created or updated.
deleted_count Number of stale tools removed.
skipped_built_in Built-in tools that were skipped. Cannot be replaced.
failed_deletes Tools that could not be deleted. Partial failure. All new or updated tools are still committed.
Example:
JSON
{
  "external_app_id": "my_monitoring_app",
  "message": "Replaced tools successfully.",
  "registered_count": 2,
  "deleted_count": 1,
  "skipped_built_in": [],
  "failed_deletes": []
}

Error responses

See the following table for error response information:

Status Code Reason
400 Bad Request missing_external_app_id The external_app_id is missing or empty.
400 Bad Request invalid_tools Tools is not an array.
400 Bad Request validation_error One or more tool definitions failed validation.
400 Bad Request duplicate_tools The payload contains two tools with the same name under the same app.
500 Internal Server Error kvstore_error Upsert failed. All changes have been rolled back automatically.

Enable or disable a tool

Toggle a registered tool on or off. Only enabled tools are exposed via the MCP server.

  • Method: POST

  • Content type: application/json

CAUTION: Enabling a tool checks for name collisions. Use pass override: true to take over when needed.

Request body

See the following table for request body field information:

Field Type Required Description
tool_id string Yes Full Tool ID : <external_app_id>:<tool_name>
enabled boolean Yes Use true to enable, false to disable.
override boolean No When true, disables any conflicting tool that currently holds this name (enable only).

Examples

Create a new custom tool:
JSON
curl -k -X POST \
  "https://<splunk_host>:8089/services/mcp_tools" \
  -H "Authorization: Bearer <splunk_token>" \
  -H "Content-Type: application/json" \
  -d '{
  "tool_id": "my_monitoring_app:search_error_logs",
  "enabled": true
}'
Enable a tool with override flag set to true to bypass tool collision detection:
JSON
curl -k -X POST \
  "https://<splunk_host>:8089/services/mcp_tools" \
  -H "Authorization: Bearer <splunk_token>" \
  -H "Content-Type: application/json" \
  -d '{
  "tool_id": "my_monitoring_app:search_error_logs",
  "enabled": true,
  "override": true
}'
Disable a tool:
JSON
curl -k -X POST \
  "https://<splunk_host>:8089/services/mcp_tools" \
  -H "Authorization: Bearer <splunk_token>" \
  -H "Content-Type: application/json" \
  -d '{
  "tool_id": "my_monitoring_app:search_error_logs",
  "enabled": false
}'

Success response: 200 OK

JSON
{
  "tool_id": "my_monitoring_app:search_error_logs",
  "enabled": true,
  "message": "Tool enabled successfully."
}

Error responses

See the following table for error response information:

Status Code Reason
400 Bad Request missing_tool_id The tool_id field is missing or empty.
400 Bad Request missing_enabled_flag The enabled field is missing or not a boolean.
400 Bad Request missing_tool_name The tool_name could not be inferred from tool_id; provide it explicitly.
404 Not Found tool_not_found No tool found with the given tool_id.
409 Conflict tool_name_conflict Another tool with the same short name is already enabled. Use "override": true to take over.
409 Conflict required_app_missing The tool requires a Splunk app that is not installed.
500 Internal Server Error tool_enable_failed Unexpected error while enabling the tool.
Note: Note that collision_ids lists other registered tools that share the same short name but are not active due to the conflict.

List or get tools

Retrieve registered tools, optionally filtered by various criteria.

  • Method: GET

Query parameters

See the following table for list of get tools parameters:

Parameter Type Description
tool_id string Fetch a single tool by its full Tool ID.
name string Filter by tool short name.
external_app_id string Filter tools belonging to a specific app.
tags string Comma-separated list of tags to filter by (e.g., logs,errors).
enabled_tools boolean Pass true to get the list of currently enabled tools only.

Examples

List all tools:
CODE
curl -k -X GET \
  "https://<splunk_host>:8089/services/mcp_tools" \
  -H "Authorization: Bearer <splunk_token>"
Get a single tool by ID:
CODE
curl -k -X GET \
  "https://<splunk_host>:8089/services/mcp_tools?tool_id=my_monitoring_app:search_error_logs" \
  -H "Authorization: Bearer <splunk_token>"
List all tools for an app:
CODE
curl -k -X GET \
  "https://<splunk_host>:8089/services/mcp_tools?external_app_id=my_monitoring_app" \
  -H "Authorization: Bearer <splunk_token>"
List enabled tools:
CODE
curl -k -X GET \
  "https://<splunk_host>:8089/services/mcp_tools?enabled_tools=true" \
  -H "Authorization: Bearer <splunk_token>"

Success responses

Single tool:
JSON
{
  "tool": {
    "name": "my_monitoring_app_search_error_logs",
    "title": "Search Error Logs",
    "description": "Searches Splunk for error log events matching a keyword.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "keyword": {"type": "string", "description": "Error keyword to search for."}
      },
      "required": ["keyword"]
    },
    "_meta": {
      "external_app_id": "my_monitoring_app",
      "execution": {"type": "spl", "template": "index=main {{keyword}} | head 20"},
      "tags": ["logs", "errors"],
      "built_in": false
    }
  }
}
Multiple tools:
JSON
{
  "tools": [
    { "name": "...", "title": "...", "description": "...", "inputSchema": {...}, "_meta": {...} },
    { "name": "...", "title": "...", "description": "...", "inputSchema": {...}, "_meta": {...} }
  ],
  "total": 2
}
Enabled tools:
JSON
{
  "enabled_tools": [
    {
      "tool_name": "search_error_logs",
      "tool_id": "my_monitoring_app:search_error_logs",
      "collision_ids": []
    }
  ]
}
Note: Note that collision_ids lists other registered tools that share the same short name but are currently not active due to the conflict.

Update a tool

Modify an existing tool's definition. Only supplied fields are updated. Omitted fields remain unchanged.

  • Method: PUT

  • Content-Type: application/json

Note: The name and external_app_id are immutable after creation. Built-in tools cannot be updated

Request body

See the following table for request body field information:

Field Type Required Description
tool_id string Yes The full Tool ID of the tool to update.
title string No New display title.
description string No New description.
inputSchema object No Replacement input schema.

Example

Modify tool definition for an existing tool:
JSON
curl -k -X PUT \
  "https://<splunk_host>:8089/services/mcp_tools" \
  -H "Authorization: Bearer <splunk_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "tool_id": "my_monitoring_app:search_error_logs",
    "description": "Searches Splunk for error log events. Supports index and keyword filters.",
    "_meta": {
      "tags": ["logs", "errors", "monitoring"],
      "execution": {
        "type": "spl",
        "template": "index={{index}} {{keyword}} | head {{max_results}}",
        "row_limiter": true,
        "time_range": true
      }
    }
  }'

Success response: 200 OK

JSON
{
  "tool_id": "my_monitoring_app:search_error_logs",
  "tool": {
    "name": "my_monitoring_app_search_error_logs",
    "title": "Search Error Logs",
    "description": "Searches Splunk for error log events. Supports index and keyword filters.",
    "inputSchema": {...},
    "_meta": {...}
  }
}

Error responses

See the following table for error response information:

Status Code Reason
400 Bad Request invalid_payload Request body is not a JSON object.
400 Bad Request missing_tool_id The tool_id field is missing or empty.
400 Bad Request no_updates No updatable fields were provided in the body.
400 Bad Request invalid_meta The _meta field is not an object.
404 Not Found tool_not_found No tool found with the given tool_id.
409 Conflict immutable_field Attempted to change name or external_app_id.
409 Conflict built_in_tool Built-in tools cannot be updated.
502 Bad Gateway kvstore_response_error KV Store returned a malformed response.

Delete tools

You can delete a single tool by specifying tool_id or delete all tools linked to an app.

  • Method: DELETE

  • Content type: application/json

Request body

See the following table for request body field information to delete a single tool:

Field Type Required Description
tool_id string One of The full Tool ID of the tool to delete.
external_app_id string One of Delete all tools linked to this app.

Examples

Delete a single tool:
JSON
curl -k -X DELETE \
  "https://<splunk_host>:8089/services/mcp_tools" \
  -H "Authorization: Bearer <splunk_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "tool_id": "my_monitoring_app:search_error_logs"
  }'
Delete all tools for an app:
JSON
curl -k -X DELETE \
  "https://<splunk_host>:8089/services/mcp_tools" \
  -H "Authorization: Bearer <splunk_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "external_app_id": "my_monitoring_app"
  }'

Success response: 200 OK

JSON
{
  "deleted": true,
  "tool_id": "my_monitoring_app:search_error_logs",
  "message": "Tool deleted successfully."
}

Error responses

See the following table for error response information:

Status Code Reason
400 Bad Request invalid_payload Request body is not a JSON object.
400 Bad Request missing_identifier Neither tool_id nor external_app_id was provided.
404 Not Found tool_not_found No tool found with the given tool_id.
409 Conflict built_in_tool Attempted to delete a built-in tool.
502 Bad Gateway kvstore_response_error KV Store returned a malformed response.