{"slug": "show-hn-metrics-monitoring-and-alerting-for-agents", "title": "Show HN: Metrics, Monitoring, and Alerting for Agents", "summary": "ChirrOp released @chirropjs/chirrop-mcp, an MCP server that exposes the full ChirrOp monitoring API as tools, resources and prompts to MCP-compatible clients including Claude Code, Claude Desktop and Cursor, installable via npm or npx. The server separates its own CHIRROP_API_KEY credential, which can never be referenced from a tool call, from CHIRROP_SECRET_-prefixed variables for notification destinations such as Slack, Discord and Telegram, so a prompt-injected request cannot send the API key to an attacker's webhook; destination URLs and credentials are redacted in tool output and the chirrop://destinations resource.", "body_md": "MCP server for the [ChirrOp](https://www.chirrop.com) monitoring platform. Exposes the full ChirrOp API as tools, resources, and prompts for any MCP-compatible client (Claude Code, Claude Desktop, Cursor, etc.).\n\n```\nnpm install @chirropjs/chirrop-mcp\n```\n\nOr run directly:\n\n```\nnpx @chirropjs/chirrop-mcp\n```\n\n| Variable | Required | Purpose | \n|---|---|---|\n| `CHIRROP_API_KEY` | Yes | Your ChirrOp API key, used by the server to call the ChirrOp API. Must start with `chp_` . | \n| `CHIRROP_SECRET_<NAME>` | No | Secrets for notification destinations (webhook URLs, bot tokens) that you want to reference as `env:CHIRROP_SECRET_<NAME>` instead of pasting them into the conversation. See[Destination secrets](#destination-secrets) . | \n\nThese are two separate kinds of variable on purpose. `CHIRROP_API_KEY` is the server's own credential and can **never** be referenced from a tool call. Only `CHIRROP_SECRET_`-prefixed variables can, so a prompt-injected request can't send your API key to an attacker's webhook.\n\nSet the variables wherever your MCP client launches the server. The server does not read `.env` files itself.\n\nPass the key when adding the server:\n\n```\nclaude mcp add chirrop -e CHIRROP_API_KEY=chp_your_api_key -- npx -y @chirropjs/chirrop-mcp\n```\n\nTo keep the key out of a committed `.mcp.json`, export it in your shell instead and reference it from the config:\n\n```\nexport CHIRROP_API_KEY=chp_your_api_key\n{\n  \"mcpServers\": {\n    \"chirrop\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@chirropjs/chirrop-mcp\"],\n      \"env\": {\n        \"CHIRROP_API_KEY\": \"${CHIRROP_API_KEY}\"\n      }\n    }\n  }\n}\n```\n\nClaude Code expands `${CHIRROP_API_KEY}` from the environment it was started in, so export the variable **before** running `claude`, then check the connection with `/mcp`.\n\nAdd to your `claude_desktop_config.json`:\n\n```\n{\n  \"mcpServers\": {\n    \"chirrop\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@chirropjs/chirrop-mcp\"],\n      \"env\": {\n        \"CHIRROP_API_KEY\": \"chp_your_api_key\"\n      }\n    }\n  }\n}\n```\n\nSlack, Discord and webhook URLs and Telegram bot tokens are secrets. Rather than pasting them into the conversation, add them to the server's environment with a `CHIRROP_SECRET_` prefix:\n\n```\n\"env\": {\n  \"CHIRROP_API_KEY\": \"chp_your_api_key\",\n  \"CHIRROP_SECRET_SLACK_URL\": \"https://hooks.slack.com/services/T000/B000/XXXX\",\n  \"CHIRROP_SECRET_TELEGRAM_TOKEN\": \"123456:ABC-your-bot-token\"\n}\n```\n\nThen refer to them by name when creating or updating a destination, e.g. *\"create a Slack destination using `env:CHIRROP_SECRET_SLACK_URL`\"*. The tool call then contains:\n\n```\n{ \"channel\": \"slack\", \"url\": \"env:CHIRROP_SECRET_SLACK_URL\", \"scope\": \"all\", \"policy_ids\": [], \"enabled\": true }\n```\n\n- References to any variable without the `CHIRROP_SECRET_` prefix (including`CHIRROP_API_KEY` ) are refused.\n- Restart the server after adding a variable; a reference to an unset variable returns an error.\n- Destination URLs and credentials are **redacted** in everything the server returns (tools and the`chirrop://destinations` resource), e.g.`https://hooks.slack.com/[redacted]` . Email`address` and Telegram`chat_id` stay visible because they identify the recipient rather than authenticate.\n\n- **Event references** : every tool that takes an`event_id` (UUID) also accepts`event` (the event name) plus an optional`agent` — the server resolves the UUID for you.`compare_events` accepts a mix of UUIDs and exact names.\n- **Pagination** :`list_events` and`list_alerts` default to 50 results and return`has_more` /`next_offset` for paging.\n- **Cheap reads** :`get_event_rollups` supports`summary_only=true` to return just the statistical summary plus anomalous buckets.\n- **Timestamps** : time-series and health responses include`retrieved_at` so staleness can be reasoned about reliably.\n- **Secrets** : destination`url` and`credentials` values accept`env:CHIRROP_SECRET_<NAME>` references and are redacted in output. See[Destination secrets](#destination-secrets) .\n- **Idempotent logging** :`send_logs` returns the`log_id` of every entry (generated if you didn't supply one). If a send fails, retry with the same`log_id` s so nothing is double-counted.\n- **Health checks** :`get_stream_health` and`get_agent_summary` compare each policy's threshold against the latest bucket of that policy's own period (minute/hour/day). In`get_agent_summary` , a stream whose data couldn't be fetched shows`status: \"error\"` with the reason, rather than appearing stale.\n- **No deletes** : the ChirrOp API has no delete operations. Retire policies and destinations by setting`enabled: false` ; event definitions cannot be removed.\n\n| Tool | Description | \n|---|---|\n| `send_logs` | Send up to 250 log entries to ChirrOp, with per-entry results including each `log_id` for safe retries | \n\n| Tool | Description | \n|---|---|\n| `list_events` | List event definitions, filterable by agent/name, with pagination | \n| `list_agents` | List distinct agent identities and their event counts | \n| `get_event` | Get a single event definition | \n| `create_event` | Create a new event definition | \n| `update_event` | Update event metadata | \n| `get_event_rollups` | Get time-bucketed rollup data (minute/hour/day) with anomaly/trend enrichment; `summary_only` for a cheap overview | \n| `get_stream_health` | One-call health check: staleness, active alerts, proximity to each policy's threshold over its own period | \n| `get_agent_summary` | Health overview of every event stream produced by an agent; per-stream `error` status if its data can't be fetched | \n\n| Tool | Description | \n|---|---|\n| `get_event_analytics` | Get window comparison analytics (1h/1d/7d/1m vs previous) | \n| `detect_anomalies` | Scan rollup buckets and return statistically anomalous ones | \n| `compare_events` | Side-by-side statistical comparison of multiple event streams | \n\n| Tool | Description | \n|---|---|\n| `list_policies` | List monitoring policies, optionally filtered by event | \n| `get_policy` | Get a single policy | \n| `create_policy` | Create a new alerting policy | \n| `update_policy` | Update an existing policy | \n\n| Tool | Description | \n|---|---|\n| `list_alerts` | List alerts, filterable by status/event, with pagination | \n| `get_alert` | Get alert details | \n| `get_alert_deliveries` | Get delivery attempts for an alert, filterable by kind and destination | \n| `acknowledge_alert` | Acknowledge a triggered alert | \n| `resolve_alert` | Resolve an alert | \n| `archive_alert` | Archive an alert | \n\n| Tool | Description | \n|---|---|\n| `list_destinations` | List notification destinations, filterable by channel/enabled state (URLs and credentials redacted) | \n| `get_destination` | Get a single destination (URLs and credentials redacted) | \n| `create_destination` | Create a notification destination (email/webhook/Slack/Telegram/Discord); secrets may be `env:CHIRROP_SECRET_<NAME>` references | \n| `update_destination` | Update a destination, or set `enabled: false` to retire it | \n| `test_destination` | Send a test notification and get a test alert_id | \n\n| URI | Description | \n|---|---|\n| `chirrop://events` | All event definitions | \n| `chirrop://events/summary` | Events combined with triggered alert counts | \n| `chirrop://alerts/triggered` | Currently triggered alerts | \n| `chirrop://alerts/{status}` | Alerts filtered by lifecycle status | \n| `chirrop://destinations` | All configured destinations (URLs and credentials redacted) | \n| `chirrop://events/{event_id}/rollups/{period}` | Time-bucketed rollup data for an event ( `event_id` must be a UUID;`period` is minute, hour or day) | \n| `chirrop://events/{event_id}/analytics/{period}/{previous}` | Window comparison analytics for an event ( `period` is 1h, 1d, 7d or 1m;`previous` is previous_window, previous_1d, previous_7d or previous_1m) | \n| `chirrop://agents/{agent}/events` | All events produced by a specific agent | \n\n| Prompt | Description | \n|---|---|\n| `setup-monitoring` | Guided walkthrough: create event, policy, destination, and verify | \n| `triage-alert` | Investigate an alert through a structured triage loop | \n| `compare-analytics` | Walk through a window comparison with interpretation | \n| `investigate-stream` | End-to-end health check, anomaly scan, trend context, and recommendation | \n| `bulk-ingest-review` | Review ingest health across all streams and produce a markdown summary | \n\n| Symptom | Cause and fix | \n|---|---|\n| Every tool returns \"CHIRROP_API_KEY is not set\" | The server started without the key. Add it to the server's `env` block, or export it in the shell you launch your MCP client from, then restart the server. | \n| Every tool returns \"CHIRROP_API_KEY is invalid: … must start with 'chp_'\" | The key is malformed, or a `${CHIRROP_API_KEY}` placeholder wasn't expanded because the variable wasn't exported when the client started. Fix the value and restart. | \n| \"Authentication failed … Check that CHIRROP_API_KEY is valid\" | The key has the right format but ChirrOp rejected it (revoked, or from another account). | \n| `Environment variable \"NAME\" cannot be referenced: only variables prefixed CHIRROP_SECRET_ are allowed` | Rename the variable to `CHIRROP_SECRET_NAME` in the server's environment, restart, and reference`env:CHIRROP_SECRET_NAME` . | \n| `Environment variable \"CHIRROP_SECRET_…\" … is not set` | The variable is referenced but missing from the server's environment. Add it and restart. | \n| Server shows as disconnected in Claude Code | Run `/mcp` to see its status and reconnect. Make sure`npx` (or`node` ) is on the PATH of the environment Claude Code runs in. | \n\n```\ngit clone https://github.com/chirrop/chirrop-mcp\ncd chirrop-mcp\nnpm install\nnpm run build\n```\n\nRun the tests:\n\n```\nnpm test\n```\n\nTest with the MCP Inspector:\n\n```\nCHIRROP_API_KEY=chp_your_key npx @modelcontextprotocol/inspector node dist/index.js\n```\n\nMIT", "url": "https://wpnews.pro/news/show-hn-metrics-monitoring-and-alerting-for-agents", "canonical_source": "https://github.com/chirrop/chirrop-mcp", "published_at": "2026-11-05 09:02:23+00:00", "updated_at": "2026-11-05 09:21:15.885712+00:00", "lang": "en", "topics": ["ai-agents", "agent-protocols", "ai-tools", "developer-tools", "mlops"], "entities": ["ChirrOp", "@chirropjs/chirrop-mcp", "Claude Code", "Claude Desktop", "Cursor", "Slack", "Discord", "Telegram"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-metrics-monitoring-and-alerting-for-agents", "markdown": "https://wpnews.pro/news/show-hn-metrics-monitoring-and-alerting-for-agents.md", "text": "https://wpnews.pro/news/show-hn-metrics-monitoring-and-alerting-for-agents.txt", "jsonld": "https://wpnews.pro/news/show-hn-metrics-monitoring-and-alerting-for-agents.jsonld"}}