Skip to content

MCP servers whose tool surface exists only as code have no route: detect reports the official MongoDB and Grafana servers as not agent projects #431

Closed

Description

@pengfei-threemoonslab

Summary

Shipgate reports that the official MongoDB MCP server is not an agent project:

$ agents-shipgate detect --workspace mongodb-mcp-server --json
is_agent_project : false
frameworks       : []
suggested_sources: []
next_action      : "Stop: Workspace has no framework imports, no tool artifacts,
                    and no prompt directory."

That repository publishes 49 MCP tools, including drop-database, drop-collection,
delete-many, update-many, atlas-create-cluster and atlas-create-db-user. It is
precisely the kind of surface this product exists to gate, and there is no route to it today.

The important finding is what the repository actually contains, because it changes what the
ask should be. This is not a Zod problem.

The gap, measured

Reproduced on 9233c6b8, against
mongodb-js/mongodb-mcp-server#1417
("request elicitation for aggregations that include write stages") — a PR that adds a human
confirmation control to write-stage aggregations.

verify --preview on that diff:

diff completeness: complete   files: 17
should_run       : None   evaluation_status: unclassified   matched_rules: []   diff_tokens: []

The abstention itself is correct — #403's "skip requires positive evidence" is doing its job,
and this reports unclassified rather than claiming nothing changed. But no rule classified a
diff that adds a confirmation control to MCP write tools, and detect cannot see the project
at all, so there is nothing downstream to abstain into.

The only route the suggested-source probe found was filename-shaped, and it correctly rejected
what it found:

"excluded_sources": [{"type": "mcp", "path": "packages/mongodb-atlas-mcp-remote/package.json",
                      "reason": "MCP tools file must contain a tools array"}]

What the repository already declares statically

Every tool is a class with two literal static fields:

export class AggregateTool extends MongoDBToolBase {
    static toolName = "aggregate";
    public description = "Run an aggregation against a MongoDB collection";
    public argsShape = { ...ConnectionIdArgs, ...CollOperationArgs, ...AggregateArgs };
    static operationType: OperationType = "read";
}

Counted across the tool tree:

unique static toolName = "…" literals 49
static operationType literals 54
distribution read 19 · create 11 · metadata 9 · delete 6 · update 5 · connect 4

