MCP server monitoring
Agents trust whatever an MCP server says in tools/list. Tool
descriptions and server instructions go straight into the model’s
context, so a changed description is a changed instruction to every
agent connected to that server. Servers also break quietly: a spec
revision or an auth change, and new clients stop connecting while
nothing in your logs looks wrong. The mcp monitor type watches a
remote MCP server the way an HTTP monitor watches a website - is it
up, does the handshake still work - and adds the part an uptime check
cannot see: did the tools it advertises change since a person last
approved them.
It suits two jobs:
- You publish an MCP server. Uptime, auth-chain health, protocol version, and tool changes you did not expect after a deploy.
- You depend on someone else’s MCP server. Pin the approved tool surface and get paged when it changes.
Create an MCP server monitor
Section titled “Create an MCP server monitor”Monitors, then Add Monitor, type MCP server.
| Field | Notes |
|---|---|
| MCP endpoint URL | The server endpoint, e.g. https://mcp.example.com/mcp |
| Transport | Streamable HTTP (default) or Legacy SSE |
| Authentication | None, Header (API key or bearer token), or OAuth - check discovery chain only |
| Headers | Header mode only: name and value pairs such as Authorization or X-API-Key, up to 10 |
| Drift policy | Alert on changes (default) or Record changes only |
| Expected tools | Optional, comma-separated tool names that must be present |
| Max tools | Fail when the server lists more tools than this; 0 means no cap |
| Check interval | Default 10 minutes |
| Timeout | Default 20 seconds |
| Escalation policy | Where a failure pages |
Everything else is the same as other monitors - locations, escalation
policy binding, pausing - and is described in
Monitor types. AI assistants can create
one with the create_mcp_monitor tool; see the
MCP tool reference.
What each check does
Section titled “What each check does”| Step | What happens |
|---|---|
| Handshake | Connects over the chosen transport and runs initialize, recording the negotiated protocol version, server name and version, capabilities, and a hash of the server instructions |
| Protocol version | A server that negotiates a version older than 2025-06-18 gets a warning; the check still passes |
| Tool list | Calls tools/list and follows nextCursor across pages, up to 20 pages or 500 tools. A truncated list is a warning |
| Fingerprints | Each tool is hashed as canonical JSON of its name, title, description, input and output schema, and annotations, with separate hashes for the text, the schema, and the annotations so a change can be named. The whole surface gets one digest |
| Drift | The fingerprints are compared with the approved baseline (below) |
| Lint | Every tool title and description, and the server instructions, are scanned for suspicious patterns |
| Assertions | Expected tools must be present, and the tool count must not exceed the max |
| Latency | Handshake time and list time are recorded on every successful check |
The check only lists. It never calls a tool, so it is safe to point at any server.
Drift classes
Section titled “Drift classes”| Change | Event | Fails the check by default |
|---|---|---|
| Tool removed | mcp_tool_removed | Yes - clients that call it break |
| Tool added | mcp_tool_added | Yes - the server’s reach grew |
| Input or output schema changed | mcp_tool_schema_changed | Yes |
| Description or title changed | mcp_tool_description_changed | Yes - the text a model reads changed |
| Server instructions changed | mcp_instructions_changed | Yes - same reason |
| Annotations: a read-only tool is no longer read-only, or a tool becomes destructive | mcp_tool_annotations_changed | Yes |
| Any other annotation change | mcp_tool_annotations_changed | No, event only |
| Server version or protocol version changed | mcp_server_version_changed | No, event only |
| OAuth discovery metadata changed | mcp_auth_metadata_changed | No, event only |
| New suspicious pattern found | mcp_poison_finding | Yes |
With Record changes only, nothing in this table fails the check: every change is still recorded as an event, which suits a development server that changes several times a day. Missing expected tools and a tool count over the max fail under either policy.
Suspicious patterns
Section titled “Suspicious patterns”The lint is a set of heuristics. False positives happen; a finding is accepted the same way as any other change.
| Rule | Looks for |
|---|---|
Hidden characters (hidden_unicode) | Zero-width, bidirectional control, invisible operator, byte-order mark, and Unicode tag characters: text a model reads but a person reviewing the list does not see |
Instruction markers (instruction_marker) | Tags such as <IMPORTANT> or <SYSTEM>, “ignore previous instructions”, “do not tell the user”, “before using this tool, read…” |
Sensitive paths (sensitive_path) | ~/.ssh, id_rsa, .env, .aws/credentials, mcp.json, .cursor/, .claude/, /etc/passwd, .npmrc, .git-credentials, .kube/config |
Cross-tool steering (cross_tool) | “When calling the X tool” where X is not a tool this server lists |
Encoded blobs (encoded_blob) | Base64-like runs over 200 characters or hex runs over 128 |
Oversized (oversized) | A description over 16 KB. Over 4 KB is a warning only |
OAuth discovery chain
Section titled “OAuth discovery chain”With OAuth - check discovery chain only, the monitor walks the same discovery a new client would, without logging in:
- An unauthenticated request to the endpoint must get HTTP 401. The
resource_metadataURL is taken from theWWW-Authenticateheader; if it is missing, the monitor falls back to the well-known location and records a warning. - The protected-resource metadata (RFC 9728) must load and name at least one authorization server.
- The authorization-server metadata (RFC 8414, then OpenID
configuration) must load, and its
issuermust match the authorization server URL (the RFC 9207 mix-up defence). - PKCE
S256must be advertised, and the authorization and token endpoints must be present. - A client registration path must exist: client ID metadata documents, or dynamic client registration. Relying on dynamic registration alone is a warning, since the 2026-07-28 spec deprecates it; having neither fails.
Any break in the chain fails the check. Because there is no login yet, the tool list is not checked in this mode: use None or Header when the server lets you list tools that way.
The approved baseline
Section titled “The approved baseline”- The first successful check sets the baseline. The first tool list
the monitor sees becomes the approved one, and the monitor records a
mcp_baseline_setevent. Review it on the monitor’s page. - Findings are not accepted automatically. If the first check finds a suspicious pattern, the check fails until a person accepts it. A server that was already poisoned when you added it still gets flagged.
- Changes keep failing until someone accepts them. Drift is not a
one-off event: the check stays failing, and the alert stays open,
until a person reviews the diff and chooses Accept changes on the
monitor’s page. That copies the current tools, instructions, and
findings into the baseline, records who accepted it, and logs a
mcp_baseline_acceptedevent. - Accepting is human-only. It works only from a signed-in session in the AlertKick web app. The API refuses it for API keys and for the MCP connector, and there is no MCP or WebMCP tool for it: an agent must not be able to approve the tool changes that would steer it.
- Changing the URL resets the baseline. A different endpoint is a different tool surface, so the next check starts over.
Settings
Section titled “Settings”| Setting | Default | Notes |
|---|---|---|
Transport (mcp_transport) | streamable-http | sse for servers that only speak the legacy HTTP+SSE transport |
Authentication (mcp_auth_mode) | none | header sends fixed headers with every request; oauth checks the discovery chain only |
Headers (mcp_headers) | - | Header mode only. Values are stored encrypted and never returned by the API; the web app shows header names only, and leaving a value blank on edit keeps the stored secret. Switching away from header mode deletes them |
Drift policy (mcp_drift_policy) | alert | record turns every drift row and finding into an event only |
Expected tools (mcp_expected_tools) | - | Up to 100 names. A missing one fails the check under either policy |
Max tools (mcp_max_tools) | 0 (no cap) | Up to 10,000 |
| Check interval | 10 minutes | Tool lists change with deploys, not by the second |
| Timeout | 20 seconds | Covers the handshake and every page of the tool list |
The monitor identifies itself honestly: client name alertkick-monitor
and the user agent AlertKick-MCP-Monitor/1.0, with a link to this
page.
What it does not do
Section titled “What it does not do”- It never calls a tool. It lists, fingerprints, and compares.
- No OAuth login yet. OAuth mode checks the discovery chain; it cannot list tools behind a login.
- Remote servers only. Servers that run locally over stdio, such as npm or PyPI packages started by a desktop client, cannot be reached by a poller.
- It cannot see per-user cloaking. A server that shows clean tool text to monitors and something else to specific users or clients is invisible to any outside check. A server that returns different tools per account will also show drift if the monitor’s key changes.
- Resources and prompts are not fingerprinted, only tools and the server instructions.
Tool text is treated as untrusted
Section titled “Tool text is treated as untrusted”Tool descriptions and server instructions come from the server being
watched, so AlertKick treats them as untrusted input. The monitor’s page
shows short excerpts as plain text. Events, alerts, and AI triage carry
tool names and change kinds only, never the text itself. The MCP tools
get_monitor and list_monitors omit descriptions, titles,
instructions, and finding excerpts and return a compact mcp_summary
instead, so a poisoned description cannot reach an assistant through
AlertKick.
Events and metrics
Section titled “Events and metrics”Every event in the drift table above, plus mcp_baseline_set and
mcp_baseline_accepted, appears on the monitor’s timeline. Drift and
finding events fire once, when the change first appears, while the check
keeps failing until it is accepted.
| Metric | Meaning |
|---|---|
mcp_tool_count | Tools listed by the server |
mcp_init_ms | Handshake time in milliseconds |
mcp_list_ms | Time to list every page of tools, in milliseconds |
The monitor’s response time is the whole check, handshake plus tool list.