Skip to content

About

Splunk search as a local MCP server — asynchronous job pattern over the REST API guarantees exact result counts (never oneshot/preview), large result sets delivered as JSONL files without truncation, destructive-SPL guard, one instance per Splunk host

Resources

Contributing

Stars

0 stars

Watchers

1 watching

Forks

Repository files navigation

splunk-mcp

A local MCP server that exposes Splunk search over the REST API — built for data analysis where exact result counts and full retrieval are non-negotiable.

日本語版 README はこちら

Why

Splunk's official MCP Server app (installed on the Splunk side) runs searches with exec_mode=oneshot under a 60-second timeout and silently injects | head N (default cap 1000 rows). Row counts are unstable, capped results report only an approximation, and there is no way to retrieve the full set — unusable for data analysis, and a trigger for agent retry loops.

splunk-mcp runs every search as an asynchronous Splunk job (create → poll until DONE → read the final resultCount → page through /results), so:

  • total_rows is always the exact final count — never a preview, never an approximation
  • Large result sets are never cut silently: rows beyond max_rows are dropped from the response and counted, next to an exact total_rows
  • Long searches don't time out: run_query returns a SID on wait_timeout and the job keeps running server-side
  • No app installation on the Splunk side — a token is all you need

Tools

Tool Purpose
run_query Run SPL, wait for completion, return exact count + results
start_query Start SPL asynchronously, return the SID immediately
check_job Poll job state / result count by SID
get_results Fetch results of a completed job (offset/count paging)
cancel_job Cancel a running job
list_indexes List visible event indexes with counts and time bounds
list_sourcetypes List sourcetypes for an index/window (via | metadata)
list_saved_searches List saved searches with their SPL and schedule
run_saved_search Dispatch a saved search (alert actions never fire)
get_usage Full tool reference served to the agent

Result delivery

Results come back in the response, up to max_rows (default 50,000; a call may set its own, and 0 means no cap). When the cap drops rows, the response says so with truncated, omitted_rows, and the exact total_rows — the count is the product, so a capped answer is still an answer about the whole set. To work through more than one answer can hold, page with get_results offset/count.

This server does not write results to a file it chose. It cannot know the caller's context window, and an agent runtime that needs a large response on disk already puts it there (gem-agent does this automatically).

Arguments are checked strictly. A call carrying an argument a tool does not declare fails with invalid_arguments, naming it — unknown field "max_rowz" — rather than running without it. That matters most for max_rows: a misspelt one used to fall back to the configured default while reading as though the caller's cap had been honoured, which is the opposite of the explicit count this server exists to give. Wrong-typed arguments are refused the same way, and nothing reaches Splunk before the arguments decode, so a rejected call starts no search job. Omitting arguments entirely still means "none".

SPL guard

Write/delete commands (delete, collect, mcollect, meventcollect, outputlookup, outputcsv, sendemail, runshellscript, script) are rejected by default with a structured unsafe_spl error. Individual commands can be re-allowed via [server] allow_commands. Splunk-side RBAC remains the final authority.

Installation

Download a pre-built binary from the releases page, or build from source:

git clone https://github.com/nlink-jp/splunk-mcp.git
cd splunk-mcp
make build
# Binary: dist/splunk-mcp

Configuration

One server instance connects to exactly one Splunk host. For multiple destinations, create one config file per host and register the server multiple times:

{
  "mcpServers": {
    "splunk-prod": { "command": "splunk-mcp", "args": ["--config", "/path/to/prod.toml"] },
    "splunk-dev":  { "command": "splunk-mcp", "args": ["--config", "/path/to/dev.toml"] }
  }
}

Copy config.example.toml to ~/.config/splunk-mcp/config.toml (the default path) and set your values:

[splunk]
host  = "https://your-splunk.example.com:8089"
token = "your-token"
# insecure = false          # self-signed certs
# prepend  = "pipe-only"    # auto | pipe-only | off (same as splunk-cli)

[server]
# max_rows = 50000
# job_ttl              = "10m"
# allow_commands       = []
chmod 600 ~/.config/splunk-mcp/config.toml

Config resolution order: --config flag → $SPLUNK_MCP_CONFIG → ~/.config/splunk-mcp/config.toml → ./config.toml. Connection settings in the file are overridden by env vars (SPLUNK_HOST, SPLUNK_TOKEN, SPLUNK_USER, SPLUNK_PASSWORD, SPLUNK_APP) — the same names splunk-cli uses, so credentials can be shared.

Usage

splunk-mcp                    # serve MCP over stdio (default)
splunk-mcp serve --config /path/to/prod.toml
splunk-mcp --version

Typical agent workflows:

  • Discover first — list_indexes → list_sourcetypes to learn the data landscape before writing SPL.
  • Quick analysis — run_query with SPL; completes within wait_seconds (default 300) and returns exact counts.
  • Long-running search — start_query → poll check_job → get_results.
  • Large result set — raise max_rows if your context can hold it, or page with get_results offset/count. Nothing is ever dropped silently.
  • Saved searches — list_saved_searches → run_saved_search (optionally overriding the dispatch time window; alert actions are always suppressed).

Operational notes

  • Completed jobs expire after their TTL (Splunk default is a few minutes; raise with [server] job_ttl). An expired SID returns job_not_found.
  • Splunk enforces per-role concurrent-search quotas; prefer sequential start_query batches over mass parallelism.
  • Tool errors are structured JSON {code, message, details} — see get_usage for the full error-recovery table.

Development

make test              # go test ./...  (unit tests, no external deps)
make vet               # go vet ./...
make check             # vet + test + build
make build             # outputs dist/splunk-mcp
make integration-test  # start a Splunk container (Podman) and run live E2E tests
make splunk-down       # stop and remove the Splunk test container

Integration tests run the full lifecycle — exact counts, the max_rows cap and its accounting, async flow — against a real splunk/splunk:9.4 container. See BUILD.md for details.

License

MIT — see LICENSE.

About

Splunk search as a local MCP server — asynchronous job pattern over the REST API guarantees exact result counts (never oneshot/preview), large result sets delivered as JSONL files without truncation, destructive-SPL guard, one instance per Splunk host

Resources

Contributing

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages