{"_id":"@aryanbansal-launch/observer-launch-mcp","_rev":"3-c59f36256ffb8167dba9f7e6d454c004","name":"@aryanbansal-launch/observer-launch-mcp","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@aryanbansal-launch/observer-launch-mcp","version":"0.1.0","keywords":["mcp","model-context-protocol","observe","observeinc","opal","observability","claude"],"license":"MIT","_id":"@aryanbansal-launch/observer-launch-mcp@0.1.0","maintainers":[{"name":"aryanbansal-launch","email":"aryan.bansal@contentstack.com"}],"bin":{"observer-launch-mcp":"dist/server.js"},"dist":{"shasum":"a619ab63c31093d4727d55e158be40a55e24f51e","tarball":"https://registry.npmjs.org/@aryanbansal-launch/observer-launch-mcp/-/observer-launch-mcp-0.1.0.tgz","fileCount":6,"integrity":"sha512-UKVLKb96ZLHeQv3n17itSd8J1myIOajaQee51n205pQ6oWGCrDcMSaLae7E/Mi6ix90+XiWqbZcvn/yACKewGw==","signatures":[{"sig":"MEUCICthJZ8ivygbLjhqVmRgSD1nrxkxLkvdvyh7+YYwsDx8AiEA8iszfKi3tGGQwMcOdaheMaS8DH5yDQDyF1vzcKygvAY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":20564},"main":"dist/server.js","type":"module","scripts":{"dev":"tsx src/server.ts","build":"tsc && chmod +x dist/server.js","start":"node dist/server.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"aryanbansal-launch","email":"aryan.bansal@contentstack.com"},"_npmVersion":"11.6.0","description":"MCP server for querying Observe (OPAL) from Claude and other MCP clients -- health check, ad-hoc OPAL queries, and fast aggregated helpers for recent errors, error summaries, and log stats.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"zod":"^3.23.8","@modelcontextprotocol/sdk":"^1.12.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.16.2","typescript":"^5.5.3","@types/node":"^20.14.9"},"_npmOperationalInternal":{"tmp":"tmp/observer-launch-mcp_0.1.0_1784455377712_0.4675210540517327","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@aryanbansal-launch/observer-launch-mcp","version":"0.1.1","keywords":["mcp","model-context-protocol","observe","observeinc","opal","observability","claude"],"license":"MIT","_id":"@aryanbansal-launch/observer-launch-mcp@0.1.1","maintainers":[{"name":"aryanbansal-launch","email":"aryan.bansal@contentstack.com"}],"bin":{"observer-launch-mcp":"dist/server.js"},"dist":{"shasum":"ef4968bcad3ceb6dbf69edb6eea22299e09da6ef","tarball":"https://registry.npmjs.org/@aryanbansal-launch/observer-launch-mcp/-/observer-launch-mcp-0.1.1.tgz","fileCount":8,"integrity":"sha512-OLe+kD2En6Jd7TeJV1xYafiHa3oIVlXFs8TVjPH6zT0tjZe6mRzIWOIcN60VE9rCiMnHfbU8ZtKgfvw2NTNXzg==","signatures":[{"sig":"MEYCIQDrILjS7nsxuPaahu0jb0NkQ0WATki7E+EEIliIKWHNMwIhAOcPDcFMlgD/BqOQmb2DZ7RL8QloRiMEcVfDT9kf+vJI","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":28744},"main":"dist/server.js","type":"module","gitHead":"c31f92edd694976a59e3f1e4ecaac7f5c8d8ed81","scripts":{"dev":"tsx src/server.ts","build":"tsc && chmod +x dist/server.js","start":"node dist/server.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"aryanbansal-launch","email":"aryan.bansal@contentstack.com"},"_npmVersion":"11.6.0","description":"MCP server for querying Observe (OPAL) from Claude and other MCP clients -- health check, ad-hoc OPAL queries, and fast aggregated helpers for recent errors, error summaries, and log stats.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"zod":"^3.23.8","@modelcontextprotocol/sdk":"^1.12.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.16.2","typescript":"^5.5.3","@types/node":"^20.14.9"},"_npmOperationalInternal":{"tmp":"tmp/observer-launch-mcp_0.1.1_1784457450655_0.7695291214477704","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@aryanbansal-launch/observer-launch-mcp","version":"0.2.0","description":"MCP server for querying Observe (OPAL) from Claude and other MCP clients -- health check, ad-hoc OPAL queries, and fast aggregated helpers for recent errors, error summaries, and log stats.","type":"module","main":"dist/server.js","bin":{"observer-launch-mcp":"dist/server.js"},"publishConfig":{"access":"public"},"keywords":["mcp","model-context-protocol","observe","observeinc","opal","observability","claude"],"license":"MIT","scripts":{"build":"tsc && chmod +x dist/server.js","prepublishOnly":"npm run build","start":"node dist/server.js","dev":"tsx src/server.ts"},"dependencies":{"@modelcontextprotocol/sdk":"^1.12.0","zod":"^3.23.8"},"devDependencies":{"@types/node":"^20.14.9","tsx":"^4.16.2","typescript":"^5.5.3"},"_id":"@aryanbansal-launch/observer-launch-mcp@0.2.0","gitHead":"3290044a53b2281c4f619681b16c3b501d99ce99","_nodeVersion":"22.14.0","_npmVersion":"11.6.0","dist":{"integrity":"sha512-NRcIAiBFZCEo4vnPxbT4kMM4D1NzHCP7paaCXvLVyP+EIbT13/lpb+9onL6NlrCny9JAiVqof4hciiZLzMROdQ==","shasum":"00d6d8ed6536d731f8fb4c8c6c7b4d621c3a8004","tarball":"https://registry.npmjs.org/@aryanbansal-launch/observer-launch-mcp/-/observer-launch-mcp-0.2.0.tgz","fileCount":10,"unpackedSize":53116,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIA/mF7YBs/8IY6p7b1iob86R+x/h/JmCZ0btc3VAt299AiBH/gVHctBpnWwf9JGegvYFqKHAfsaprQe4SFYNoPhVyw=="}]},"_npmUser":{"name":"aryanbansal-launch","email":"aryan.bansal@contentstack.com"},"directories":{},"maintainers":[{"name":"aryanbansal-launch","email":"aryan.bansal@contentstack.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/observer-launch-mcp_0.2.0_1787146880814_0.8092678858336528"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-19T10:02:57.532Z","modified":"2026-08-19T13:41:21.135Z","0.1.0":"2026-07-19T10:02:57.957Z","0.1.1":"2026-07-19T10:37:30.782Z","0.2.0":"2026-08-19T13:41:20.944Z"},"license":"MIT","keywords":["mcp","model-context-protocol","observe","observeinc","opal","observability","claude"],"description":"MCP server for querying Observe (OPAL) from Claude and other MCP clients -- health check, ad-hoc OPAL queries, and fast aggregated helpers for recent errors, error summaries, and log stats.","maintainers":[{"name":"aryanbansal-launch","email":"aryan.bansal@contentstack.com"}],"readme":"# observer-launch-mcp\n\nAsk Claude questions about your [Observe](https://observeinc.com) data --\nrecent errors, common failures, log volume by pod -- without writing OPAL by\nhand. This is an MCP server that gives Claude direct, live access to your\nObserve tenant.\n\n## Setup (2 steps)\n\n**1. Run the setup wizard.** It asks for your Observe credentials and\ndataset aliases, then registers itself with Claude Code automatically:\n\n```bash\nnpx @aryanbansal-launch/observer-launch-mcp init\n```\n\nYou'll be asked for:\n- **Observe customer ID** -- a number, found in your Observe URL.\n- **Observe API key** -- create one at **Manage account → My API tokens**.\n  (Not an ingest/datastream token -- those look similar but don't work here.)\n- **Cluster** -- e.g. `eu-1`. Leave blank if you're on the default US tenant.\n- **Dataset aliases** (optional) -- friendly names for datasets you'll ask\n  about often, e.g. `launch-management` → `41249178`. Find the ID in the\n  Observe UI: open a dataset, it's in the URL. You can skip this and use raw\n  dataset IDs instead, or add aliases later.\n\n**2. Restart Claude Code** (or start a new session) so it picks up the new\nserver. Confirm it worked:\n\n```\nIs the Observe connection working?\n```\n\nThat's it -- the other tools below are now available in chat.\n\n<details>\n<summary>Prefer to register it by hand instead of the wizard?</summary>\n\n```bash\nclaude mcp add observe -s user \\\n  -e OBSERVE_CUSTOMER_ID=<your id> \\\n  -e OBSERVE_API_KEY=<your token> \\\n  -e OBSERVE_CLUSTER=eu-1 \\\n  -e OBSERVE_DATASETS='{\"launch-management\":\"41249178\"}' \\\n  -- node <path to installed server.js>\n```\n\nRun `npx @aryanbansal-launch/observer-launch-mcp init` once first if you just\nwant that absolute path -- it prints the exact command it would run, right\nbefore running it. **Use a direct `node <path>` command, not `npx <package>`\nas the final entry** -- Claude Code spawns registered servers with a bare\nenvironment (no PATH), and `npx`'s own shebang needs PATH to find `node`, so\n`npx`-as-command silently shows \"Failed to connect.\" The wizard already\navoids this for you.\n</details>\n\n## What you can ask\n\nOnce it's set up, just talk to Claude naturally:\n\n- *\"Is the Observe connection working?\"*\n- *\"What service/dataset aliases do I have configured?\"*\n- *\"Show me recent errors from launch-management in the last hour\"*\n- *\"What's our most common error in launch-nginx this week?\"*\n- *\"Which pods are logging the most in launch-management-bg right now?\"*\n- *\"How much stderr output is launch-nginx producing in the last 6 hours?\"*\n- *\"Count log lines per pod in launch-telemetry over the last hour\"*\n- *\"Pull the last 20 raw log lines from launch-logs-bg-exporter\"*\n\nFor anything not covered by a specific question shape, just describe what\nyou want -- Claude will write the OPAL pipeline for you using the general\nquery tool.\n\n## Tools reference\n\n| Tool | Use for |\n|---|---|\n| `observe_health_check` | \"Is the connection working?\" |\n| `list_known_services` | \"What datasets/aliases do I have?\" |\n| `get_service_errors` | Recent raw errors for one service, most recent first |\n| `get_error_summary` | Top recurring errors ranked by count -- fast even over days/weeks |\n| `get_log_stats` | Log counts grouped by pod/node/container/stream/namespace |\n| `run_opal_query` | Anything else -- arbitrary OPAL pipeline against any dataset |\n\n`get_error_summary` and `get_log_stats` aggregate server-side, so they stay\nfast regardless of the time window. `get_service_errors` and `run_opal_query`\nwill add a warning if you query a 6h+ window without aggregating, since raw\nrow dumps over a wide window risk Observe's 100k row cap.\n\n### Log formats\n\nDatasets in one tenant rarely share a log schema, so `get_service_errors` and\n`get_error_summary` detect the format per dataset (once per process) and build\nthe matching query:\n\n| Format | Looks like | Errors are |\n|---|---|---|\n| `level` | `{\"level\":\"error\",\"message\":...,\"context\":...}` — app services | `level` in error/fatal/critical/panic/… |\n| `http` | `{\"http.status\":\"500\",\"http.method\":...}` — nginx/access logs | `http.status` 5xx (pass `minStatus=400` for 4xx too) |\n| `text` | anything unstructured | keyword match on the raw line |\n\nEvery result says which format was detected. To skip the probe, declare it in\nyour dataset config:\n\n```json\n{ \"launch-nginx\": { \"id\": \"41250854\", \"shape\": \"http\" } }\n```\n\nPlain `\"alias\": \"id\"` entries still work and are auto-detected.\n\n**Coverage is reported, not hidden.** On `level` datasets, some lines have no\nparseable `level` field (a stack trace, a bare string, non-JSON output) and a\nseverity filter cannot see them. When that share exceeds 1% the tools say so:\n\n```\nNote: 529 of 2136 rows (24.8%) have no parseable \"level\" field and were not\nscanned. Pass includeUnparsed=true to keyword-match those too.\n```\n\n`includeUnparsed=true` keyword-scans those lines. It is opt-in because keyword\nmatching also flags `info` lines that merely contain the word \"error\".\n\n## Configuration reference\n\n| Variable | Required | Description |\n|---|---|---|\n| `OBSERVE_CUSTOMER_ID` | yes | Your Observe customer ID (numeric). |\n| `OBSERVE_API_KEY` | yes | A bearer token from **Manage account → My API tokens** (or a service account token for long-lived use). |\n| `OBSERVE_CLUSTER` | no | e.g. `eu-1`. Leave unset for the default (US) tenant. |\n| `OBSERVE_BASE_URL` | no | Full override if your tenant doesn't follow the `{id}.{cluster}.observeinc.com` pattern. |\n| `OBSERVE_LOOKUP_TIMEOUT_SEC` | no | Query timeout in seconds (default 300). |\n| `OBSERVE_DATASETS` | no | Inline JSON mapping alias → dataset ID, e.g. `{\"logs\":\"41001999\"}`. An entry may be `{\"id\":\"...\",\"shape\":\"level\\|http\\|text\"}` to skip format detection. |\n| `OBSERVE_DATASETS_FILE` | no | Path to a JSON file with the same shape, for larger configs. |\n| `OBSERVE_MAX_RESPONSE_BYTES` | no | Cap on a single tool response (default 200000). Past it, rows are dropped and the response says so. |\n| `OBSERVE_KEEP_INTERNAL_COLS` | no | Set to `1` to keep the `link_*`, `containerId`, and `clusterUid` columns that are stripped by default. |\n\nDataset IDs are tenant-specific and intentionally not bundled with this\npackage. Without any configured, alias lookups fall through and you can\nstill pass raw numeric dataset IDs directly to any tool.\n\n## Troubleshooting\n\n- **Health check fails** → your API key is probably an ingest/datastream\n  token, not one from **Manage account → My API tokens**. Regenerate it.\n- **`claude mcp list` shows \"Failed to connect\"** on a manually-registered\n  entry → check the command is `node <path>`, not `npx <package>` (see the\n  callout above).\n- **A tool returns \"Unknown service/dataset\"** → run `list_known_services`\n  to see what's configured, or pass a raw numeric dataset ID instead.\n- **`get_service_errors`/`get_error_summary` return nothing useful** → check\n  the \"Detected log format\" note on the result. If it guessed wrong, pin the\n  format explicitly with `{\"alias\": {\"id\": \"...\", \"shape\": \"http\"}}` (see\n  [Log formats](#log-formats)). If it says `text`, your logs have no severity\n  field and matching is keyword-based.\n- **Errors you can see in the Observe UI are missing from the results** → look\n  for the coverage note. Lines with no parseable `level` are skipped by\n  default; pass `includeUnparsed=true`.\n\n## Contributing\n\n```bash\nnpm install\nnpm run build   # tsc, then chmod +x on the bin entry\nnpm publish      # only if you intend to ship a new version\n```\n\nCopy the `get_error_summary`/`get_log_stats` pattern in `src/run-server.ts`\nfor other recurring OPAL queries (e.g. cost-by-service, latency\npercentiles) -- give the tool a descriptive name/description so Claude\npicks it over `run_opal_query` when appropriate.\n","readmeFilename":"README.md"}