Repository navigation
v2.1.0
Re-based on the MCP 2026-07-28 revision: tools/list is now one complete, deterministic, cacheable listing that never changes, and the runtime browser_tools activation toggle 2.0.0 shipped three days ago is removed. The honest headline on cost: the default listing grows from ≈2,457 to ≈8,471 tokens (46 entries, 33,886 bytes, measured over raw stdio against the real entry point), because a standards-conformant list must be complete and static — still −58% against 1.x's ≈20,200. CDP_TOOL_PROFILE=core keeps the lean ≈2,266-token surface (13 entries, 9,066 bytes, −89% vs 1.x) for hosts that eagerly load every schema. Tool names, parameters, and schema shapes are unchanged, now gated by a durable wire-vs-manifest deep-equal check rather than the one-off strip-descriptions comparison 1.10.0 and 2.0.0 used. No new runtime dependency.
Added
- The 2026-07-28 protocol era, actually served.
src/mcp.tsconnects throughserveStdio(dual-era, defaultlegacy: 'serve') instead ofServer.connect(new StdioServerTransport()). A modern client that opens withserver/discovergetssupportedVersions: ["2026-07-28"],resultType: "complete", and the_meta['io.modelcontextprotocol/serverInfo']envelope; a 2025-era client that opens withinitializestill negotiates2025-11-25and sees no difference at all. The era is pinned per connection, and the server logs which one it pinned to stderr. - Cache hints on the listing.
cacheHintsnow declaresttlMs: 3600000+cacheScope: "public"for bothtools/listandserver/discover(emitted on modern-era connections), instead of the SDK's conservative{ ttlMs: 0, cacheScope: 'private' }default. The listing is identical for every caller — a stdio server has no per-caller auth — and a restart under a differentCDP_TOOL_PROFILEor--browseris a different server configuration, not a mid-life change to this one. describe_toolnow doubles as the catalog. Itsnameparameter became optional; called with no name it returns the grouped catalog of every tool the selected browser supports — a header line (cdp-toolkit 2.1.0 · browser=chrome · 45 tools available, 46 in tools/list (CDP_TOOL_PROFILE=full)), then one[listed]/[hidden] <group> (n): namesline per group, thenUnlisted tools are callable by name; describe_tool {name} documents any of them.The whole 45-tool catalog is 1,141 characters.CDP_TOOL_PROFILEaccepts a comma-separated group list on top offullandcore— e.g.core,network,console(17 entries, 11,893 bytes, ≈2,973 tokens).coreis always included, so a list can never strand the basics, and the label is canonicalised to group order so equivalent spellings read the same. An unknown group name is now a configuration error rather than a silent fallback: the server printsCDP_TOOL_PROFILE: unknown tool group 'x'. Known: full, core, input, …and exits 1.src/version.ts— theVERSIONconstant moved out ofsrc/mcp.tsinto its own module, and a test asserts it equalspackage.json'sversion. The two drifted apart between 1.11.0 and 2.0.0; that can no longer happen quietly.test/mcp-protocol.test.ts— raw-stdio protocol coverage:server/discoverandtools/listfield shapes, the cache hints, byte-identical deterministic ordering across repeated calls and connections, the wire-vs-manifest deep-equal gate (the durable replacement for the one-off strip-descriptions gate), the legacyinitializehandshake, a-32022negative control, each profile, and proof that an unlisted tool is still callable.test/tool-profile.test.tscoversresolveProfiledirectly.
Changed
- The tool listing is static. It is computed once at startup —
describe_toolfirst, then manifest order, filtered by backend availability and then by the profile — frozen, and returned as the same array to everytools/liston every connection.capabilities.tools.listChangedis nowfalse; nothing will ever notify, because nothing ever changes. The 2026-07-28 revision makes it a MUST that a server's tool set not change as a side effect of other requests on the connection, and the client best-practices page notes that adding or removing tool definitions mid-conversation invalidates the host's prompt cache. CDP_TOOL_PROFILEdefault flippedcore→full. Three reasons, in order of weight: (1) the standard puts lazy discovery on the host — the client runs its own catalog → inspect → execute funnel — and a host that defers schemas (Claude Code lists MCP tool names and loads schemas on demand) pays little for a complete list; (2) consumers that hold per-tool permission allowlists, or per-tool interception keyed on the tool NAME, for non-core tools (the console and network readers,performance_analyze_insight) were silently broken by acoredefault, because those tools were simply absent fromtools/list; (3) a list that never changes never invalidates the prompt cache.coreremains a first-class choice for hosts that eagerly load every schema — it is now a deployment decision, not a default imposed on everyone.- Host protocol eras, MEASURED 2026-09-01. Two real hosts were pointed at the 2.1 server through a wrapper that tees its stderr: Claude Code 2.1.258 and Codex CLI 0.147.0 both connected and both logged
connection pinned to the legacy protocol era— today they still open withinitializeeven though their binaries carry the 2026-07-28 client strings (capability present, not exercised by default). The SDK 2.0.0Clientdoes the same unlessversionNegotiationopts in. So the dual-era default is load-bearing, not courtesy: a modern-only server would be unreachable from every host measured. The modern era is currently exercised by the raw-stdio test suite and bymcp:smoke's pinned client. The static listing pays off on either era; thettlMs/cacheScopehints are emitted only on modern connections. - The server
instructionsblock trades the activation walkthrough for how to read a fixed, cacheable listing (tools/listis complete and fixed for this process's life — cache it;describe_tool {name}/describe_tool {}; profile semantics; unlisted tools stay callable). 2,019 → 1,976 characters, still under the 2 KB cap. - SDK usage, deliberately unchanged in shape. The server stays on the low-level
Serverwith string-methodsetRequestHandler('tools/list', …), which SDK 2.0 still supports for advanced use cases (and whichMcpServeritself is built on;McpServerFactoryexplicitly accepts aServer).McpServer.registerToolwas evaluated and rejected on two grounds: it wants a Standard-Schema (zod) object forinputSchema, andCONTRACT.mdrule 1 forbids any runtime dependency beyond the SDK — our manifest is plain JSON Schema — and it cannot express "unlisted tools remain callable by name", which is the whole point ofCDP_TOOL_PROFILE. test/mcp-smoke.tsadditionally connects with the SDK client pinned to2026-07-28and asserts the modern era end to end, alongside the existing legacy handshake and livetools/callround trip.- Under
CDP_BROWSER=firefoxthe default listing is 35 entries (26,533 bytes, ≈6,633 tokens): the 34 tools Firefox's BiDi surface supports, plusdescribe_tool.
Removed
browser_tools— thelist_groups/activate/deactivate/set_profilemeta-tool, removed three days after 2.0.0 shipped it. Mutating the advertised tool set at runtime and firingnotifications/tools/list_changedis precisely what the 2026-07-28 revision forbids (the tool set MUST NOT change as a side effect of other requests) and what the client best-practices page warns invalidates the host's prompt cache. Its open question — "does any given client actually re-fetch onlistChanged?" — becomes moot once the list is static. Calling it now returnsunknown tool: browser_tools; nothing else needs it, because every tool it could have activated is either already listed under the newfulldefault or reachable by name under a narrower profile, anddescribe_tool {}covers whatlist_groupsreported.
Fixed
- v2.0.0 was not on the current protocol revision, despite running on the 2.0 SDK. Probed raw over stdio, it answered
server/discoverwith-32601 Method not foundand served atools/listcarrying noresultType,ttlMs, orcacheScope: a 2025-11-25 (legacy-era) server, becauseServer.connect(new StdioServerTransport())is the legacy path. The"2026-07-28"literal that appears inside the SDK packages isSUPPORTED_MODERN_PROTOCOL_VERSIONSin@modelcontextprotocol/server/stdio— the modern era is reachable only throughserveStdio(or the HTTPcreateMcpHandler), never throughconnect(). Both eras are now served.