feat(mcp): /mcp and an MCP context segment — S5 (visibility) - #57
Merged
Conversation
Until now MCP was entirely invisible unless it happened to work: no way to ask what is configured, why a server is not being used, or what these servers cost in context every turn. Two surfaces close that. 1. `/mcp` — lists CONFIGURED servers, not running ones. Deliberate: the questions worth asking are "why is my server not being used?" and "what is this repo asking to run?", and a list built from live handles answers neither — the interesting server is the one that is absent. Each row carries state, provenance, and the literal command it would run. State distinguishes `needs approval` from `not started`, because a repo server waiting on the G1 consent card is working exactly as designed and saying only "not running" would send the user debugging a non-problem. When a server IS live, tool names and their allow-list state come from the same buildAgentTools + classifyMcpTool the agent uses — so the list cannot claim a tool is allow-listed while runTool refuses it. A destructive tool sitting in the allow-list is counted as NOT allowed, matching what actually happens. summarizeMcp() is pure and unit-tested; the command re-reads config each time, because /mcp is most useful right after an edit that appeared to do nothing. 2. An `MCP tools` segment in the context-usage popover. Every tool schema rides EVERY turn, so a chatty server is a standing tax on the window rather than a one-off — and it was folded invisibly into `Tools`. Carved OUT of that slice, never added alongside: `tools` already counts every schema, MCP included, so adding would double-count and the bar would stop summing to `used` — which does not look like a bug, it looks like the model used more context than it did. A dedicated test asserts the segments always total the window across the MCP, no-MCP, over-estimate and junk-input cases. Hidden when no server contributed a schema, which is nearly every run. Found while testing: ctxSegments took `x || 0` for its numeric inputs, which rescues NaN and null but passes a STRING through to Math.max, yielding NaN and silently un-summing the whole bar. Unreachable today (the caller type-checks), but this function's entire contract is that the segments add up, so it now coerces. 66 mcpConfig cases, 5 ctxSegments, 11 webviewCss; 24 suites, 0 failures.
Contributor
There was a problem hiding this comment.
Pull request overview
Adds visibility surfaces for MCP in the LevelCode AI extension so users can (1) inspect configured MCP servers and their trust/running state, and (2) see the standing context-window cost of MCP tool schemas per turn.
Changes:
- Introduces a
/mcpslash command in the chat webview, backed by an extension-side “overview” that re-reads merged MCP config and correlates it with live handles, tool allow-policy, and G1 launch trust. - Splits “Tools” context usage into “Tools” + “MCP tools” (carved out of the existing tools slice) and hardens
ctxSegments()input coercion to avoid NaN propagation. - Adds unit/static tests covering
/mcpwiring/rendering,summarizeMcp()behavior, and the context-segment invariants.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| extensions/levelcode-ai/media/chat.html | Adds /mcp command handling + MCP list rendering; adds MCP tools context segment and numeric coercion hardening in ctxSegments(). |
| extensions/levelcode-ai/extension.js | Implements mcpOverview() and wires listMcp ↔ mcpList message flow end-to-end. |
| extensions/levelcode-ai/mcpConfig.js | Adds pure summarizeMcp() to produce configured-first MCP status rows, including per-tool allow-state via shared agent classification logic. |
| extensions/levelcode-ai/agent.js | Computes and reports mcpTools token estimate alongside tools for context breakdown. |
| extensions/levelcode-ai/test/mcpConfig.test.js | Adds S5 coverage for configured-vs-running listing, trust-state semantics, allow-state reporting, and junk-input tolerance. |
| extensions/levelcode-ai/test/webviewCss.test.js | Adds static assertions that /mcp is intercepted, wired, and renders all row states with escaped, wrapping command lines. |
| extensions/levelcode-ai/test/ctxSegments.test.js | New extracted-from-chat.html unit tests asserting segment-sum invariants and MCP carve-out behavior. |
| docs/MCP.md | Updates S5 documentation to reflect completed /mcp and context-usage segment behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #55 — review that first; this diff is only the two visibility surfaces.
Until now MCP was invisible unless it happened to work. There was no way to ask what is configured, why a server is not being used, or what these servers cost in context on every turn.
1.
/mcp— configured servers, not running onesThat distinction is the whole design. The questions worth asking are "why is my server not being used?" and "what is this repo asking to run?" — and a list built from live handles answers neither, because the interesting server is the one that is absent.
Each row carries state, provenance, and the literal command it would run:
runningneeds approvalnot startedneeds approvalis separated fromnot startedon purpose: a repo server waiting on consent is working exactly as designed, and reporting only "not running" would send someone debugging a non-problem.When a server is live, tool names and their allow-list state come from the same
buildAgentTools+classifyMcpToolthe agent uses — so the list cannot claim a tool is allow-listed whilerunToolrefuses it. A destructive tool sitting in the allow-list is counted as not allowed, matching what actually happens at call time.summarizeMcp()is pure and unit-tested. The command re-reads config on each invocation, because/mcpis most useful immediately after an edit that appeared to do nothing.2. An
MCP toolssegment in the context popoverEvery tool schema rides every turn, so a chatty server is a standing tax on the window rather than a one-off — and it was folded invisibly into
Tools.The segment is carved out of that slice, never added alongside it.
toolsalready counts every schema, MCP included, so adding would double-count and the bar would stop summing toused— which does not read as a bug, it reads as the model used more context than it did. A dedicated test asserts the segments always total the window across the MCP, no-MCP, over-estimate and junk-input cases.Hidden entirely when no server contributed a schema, which is nearly every run.
Found while testing
ctxSegmentstookx || 0for its numeric inputs. That rescuesNaNandnullbut passes a string straight through toMath.max, which yieldsNaNand silently un-sums the whole bar. Unreachable today — the caller type-checks — but this function's entire contract is that the segments add up, so it now coerces properly.Verification
24 suites, 0 failures.66 mcpConfig cases, 5 new ctxSegments, 11 webviewCss.The webview is covered by static assertions, matching how
chat.htmlis tested here (there is no DOM harness): the command is wired end to end, all three row states exist, and the command line is rendered escaped and wrapping rather than truncated.Remaining
S6 — Streamable HTTP transport and the
2026-07-28revision, resources/prompts, and a "Manage MCP servers…" QuickPick. Reach, not security: every gate in §4 is enforced as of #55.