A linter for MCP servers
One command. Connects to your server, runs the handshake, walks every tool, and reports exactly what's malformed. Non-zero exit, so it drops straight into CI.
npx mcp-probe ./server
mcp-probe is a command-line linter for Model Context Protocol servers. It runs your server, speaks the protocol to it, and reports where the server's answers don't match the spec.
go vet for Go. A static check you run before shipping — except MCP servers can't be read statically, so it starts the server and asks.initialize handshake, the declared capabilities, and every tool the server exposes — its name, its description, and its inputSchema. Then it deliberately sends a bad request to see how the server fails.0 if clean, 1 if anything is broken. Nothing is uploaded; nothing phones home.Every check maps to something that actually breaks when an agent calls your server. Errors fail the run. Warnings don't — but they're the ones that cause wrong tool selection.
| Check | What breaks if you skip it |
|---|---|
| initialize handshake | Client rejects the connection outright. Protocol version must be one the client understands; capabilities must be declared for anything you serve. |
| inputSchema is valid JSON Schema | Every entry in required must exist in properties. Every property needs a type. Every $ref must resolve. Clients silently drop tools with broken schemas. |
| JSON-RPC 2.0 error shape | An unknown tool or bad params has to come back as a proper error object with a code — not a stack trace. Agents can recover from -32602. They can't recover from a string. |
| tool names are unique & well-formed | Duplicate or empty names collide in the client's tool table. Some clients reject names outside [a-zA-Z0-9_-]. |
| descriptions carry enough to choose on | The description is the prompt. Too short, and the model can't tell cancel_order from delete_order. Flags very short, empty, and placeholder text. |
| free-text params are constrained | A string with no enum, pattern, or maxLength is an open door. Warned, not failed — sometimes it's intentional. |
Exit 0 on clean, exit 1 on any error. Runs against stdio or streamable HTTP. No account, no service, no telemetry — it's a CLI.
# .github/workflows/mcp.yml on: [push, pull_request] jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npx mcp-probe ./server
Narrow on purpose.
create_order is callable correctly — not that it creates an order.The connect-and-walk library under mcp-probe is shared. The next three are thin layers on it, and they ship in this order.
server.json, emits submission payloads.