All posts

MCP tool surfaces need release review

MCP makes it trivial to expose dozens of tools to an agent. The exported tool surface is a release artifact — review wildcards, scopes, and undocumented actions before promotion.

The Model Context Protocol made one thing very, very easy: exposing tools to an LLM. A few lines of TypeScript or Python, an mcp.server.list_tools() handler, and an agent can suddenly read your filesystem, send Slack messages, query your database, deploy to staging.

That ease is a feature. It’s also a release-readiness problem.

When the cost of exposing a tool drops to ~zero, the cost of reviewing the exposed surface needs to drop to match. Today, on most teams, it hasn’t. The MCP server gets written, the agent gets pointed at it, and nobody has a finite list of “what can this agent now do” they can review before promotion.

This post is the case for treating an MCP export as a release artifact.

The shape of an MCP export

An MCP server’s listTools response is a JSON array of tool definitions:

[
  {
    "name": "send_message",
    "description": "Send a message to a Slack channel.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "channel": {"type": "string"},
        "text": {"type": "string"}
      },
      "required": ["channel", "text"]
    },
    "annotations": {
      "destructiveHint": true,
      "title": "Send Slack message"
    }
  }
]

That JSON is the contract between the agent and the world. Every action the agent can take corresponds to an entry in that array. Every entry is a release decision — “do we want this agent able to do this in production?” — that someone needs to make.

Most teams write the MCP server, deploy it, and treat the contract as implicit. The agent gets whatever the server returns. The reviewer never sees a finite list.

What goes wrong

Five failure modes show up over and over once teams start treating MCP seriously:

1. Wildcard surfaces. A team enables an MCP server “the team owns” without knowing it exposes 47 tools, three of which are write-shaped. Nobody reviewed the 47.

2. Drift. The MCP server adds a new tool in a minor version bump. The manifest says “use this MCP server”; the agent now has a new capability nobody approved.

3. Opaque metadata. The server marks all tools destructiveHint: false because the maintainer didn’t think about it. The agent ships with mutating actions that look read-only to anyone trying to do scope review by inspecting annotations.

4. Schema sloppiness. A Slack-like tool takes text: string with no max length. The model can put arbitrary text into customer-facing messages. Nobody catches it because the eval suite doesn’t test for prompt injection in tool inputs.

5. Mixed server-side and client-side tools. Anthropic-managed server tools (web_search_*, code_execution_*) get bundled with custom client tools in the same export. Reviewing them as one bag hides which ones the team actually controls.

Each one is the kind of thing you’d catch in a careful review. The problem is that “careful review” doesn’t scale to hundreds of MCP servers across an organization.

The MCP export as a release artifact

The fix is to treat the MCP export the same way you treat an OpenAPI spec on a service deployment: it’s the contract, it’s checked into the release, it’s diffed in code review, and it’s gated by an automated check.

In practice that looks like:

# shipgate.yaml
version: "0.1"
project:
  name: support-agent
agent:
  name: support-agent
  declared_purpose:
    - answer customer support questions
    - look up case status
environment:
  target: production_like
tool_sources:
  - id: support_mcp
    type: mcp
    path: mcp/exports/support-server.json
policies:
  require_approval_for_tools:
    - cancel_subscription
    - issue_refund

The MCP export is checked into the repo. On every PR that touches it, Agents Shipgate re-scans the surface and produces a finding list:

Status: Release blockers detected
Critical: 1 · High: 6 · Medium: 3

Top findings:
1. cancel_subscription lacks a declared approval policy
   evidence: destructive=true, write=true, scope=billing:write
2. wildcard_mcp_tools.* exposes wildcard tool surface
   evidence: 12 tools matched, 4 write-shaped
3. send_message annotations claim destructiveHint=false
   evidence: name+description suggest customer_communication+write

The reviewer sees a diff. The diff has evidence. The merge waits on remediation.

Where MCP review fits with platform review

A mature platform team reviews each new service deployment for scopes, ownership, on-call rotation, and SLOs. The same kind of review needs to exist for MCP servers and their exports.

The right place for an MCP-specific gate is upstream of agent adoption:

  • The MCP server team reviews their own export at PR time
  • A central agent platform team reviews any new MCP server before it’s added to the registry of approved sources
  • Agent teams add an approved MCP source to their shipgate.yaml, triggering a scan on their side too

Each layer catches a different failure: the server team catches schema sloppiness and metadata gaps; the platform team catches wildcards and broad-scope risk; the agent team catches contradictions between their declared purpose and the surface they imported.

What good looks like for MCP builders

If you maintain an MCP server, three concrete things make your export easier to review:

  1. Annotate write/destructive tools accurately. Set annotations.destructiveHint: true on anything that mutates state, even if it doesn’t feel “destructive.” Reviewers rely on this.
  2. Bound free-form fields. A text: string parameter with no max length is a footgun. Add maxLength, enum, or split into structured fields where you can.
  3. Stable tool names across versions. Renaming a tool is a breaking release event. Treat the export’s name field with the same care as an HTTP route.

If you ship MCP servers and want to wire scanning into your release workflow, the GitHub Action quickstart is five minutes of YAML.

The MCP ecosystem is moving fast. The release-readiness surface should move with it.

Install agents-shipgate GitHub