{
  "markdown": "# scouter-mcp\n\n> 한국어 문서는 [README.ko.md](README.ko.md) 를 참고하세요.\n\nA stdio MCP server that connects directly to a Scouter Collector over TCP and queries XLogs,\ncounters, and objects. Its purpose is to let an AI quickly explore Scouter metrics and diagnose\nroot causes. Each result carries `txid` / `gxid` / `objName` / `endTimeIso`, which you can use as\nkeys to cross-analyze with other observability tools such as OpenSearch or Datadog.\n\n## Architecture\n\nJava 17. It reuses `scouter-common` and ports the `scouter.webapp` net/server classes into the\n`scouter.mcp.client` package. MCP uses the Java SDK 2.0.0 stdio transport. All operations against\nthe Collector are read-only.\n\n## Build\n\n```bash\n./gradlew shadowJar\n# output: build/libs/scouter-mcp-<version>-all.jar\n```\n\nThe `.mcpb` bundle is produced only by the release CI (which wraps this jar); local builds just\nproduce the jar.\n\n## Registration (Claude Code, Claude Desktop, ...)\n\nFor a single collector, install the `.mcpb` bundle from the [GitHub Release](../../releases) for a\none-click setup — or copy `.mcp.json.example`, point it at the fat jar (downloaded from the release or\nbuilt locally), and fill in the credentials. For multiple collectors, see\n[Multiple collectors](#multiple-collectors).\n\n| Env var | Description |\n|---|---|\n| `SCOUTER_COLLECTOR_HOST` | Collector host |\n| `SCOUTER_COLLECTOR_PORT` | Collector TCP port (default 6100) |\n| `SCOUTER_USER` | Login user |\n| `SCOUTER_PASSWORD` | Login password |\n| `SCOUTER_TZ` | Time zone (e.g. `Asia/Seoul`) |\n| `SCOUTER_LOCALE` | User-facing message locale: `en` or `ko`. If unset, derived from the JVM default (Korean only when the JVM language is Korean, otherwise English) |\n| `SCOUTER_INCLUDE_BIND_PARAMS` | Operator kill-switch for SQL bind parameters in `get_xlog_detail` (default `true`). Set to `false` to strip bind params server-side regardless of the per-call argument — an LLM cannot re-enable them. Use when bind values may contain PII. |\n\n### Multiple collectors\n\nThe official release ships **one** `.mcpb` bundle (one-click install, a single collector) plus the\nstandalone fat jar. A `.mcpb` defines exactly one server with one credential set, so it cannot register\ntwo collectors at once. For multiple collectors — which usually differ in their whole connection set\n(host/port **and** user/password) — use the jar directly and add one entry per collector:\n\n1. Download `scouter-mcp-<version>-all.jar` from the [GitHub Release](../../releases).\n2. Add one `mcpServers` entry per collector to your client config (`.mcp.json` /\n   `claude_desktop_config.json`), all pointing at that same jar, each with its own env set. This keeps\n   each credential isolated:\n\n```json\n{\n  \"mcpServers\": {\n    \"scouter-prod\": {\n      \"command\": \"java\",\n      \"args\": [\"-jar\", \"/ABSOLUTE/PATH/scouter-mcp-<version>-all.jar\"],\n      \"env\": {\n        \"SCOUTER_COLLECTOR_HOST\": \"prod-collector\", \"SCOUTER_COLLECTOR_PORT\": \"6100\",\n        \"SCOUTER_USER\": \"prod-user\", \"SCOUTER_PASSWORD\": \"***\", \"SCOUTER_TZ\": \"Asia/Seoul\"\n      }\n    },\n    \"scouter-stg\": {\n      \"command\": \"java\",\n      \"args\": [\"-jar\", \"/ABSOLUTE/PATH/scouter-mcp-<version>-all.jar\"],\n      \"env\": {\n        \"SCOUTER_COLLECTOR_HOST\": \"stg-collector\", \"SCOUTER_COLLECTOR_PORT\": \"6100\",\n        \"SCOUTER_USER\": \"stg-user\", \"SCOUTER_PASSWORD\": \"***\", \"SCOUTER_TZ\": \"Asia/Seoul\"\n      }\n    }\n  }\n}\n```\n\nThe AI then orchestrates across the collectors.\n\n## Tools (14)\n\n| Name | Purpose | Key inputs |\n|---|---|---|\n| `list_objects` | List objects/agents | `objType?`, `nameLike?` (case-insensitive) |\n| `search_xlog` | Search XLogs (latency/errors) | `from`, `to`, `objNameLike?`, `objHash?`, `service?`, `login?`, `ip?`, `desc?`, `minElapsedMs?`, `onlyError?`, `limit?` (default 20, max 200) |\n| `get_service_summary` | Per-service aggregate (count/avg/max/p95/errorRate), top 50 | `from`, `to`, same filters as `search_xlog` |\n| `get_summary` | Collector's daily pre-aggregated stats (top-50 SQL/service/error/... — no scanning) | `category` (service/sql/apiCall/ip/userAgent/error/alert), `from`, `to` (up to 31 days), `objType?`, `objNameLike?`, `objHash?` |\n| `get_xlog_detail` | XLog detail (SQL/bind params) | `txid`, `date?`/`at?`, `includeBindParams?` (default true) |\n| `get_xlog_by_gxid` | Distributed-transaction group | `gxid`, `date?`/`at?` |\n| `get_counter` | Counter time series (same-day, full resolution) | `objNameLike`\\|`objHashes`\\|`objType`, `counter`, `from`, `to` |\n| `get_counter_stat` | Long-range counter stats (5-min resolution, up to 31 days) | `objNameLike`\\|`objHashes`\\|`objType`, `counter`, `from`, `to` |\n| `list_counters` | Available counters for an objType | `objType` |\n| `list_alerts` | Past collector alerts | `from`, `to`, `level?`, `object?`, `key?`, `limit?` |\n| `get_active_services` | Services running right now | `objNameLike`\\|`objType`\\|`objHash` |\n| `list_threads` | JVM thread list (state histogram + top 50 by cpu) | `objNameLike`\\|`objHash` (max 5 alive instances) |\n| `get_thread_detail` | Live thread of an ACTIVE transaction (stack/lock owner/current SQL) | `txid` (required, active), `id?`, `objNameLike`\\|`objHash` |\n| `get_object_env` | Agent JVM system properties (secrets masked) | `objNameLike`\\|`objHash`, `keyLike?` |\n\n### Fuzzy targeting (`objNameLike`)\n\nUsers say app-name fragments (\"shop-order-api\"), but real objNames embed the k8s pod name\n(`/shop-order-api-deployment-5f4b8c7d9-abcde/shop-order-api1`), so objHash changes on every deploy\nand an app spans multiple instances. `objNameLike` solves this: a case-insensitive fragment is\nresolved to **all** matching instances (alive first, capped at 20) and queried across them — no\nobjHash needed, ever. For XLog search/summary the resolution also unions the collector's daily\nobject DB, so pods replaced by a deploy during the queried window are still found. If nothing\nmatches, the error is `NOT_FOUND` with a `candidates` hint listing actual objNames so the caller\ncan self-correct in one step.\n\n### Sloppy service queries\n\nScouter service names look like `/api/order/.../search-order-info-grade<POST>`, but users type\n\"GET orderDetail\" or \"order info grade\". The `service` filter normalizes such input: an HTTP method is\nextracted from any position (`GET x`, `x POST`, pasted `<POST>`), whitespace-separated words fall back\nto the longest token server-side, and explicit `*` patterns pass through untouched. Server-side\nmatching is still case-sensitive — so when a pattern matches nothing, the same window is re-scanned\n(bounded) without the service filter and real service names matching the query tokens\ncase-insensitively are returned as `serviceCandidates`, ordered by traffic. One retry with an exact\nname resolves it.\n\n`service`/`login`/`ip`/`desc` use substring match by default (server-side `StrMatch`), so a short token\nlike `search-order-info-grade` matches `/api/order/ext/order-info/search-order-info-grade<POST>`.\n`objNameLike`/`login`/`ip`/`desc` count as server-side filters, so they relax the 5-minute\nunfiltered-window cap. `list_counters` also accepts `objNameLike` and derives the objType, so users\nnever need to know Scouter's type taxonomy.\n\nAll tools are advertised with `readOnlyHint`. A `diagnose_root_cause` MCP prompt exposes the\nrecommended tool order for latency/error investigations.\n\n## Resource / token safety policy\n\nProduction Scouter can produce hundreds of thousands of XLogs in five minutes, so `search_xlog`\nenforces guardrails (see `scouter.mcp.policy.Limits`):\n\n- During streaming, it stops once the `limit` or the scan cap (5,000 examined packs) is reached and\n  closes the socket, which also stops the Collector's scan/transfer — bounding server load, network,\n  and MCP heap together.\n- Without a `service` or `objHash` filter, only windows up to 5 minutes are allowed; the absolute\n  window cap is 24 hours.\n- `limit` defaults to 20 and is capped at 200. Results include `truncated`/`scanCapReached` and a\n  `hint` so the caller can narrow filters instead of refetching.\n- `get_service_summary` retains no rows (only per-service counters), so it uses a higher scan cap\n  (200,000) to cover wider windows cheaply; it reports `scanCapReached`/`examined` too.\n- `get_counter` caps the per-`objType` fan-out at 20 instances, and downsamples long series with a\n  min/max scheme that preserves spikes/dips (summary `min`/`max`/`avg` are computed from the full series).\n- Windows crossing midnight are split per calendar day (the collector partitions XLogs/counters/alerts\n  by day), so no data is lost on either side of the boundary.\n- Response text budgets: SQL text is cut at 1,500 chars, error messages at 500, thread stack traces at\n  4,000, env values at 500 — each with a truncation marker carrying the original length. `get_xlog_detail`\n  profile steps are capped at 150, signalled via `totalSteps`/`stepsTruncated`.\n- A single request may fan out to at most 40 collector round-trips (instances x day segments). When\n  client-side filters (`minElapsedMs`/`onlyError`) discard over 99% of scanned rows, a low-selectivity\n  hint steers the model toward server-side filters or `get_summary`.\n- `get_summary`/`get_counter_stat` read the collector's daily pre-aggregated data (no scanning), capped at\n  31 days; summary returns the top 50 rows per category. `list_threads` caps at 5 alive instances and 50\n  thread rows each (the state histogram always covers all threads).\n- Per-request telemetry (passes/examined/kept/tookMs) is logged to stderr as structured `key=value` lines\n  for post-hoc load analysis.\n\n## Internationalization\n\nOnly dynamic, user-facing output (tool error messages, result hints, notes) is localized, in English\nand Korean, via `messages.properties` / `messages_ko.properties`. Static schema/tool descriptions and\nstructured stderr logs (`key=value`) remain English for a stable contract and log parsing.\n\n## Security notes\n\n- Read-only against the Collector (no write commands are exposed).\n- Credentials are injected via environment variables only (never in files or arguments as plaintext).\n  Prefer a least-privilege / read-only Scouter account.\n- **Transport is plaintext TCP** (the Scouter protocol has no TLS): the SHA-256 password digest, the\n  session token, and all XLog/counter data cross the wire unencrypted. Run only inside a trusted network,\n  or tunnel over SSH/VPN. Do not expose the collector port over the public internet.\n- `get_xlog_detail` bind parameters can contain PII. Set `SCOUTER_INCLUDE_BIND_PARAMS=false` to strip\n  them server-side (the LLM cannot re-enable them). See the env table above. `get_thread_detail`'s live\n  bind values (`SQLActiveBindVar`) obey the same kill-switch.\n- `get_object_env` **unconditionally** masks values of keys matching password/secret/token/credential/\n  private — a server-side policy the LLM cannot opt out of.\n- stdout is reserved for JSON-RPC, so all logs go to stderr only.\n\n## License / Notice\n\nThe `scouter.mcp.client` package is ported from Scouter v2.20.0 client code (Apache License 2.0).\nSee [NOTICE](NOTICE) for details.\n\n## Known limitations\n\n1. `search_xlog`/`get_service_summary` `minElapsedMs`/`onlyError`/`limit` are applied client-side because\n   the Collector has no native parameters for them. `truncated=true` is a heuristic (returned count ==\n   limit) and can be a false positive.\n2. On a session-expiry (`INVALID_SESSION`) the client re-logs in once and retries the request; a second\n   failure surfaces as `SCOUTER_AUTH_FAILED` (no infinite retry loop). The upstream 2-second time-delta\n   refresh daemon is still not ported, so long-running processes may drift slightly for real-time\n   relative queries. Absolute-epoch (historical) queries are unaffected.\n3. `list_alerts`/`get_active_services` were ported from the upstream protocol and validated against a\n   collector via the smoke tests (`SmokeIT`); field coverage may vary by collector version.\n",
  "bytes": 11894,
  "sha": "2624ad07417fc5f3830f91ee49af62baaa5adcc9d56e3144805a37411c1c28cf",
  "repo_slug": "335/scouter-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_335_scouter_mcp_b7cfb0f8/readme"
}