Managed-host debugging guide
Connected isn’t called.
A managed MCP host can show a server as connected while the server’s tool callback never runs. That is not necessarily a contradiction. “Connected” is a host-visible state; callback execution is a server-observed event. The useful question is: what is the last protocol boundary the test server actually observed?
MCP Host Canary creates a disposable known server, records only allowlisted protocol boundaries, and freezes them in a privacy-minimized receipt. It does not inspect a host’s internals or assign a compatibility grade.
Why a connection indicator is not callback evidence
After a host accepts an MCP URL and reports a connected state, several materially different server-side results can follow:
- the disposable endpoint was created, but no recognized MCP message arrived;
- a modern client optionally sent
server/discover, or a legacy client completedinitialize; - the host requested
tools/listand the server returned a manifest; - a
tools/callrequest arrived, but no registered callback execution was observed; - a registered canary callback actually ran.
Only the final case proves callback execution at this test server. A tool appearing in a host UI is
weaker evidence: it can be consistent with manifest delivery, but it does not establish that the host
later dispatched a call. Canary therefore records tools/call.request separately from
tools/call.executed.
Read the server-observed waterfall
2026-07-28
server/discover? (optional)
tools/list.request? → tools/list.response
tools/call.request? → tools/call.executed
2025-11-25 and earlier
initialize.request → initialize.response → initialized
tools/list.request? → tools/list.response
tools/call.request? → tools/call.executed
These are two protocol-era paths, not one required sequence. MCP 2026-07-28 removed the
initialize / initialized handshake. Its server/discover method is
optional, so a modern host can reach tools/list or tools/call without either of
those earlier rows. MCP 2025-11-25 and earlier use the legacy handshake path. The current
behavior is defined by the official MCP
specification and
discovery
documentation.
On the legacy path, initialize.request means the initialize message reached Canary.
initialize.response is added only when the handler completes the response successfully; it
does not prove the closed host accepted or used that response.
Likewise, tools/list.request proves a manifest request arrived. The stronger
tools/list.response stage is recorded only after Canary finds a returned tool list in the
successful response, which is also when it retains the safe tool count.
A tools/call.request stage proves a call request arrived. If its name matches a registered
fixture, the allowlisted name can be retained. tools/call.executed is stronger because the
registered callback ran. It still does not prove that the host presented the result correctly or that
a user’s own MCP server would behave the same way.
An absent stage means only “not observed by this server.” Missing modern discovery or legacy-only initialization is not itself a failed prerequisite. No absent row proves a specific cache, policy, parser, model choice, network component, or product bug inside the host. Stage counts are aggregates, not success rates.
Use two fixtures to separate common boundaries
The baseline profile exposes three compact zero-input tools for a normal discovery and
dispatch path. The limits profile exposes 258 tools, including sentinel_257 at
the ordering boundary and a schema-boundary fixture with a 16,385-byte description.
The project’s historical controlled runs illustrate why the distinction matters. Both Claude Web and
ChatGPT Developer mode reached an executed canary_ping callback in the baseline fixture.
In the limits fixture, the retained Claude observation reached successful 258-tool manifest responses
without a sentinel callback, while the retained ChatGPT observation reached executed
sentinel_257 callbacks. Attempts were not uniform, so these are boundary case notes—not
rates, rankings, or current host-wide claims. Read the
four-run evidence summary and its limitations before
comparing the rows.
Reproduce a boundary safely
- Create a fresh
baselinerun and connect its temporary URL to one managed host. - Use a fresh host conversation and the exact direct, indirect, and negative-control prompts shown by the canary.
- Record the callback-count change after each prompt. Disable other connections where the host permits it.
- Create a separate
limitsrun and requestsentinel_257once in a fresh conversation. - Finalize each run before copying its Markdown or JSON receipt. Finalization closes the endpoint.
Runs expire after at most 30 minutes and live only in one in-memory process. A deployment, restart, or free-instance suspension can remove active runs and receipts. This is an experimental diagnostic fixture—not a conformance suite, security scanner, proxy, or production gateway.
Share the receipt, not the capability
A finalized receipt can contain the run profile, status, timestamps, stage counts, safe HTTP status, normalized client family, valid protocol version, manifest tool count, and recognized canary tool name. It excludes active MCP URLs, owner identity, sessions, tokens, Authorization values, prompts, responses, bodies, tool arguments, IP addresses, and raw User-Agent strings.
Never publish a live /mcp/:id capability URL or an owner-only API URL. Treat the receipt
fingerprint as a correlation identifier, not a digital signature. Pair the finalized receipt with
explicitly labeled operator context and use it to narrow the next direct test or support ticket without
claiming a cause the server could not observe.
Test your own managed host
Create a disposable 30-minute endpoint without signup or installation, then compare the observed boundary.
Run MCP Host CanaryPrefer to inspect the implementation first? Read the MIT-licensed source or report a finalized safe receipt.