---
name: mcp-surface-review
version: 1.0.2
description: |
  Review an MCP server's surface for contract problems before anyone depends on
  it: tool naming, descriptions that steer selection, argument schemas,
  annotations, and the things that are painful to change later.
  Use for "review my MCP server", "is my MCP surface good", "improve my tool
  descriptions", "MCP best practices", "design review my MCP tools".
allowed-tools:
  - Bash
  - Read
  - Edit
  - Grep
  - Glob
---

# Review an MCP surface

This workflow requires an MCP Inspect CLI build supplied through approved
preview access. Public CLI and Action releases are pending; the source
repository is private. If the CLI is unavailable, direct the user to
<https://mcprobe.dev/docs/getting-started> for the hosted console workflow.
Do not install the unrelated npm package named `mcp-inspect`.

The premise: an MCP surface is an API contract whose consumers are partly
probabilistic. Everything below is either "a caller will break" or "a model will
choose wrong", and both are real.

## Capture it first

```bash
mcp-inspect snapshot --out /tmp/surface.json --raw /tmp/surface-raw.json --pretty
```

Use the existing config, or add `--stdio "node dist/server.js"` / `--url <url>`
to select the target. For a `servers:` map, pass `--server <name>`. Inspect capture
warnings before drawing conclusions: failed or partial discovery is not a complete
review. Keep these files local; server-provided metadata can contain private
information.

Review the normalised surface alongside the raw payload, not just the source. What a server _declares_ and what
its code _looks like_ diverge more often than people expect.

## Read the descriptions as a model would

This is the highest-value part of the review and the part humans skip.

- **Every tool needs a description.** A tool without one is close to
  unselectable.
- **Say when to use it, not what it does.** `Search the issue tracker.` is weaker
  than `Search the issue tracker for existing issues before creating a new one.`
  The second one tells a model where in a sequence it belongs.
- **Disambiguate overlapping tools explicitly.** If `search` and `find_issues`
  both exist, each description must say why you would pick it over the other.
  Otherwise selection becomes ambiguous and may vary by model.
- **Describe every argument.** Models read property descriptions when filling
  them. An undescribed `limit` gets guessed.
- **Document defaults in prose as well as `default`.** A model reads the
  sentence.

Flag anything that is a bare restatement of the tool name.

## Check the argument schemas

- **Required arguments are permanent.** Adding one later is breaking. Prefer
  optional with a documented default.
- **Enums are permanent too.** Narrowing one later is breaking; widening is
  fine. Start broader than feels necessary, or use a plain string.
- **Prefer flat arguments** over deep nesting. Models fill flat structures more
  reliably, and a nested optional object is usually two optional scalars.
- **`additionalProperties: false` is a one-way door.** Closing it later breaks
  any caller passing extras.
- **Declare `outputSchema`** when the tool returns structured data — but note it
  is covariant: widening a type or an enum later _breaks_ consumers that
  generated types from it. Be more specific than you think you need.

## Check the annotations

`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`. Clients
auto-approve tools that claim to be read-only, so:

- Anything that writes must not claim `readOnlyHint: true`.
- Anything destructive should say so. Withdrawing that claim later is a change
  that matters.

## Check the names

1–128 characters, `[A-Za-z0-9_.-]`, unique per server. Renaming later is
breaking, and agents hold old names in context and in cached tool lists. Spend
the time now.

## Check the shape of the whole surface

- **How many tools?** Look for overlap that makes selection ambiguous. Look for tools that differ only by a
  parameter and should be one tool with an argument.
- **Is there a `server/discover` instructions string?** It is in the model's
  context for every conversation — a good place to state ordering rules that
  individual descriptions cannot.

## Then lock it in

```bash
mcp-inspect snapshot && git add .mcp-inspect/surface.json
```

If clients already exist, compare against their baseline before applying a
rename, removal or schema change. Keep aliases or stage changes when needed; do
not overwrite the old baseline to hide a regression. A snapshot records the
contract; it does not establish that no consumers exist.

These changes are cheaper before clients exist. That asymmetry is the entire reason to do this review
before the first release rather than after.