So the repository states, in static string literals, both the tool name and its operation
class
— the two things the declaration questionnaire (#410) asks a human for. Thirty-five of
the fifty-four are already classified as something other than read.

Reading those two fields requires no Zod evaluation, no type inference, and no TypeScript
compilation. The Zod argsShape matters only for the SHIP-SCHEMA-* family, which is
secondary to the gate.

Surface discipline

Answering CONTRIBUTING.md § Surface discipline
directly, because this request is squarely in its scope.

1. Which headline metric does this move? Activation rate and time-to-first-verdict. For this
class of repository both are currently undefined rather than poor: detect says "Stop", so
there is no adoption path to be slow at. Every MCP server implemented in TypeScript — which is
the reference SDK's first-class language — is in this state.

2. Can an existing surface carry it? Partly, and this deserves an honest answer. An adopter
can export tools/list to JSON and point tool_sources[].type: mcp at it, which the existing
adapter reads at high confidence. That works and needs no new surface. Its limits: the export
is a build-and-run artifact this repository does not produce, it is not in the PR diff so it
cannot gate a PR without a CI step that builds and starts the server, and generating it is
exactly the runtime dependency the static default path exists to avoid. It is a good answer for
a team adopting deliberately; it is not an answer for detect finding the repository at all.

3. Does it respect the roadmap non-goals? The
non-goal is conditional, and worth quoting rather than paraphrasing:

New adapters (AutoGen, Semantic Kernel, LlamaIndex, additional language surfaces) are accepted
only when a real workflow needs one — they do not advance the core thesis and are not a
priority.

This is filed as evidence for that clause, not as an exception to it. The judgement of whether
"a real workflow needs one" is the maintainer's; what this issue supplies is the workflow: a
first-party vendor MCP server with 49 tools including database drops, which the tool cannot
currently see.

Options, smallest first

A. Trigger rule + documented export recipe. No new adapter. One content-shaped trigger rule
that recognises MCP tool registration in TypeScript, so the gate engages and routes the adopter
to produce an export, plus a documented CI recipe for generating it. Moves nothing on its own —
detect still says Stop — but stops the silent miss.

B. Name-and-operation extraction (recommended). A narrow static reader for the two literal
fields above: toolName and an operation-class field, at medium confidence, no Zod
evaluation. This produces a catalog of 49 tools that flows into the existing questionnaire,
where effects and authority are declared by a human anyway. It is the minimum that makes the
repository gateable, and it reuses the entire decision engine rather than adding a parallel one.

C. Full TypeScript/Zod adapter. Parse argsShape into tool schemas for the SHIP-SCHEMA-*
checks. Largest surface, hits the non-goal hardest, and buys the least per unit of work given
that effects and authority are declared regardless.

Recommendation: B, with A folded into it. C only if schema findings turn out to be what
adopters ask for, which no evidence currently suggests.

Worth noting that B generalises past this repository. toolName-style literal registration is
the common shape across TypeScript MCP servers; the specific field names differ, which is the
real design question — whether this is one reader with a configurable field mapping, or a
per-server pattern list, and which of those stays honest about what it actually proved.

Acceptance


Found 2026-08-24 while walking mongodb-mcp-server#1417, chosen as a test case because it adds
a safeguard rather than a capability — a shape the gate should recognise as strengthening.

Activity

  1. added
    enhancementNew feature or request
    P2Queued; valuable but not blocking
    area:adapterFramework input adapters and tool catalog construction
    on Aug 27, 2026
  2. pengfei-threemoonslab commented on Aug 27, 2026

    @pengfei-threemoonslab
    ContributorAuthor

    This is scoped too narrowly — the gap is not TypeScript, it is any MCP server that does not emit an export. New evidence from walking grafana/mcp-grafana#1080, which is Go.

    detect  → is_agent_project: false
              "Stop: Workspace has no framework imports, no tool artifacts, and no prompt directory."
    preview → should_run: None, evaluation_status: unclassified, matched_rules: []
    

    Same outcome as MongoDB, different language. Three vendor MCP servers now measured:

    repo language tool-name declaration shipgate sees it?
    mongodb-mcp-server TypeScript static toolName = "aggregate" no
    mcp-grafana Go mcpgrafana.MustTool("update_incident", …) no
    github-mcp-server Go generates checked-in .snap JSON yes

    The discriminator is not the language — two of these are Go. It is whether the repository happens to emit its tool list as a checked-in artifact, and only one of the three does.

    The extraction remains trivial. One grep over the Go source produced the complete surface and the exact delta:

    base: 99 tools   head: 100 tools   added: update_incident
    

    Which is the same finding as MongoDB's 49 static toolName literals: tool names are static string literals at a registration site. The spelling of the registration differs per server (static toolName =, MustTool("…"); the literal does not.

    Suggested changes to this issue:

    1. Retitle to something language-agnostic — the gap is "MCP servers whose tool surface exists only as code", covering both.
    2. Option B is unchanged but generalises. It was "read two static class fields"; it is really "read the tool name literal at a registration site". The open design question I flagged — one reader with a configurable pattern, or a per-server list — is now the central question rather than a footnote, since the pattern demonstrably differs across servers while the literal does not.
    3. The activation argument strengthens: this is not one vendor's stack choice, it is the default state of the ecosystem, with github-mcp-server the exception because it invested in generating snapshots.

    Happy to retitle and rewrite the body if you want it restructured rather than amended by comment.

  3. pengfei-threemoonslab commented on Aug 29, 2026

    @pengfei-threemoonslab
    ContributorAuthor

    Settling the scope and the open design question, and retitling per the 2026-08-27 comment.

    Decision: Option B, generalized — a built-in registry of named idiom recognizers

    The central question the Grafana evidence raised — one reader with a configurable pattern, or a per-server list — resolves to neither: a built-in, versioned registry of named registration idioms, shipped with the product.

    Scope by measurement, not by ambition

    Before implementing, a half-day survey: count registration idioms across the top ~30 registry servers, then ship the smallest set that covers the beachhead and the measured majority. Expected from the three walks:

    1. TS SDK call-site registration — server.tool( / server.registerTool( with a string-literal first argument;
    2. the static-field class shape — static toolName = "…" (+ an operation-class field where present, as in MongoDB);
    3. Go call-site registration — MustTool("…" / AddTool( shapes.

    Anything below the survey's coverage line waits for evidence, same rule as Option C.

    Acceptance additions

    Priority

    Raising P2 → P1. Three first-party vendor MCP servers measured, two invisible; this issue is the front door for the whole class. Filed alongside it as one increment: #462 (the annotation-vs-evidence contradiction finding — the check that runs once these servers are visible) and #463 (surface-first rendering for unadopted repositories — the artifact a maintainer reads when it does). The three together are what turns "detect says Stop" into a report worth showing the people who maintain these servers.

  4. added
    P1Picked up inside the current milestone window; keep about 15 open
    on Aug 29, 2026
  5. changed the title [-]TypeScript MCP servers have no route: `detect` reports the official MongoDB MCP server (49 tools, incl. `drop-database`) as not an agent project[/-] [+]MCP servers whose tool surface exists only as code have no route: `detect` reports the official MongoDB and Grafana servers as not agent projects[/+] on Aug 29, 2026
  6. added this to the v0.18.0 milestone on Aug 31, 2026
  7. pengfei-threemoonslab commented on Aug 31, 2026

    @pengfei-threemoonslab
    ContributorAuthor

    Milestone: v0.18.0 — the four-flags cycle. This route is the front door for cold evaluation of MCP servers; it feeds the incident fixtures (#471) and the coverage report (#473).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Picked up inside the current milestone window; keep about 15 openarea:adapterFramework input adapters and tool catalog constructionenhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions