---
title: MCP Server
description: Use the Specdiff MCP server to expose breaking-change detection as tools for AI coding agents via the Model Context Protocol.
url: https://pr-1-8289d63b6330.thally.app/specdiff/mcp
---

# MCP Server

Use the Specdiff MCP server to expose breaking-change detection as tools for AI coding agents via the Model Context Protocol.

Specdiff provides an MCP (Model Context Protocol) server that exposes breaking-change detection as tools for AI coding agents such as Claude. This allows AI agents to analyze schema changes programmatically and generate compatible updates to API clients and documentation.

## What is an MCP Server?

The Model Context Protocol is a standard for connecting AI models to external tools and data sources. The Specdiff MCP server runs as a subprocess, communicates with an AI agent via stdio, and provides four tools:

- Compare two documents for breaking changes
- Explain a single rule
- List all rules in the catalogue
- Format a diff result for display

## Installation

Install the `@specdiff/mcp` package:

```sh
npm install -D @specdiff/mcp
```

The package provides a binary `specdiff-mcp` that you can invoke directly or via `npx`:

```sh
npx @specdiff/mcp
```

## Configuration

### Claude Desktop

Add the server to `claude_desktop_config.json` (or `~/.config/Claude/claude_desktop_config.json` on macOS):

```json
{
  "mcpServers": {
    "specdiff": {
      "command": "npx",
      "args": ["-y", "@specdiff/mcp"]
    }
  }
}
```

### .mcp.json

For other MCP clients, add to `.mcp.json` in your project:

```json
{
  "mcpServers": {
    "specdiff": {
      "command": "npx",
      "args": ["-y", "@specdiff/mcp"]
    }
  }
}
```

The server listens on stdio and logs `"specdiff-mcp listening on stdio"` to stderr on startup.

## Tools

### specdiff_compare

Compare two documents for breaking changes.

**Input:**

- `beforePath` (optional string) — file path relative to server working directory
- `afterPath` (optional string) — file path relative to server working directory
- `before` (optional string) — inline JSON or YAML document text
- `after` (optional string) — inline JSON or YAML document text
- `kind` (optional string) — `"auto" | "openapi" | "json-schema"`, default `"auto"`
- `failOn` (optional string) — `"breaking" | "warning" | "info" | "none"`, default `"breaking"`
- `ignoreRules` (optional array) — rule codes to exclude from the result

**Returns:**

A `DiffResult` object plus:
- `passed` (boolean) — true when max severity is below the `failOn` threshold
- `failOn` (string) — the threshold that was applied

**Output shape:**

```ts
{
  changes: SchemaChange[],    // sorted by severity, path, code, message
  summary: DiffSummary,       // counts per severity level
  maxSeverity: Severity | null,
  kind: DocumentKind,
  passed: boolean,
  failOn: string
}
```

**Error cases:**

- Missing both `beforePath` and `before`: returns error `"Provide either beforePath or before."`
- Missing both `afterPath` and `after`: returns error `"Provide either afterPath or after."`
- Unknown rule code in `ignoreRules`: returns error with code, e.g. `"Unknown rule code in ignoreRules: my-rule."`
- Path escapes working directory: returns error `"Path <path> is outside the server's working directory (<cwd>); run specdiff-mcp from the project root."`
- Document load or parse failure: returns error with details

### specdiff_explain_rule

Look up a rule by code and return its full details.

**Input:**

- `code` (string) — a rule code, e.g. `"required-parameter-added"`

**Returns:**

```ts
{
  code: string,
  defaultSeverity: "breaking" | "warning" | "info",
  title: string,
  description: string,
  remediation: string,
  appliesTo: "openapi" | "json-schema" | "both"
}
```

**Error cases:**

- Unknown code: returns error `"No rule named <code>. Call specdiff_list_rules for the catalogue."`

### specdiff_list_rules

List all available rules.

**Input:** None

**Returns:** Array of `RuleInfo` objects (same shape as `specdiff_explain_rule` output), in catalogue order.

### specdiff_format

Format a diff result for display.

**Input:**

- `result` (object) — a `DiffResult` object from `specdiff_compare`
- `format` (string) — `"text"` for plain human-readable output, or `"markdown"` for GitHub-Flavored Markdown

**Returns:** Rendered report as a string.

**Error cases:**

- Invalid `format`: returns error `"specdiff_format failed: <message>"`
- Invalid `result`: returns error with parsing details

## Programmatic usage

You can also create and embed the server in a Node.js process:

```ts
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { createSpecdiffServer } from "@specdiff/mcp";

const server = createSpecdiffServer({ cwd: process.cwd() });
await server.connect(new StdioServerTransport());
```

The `createSpecdiffServer` function accepts an options object:

```ts
interface SpecdiffServerOptions {
  cwd?: string;  // defaults to process.cwd()
}
```

### Additional exports

- `TOOL_NAMES` — `{ compare: "specdiff_compare", explainRule: "specdiff_explain_rule", listRules: "specdiff_list_rules", format: "specdiff_format" } as const`
- `resolveInsideCwd(cwd: string, filePath: string): string` — resolve a file path inside `cwd`; throws if it escapes.
- `parseDocumentText(text: string, fileName?: string): unknown` — parse JSON or YAML text using the file name as a format hint.

## Security

**Path sandboxing:** File paths passed to `specdiff_compare` via `beforePath` or `afterPath` must resolve inside the server's working directory. Paths that escape (e.g., `../../../etc/passwd`) are rejected with an error. Always run the server from your project root.

**Input validation:** All tool inputs are validated and sanitized before use. The server will not load or parse files outside the working directory.