Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

MCP Server

git paw mcp runs a read-only Model Context Protocol (MCP) server over stdio, so any MCP-aware client — Claude Desktop, Cursor, ChatGPT Desktop, Windsurf, VS Code MCP extensions — can query a repository’s state without an active git-paw session.

It exposes the same information git-paw already tracks, as deterministic, read-only tools:

  • Coordination — active agent intents and detected conflicts
  • Governance — your configured ADRs, test strategy, security checklist, Definition of Done, and constitution
  • Project knowledge — discovered specs and tasks (OpenSpec, Markdown, Spec Kit), the spec dependency graph, and rendered agent skills
  • Session state — the active session’s status/summary and the session-learnings file
  • Git context — branches, recent commits, and branch diffs
  • Source/Files — browse the local working tree, read a file, and search code contents (gitignored paths excluded; reads confined to the repo root)

The server is standalone: it does not need a tmux session, broker, or supervisor. When a data source is unavailable (no broker, no session, no governance config) tools return well-formed empty/null results rather than errors — so the client always gets an unambiguous answer.

Read-only in v0.7.0. There are no write tools (no creating specs, controlling sessions, or delivering feedback) — those are planned for a later release. The server also never invokes an agent CLI (claude, gemini, …) as an inference backend; every result comes from files, git, and broker state.

How it works

The MCP client spawns git paw mcp as a child process and talks to it over the process’s stdin/stdout using newline-delimited JSON-RPC 2.0. The client owns the lifecycle: the server starts when the client launches it and exits cleanly when the client closes its stdin.

git paw mcp [--repo <PATH>] [--log-file <PATH>]
FlagPurpose
--repo <PATH>Operate against a specific repository instead of the current directory. Required for Claude Desktop (see below).
--log-file <PATH>Also write diagnostics to a file. stderr is always used; stdout is reserved for the JSON-RPC stream.

Verbosity follows the standard RUST_LOG convention (default warn):

RUST_LOG=debug git paw mcp --repo /path/to/repo

Repository resolution

  1. If --repo <PATH> is given, the server uses that path (it must be inside a git repository).
  2. Otherwise the server walks up from the current directory to the nearest enclosing git repository. Inside a git worktree, it resolves to the worktree’s own root.

If no repository can be found, the server prints a clear error to stderr and exits non-zero — it never silently serves nothing.

Server identity

In the MCP initialize handshake the server advertises serverInfo.name = "git-paw" and its real crate version. To distinguish several repositories that each run git paw mcp, set a custom name in the repo’s .git-paw/config.toml:

[mcp]
name = "my-project"   # advertised as serverInfo.name; defaults to "git-paw"

This is the server’s own advertised identity. It is distinct from the display label some clients show, which comes from the mcpServers key in the client config (e.g. "git-paw" in the snippet below) — that key is yours to rename per client entry regardless of [mcp].name.

Per-client setup

Each client needs its own entry pointing at this server. The command is git with args paw mcp … (git-paw installs as a git subcommand). If you prefer, git-paw mcp … works too.

Claude Desktop

Claude Desktop spawns MCP servers from its own application-support directory, not your project — so current-directory discovery cannot work. You must pass --repo with an absolute path.

  1. Open the config file:

    • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
    • Windows: %APPDATA%\Claude\claude_desktop_config.json
  2. Add a server entry:

    {
      "mcpServers": {
        "git-paw": {
          "command": "git",
          "args": ["paw", "mcp", "--repo", "/absolute/path/to/your/repo"]
        }
      }
    }
    
  3. Restart Claude Desktop completely (quit and reopen).

  4. Verify: open a new chat, click the tools/🔌 icon, and confirm git-paw is listed with its tools. Ask “what specs are pending in this repo?” and confirm a tool call runs.

Cursor

Cursor is workspace-aware and spawns from the workspace root, so bare git paw mcp works (or pass --repo to be explicit).

  1. Create .cursor/mcp.json in your project (or edit the global ~/.cursor/mcp.json):

    {
      "mcpServers": {
        "git-paw": {
          "command": "git",
          "args": ["paw", "mcp"]
        }
      }
    }
    
  2. Reload Cursor (or toggle the server in Settings → MCP).

  3. Verify: open Settings → MCP and confirm git-paw shows a green status with its tools listed.

ChatGPT Desktop (macOS)

ChatGPT Desktop supports local MCP servers in Developer Mode. (ChatGPT Web in a browser is not supported — see Known limitations.)

  1. Enable Settings → Connectors → Advanced → Developer mode.
  2. Add a connector with command git and args paw mcp --repo /absolute/path/to/your/repo (ChatGPT Desktop, like Claude Desktop, does not run from your project directory, so pass --repo).
  3. Restart ChatGPT Desktop.
  4. Verify: start a new chat, open the connector/tools menu, and confirm git-paw and its tools are available.

Windsurf

  1. Open Settings → Cascade → MCP Servers → Add Server (or edit ~/.codeium/windsurf/mcp_config.json):

    {
      "mcpServers": {
        "git-paw": {
          "command": "git",
          "args": ["paw", "mcp", "--repo", "/absolute/path/to/your/repo"]
        }
      }
    }
    
  2. Refresh the MCP server list (or restart Windsurf).

  3. Verify: the MCP panel shows git-paw connected with its tools.

VS Code (MCP)

VS Code (1.102+) supports MCP servers. It is workspace-aware, so bare git paw mcp works from a workspace folder.

  1. Create .vscode/mcp.json in your workspace:

    {
      "servers": {
        "git-paw": {
          "type": "stdio",
          "command": "git",
          "args": ["paw", "mcp"]
        }
      }
    }
    
  2. Run MCP: List Servers from the Command Palette and start git-paw (or reload the window).

  3. Verify: in Agent mode, open the tools picker and confirm the git-paw tools appear.

Known limitations

  • ChatGPT Web is not supported. The browser version of ChatGPT cannot spawn local processes, which stdio MCP requires. ChatGPT Desktop on macOS works (with Developer Mode). ChatGPT Web waits for a future HTTP transport.
  • Per-repo configuration is required. Each repository needs its own entry in the client config (with its own --repo path where the client spawns from a fixed directory). For many repos this is repetitive; a single-server, multi-repo registry is planned for a later release.
  • Claude Desktop (and ChatGPT Desktop) need --repo. They spawn MCP servers from their own app-support directory, not your project, so current-directory discovery cannot find your repo. Workspace-aware clients (Cursor, VS Code MCP, Windsurf) work with a bare git paw mcp.

Tool reference

All tools are read-only. Collection results are empty ([]) and single-record results are null when the underlying data is unavailable. Governance tools return a protocol error only when a configured document path exists but cannot be read.

Coordination

ToolInputResult
get_intents{ intents: [{ branch_id, files, regions, summary, published_at, valid_for_seconds }] }
get_intent{ branch_id }{ intent: <intent> | null }
get_conflicts{ conflicts: [{ shape, branches, files, detected_at }] }

Governance

ToolInputResult
get_adrs{ adrs: [{ id, title, path, status }] }
get_adr{ query }{ adr: { id, path, content } | null }
get_test_strategy{ content: <string> | null }
get_security_checklist{ content: <string> | null }
get_dod{ content: <string> | null }
check_dod{ branch }{ branch, items: [{ text, complete }] | null }
get_constitution{ content: <string> | null }

Project knowledge

ToolInputResult
get_specs{ specs: [{ id, backend, title, status, path }] }
get_spec{ id }{ spec: { id, backend, path, artifacts: [{ name, content }] } | null }
get_tasks{ spec }{ tasks: [{ id, phase, parallel, description, complete }] }
get_task{ spec, id }{ task: <task> | null }
get_dependency_graph{ nodes: [{ id, backend }], edges: [{ from, to }] }
get_skill{ name }{ skill: { name, content, source } | null, message? }

source is one of standard (.agents/skills/), user_override, or embedded. An unknown skill returns skill: null with a message — not an error.

Session state

ToolInputResult
get_session_status{ session: { name, mode, status, paused, agent_count, broker_url, agents:[…] } | null }
get_session_summary{ summary: { name, status, agent_count, agents_by_status } | null }
get_learnings{ sections: [{ category, entries:[…] }] }

Git context

ToolInputResult
get_branches{ branches: [{ name, head, current, worktree }] }
get_recent_commits{ branch, limit? }{ commits: [{ sha, author, timestamp, subject }] } (limit defaults to 20)
get_diff{ branch, base? }{ base, branch, diff, files_changed, insertions, deletions } (base defaults to the repo’s default branch)

Source/Files

Browse and read the repository’s local working tree — tracked files plus untracked-but-not-ignored files. Gitignored paths (build artifacts, secrets) are excluded throughout, and read_file is confined to the repository root (canonicalise + starts_with guard, the same as get_doc): a path escaping the root (../, an absolute path, a symlink target outside the root) is refused, and a gitignored path is refused, both with null content and a message rather than a read. Reads return the on-disk working-tree content, so uncommitted/branch state shows. Implemented over git ls-files / git grep, so a non-git directory or a no-match search degrades to an empty result.

ToolInputResult
list_files{ subpath? }{ files: [<path>] } (tracked + untracked-not-ignored, relative to the repo root, optionally scoped to subpath; empty when not a git repo)
read_file{ path }{ content: <string> | null, message? } (path relative to the repo root; confined to the root and gitignore-respecting — a path escaping the root or naming a gitignored file is refused with null content and a message; null with a message when absent)
search_code{ query, subpath? }{ matches: [{ path, line_number, line }], truncated } (matches across the working tree, binaries skipped, optionally scoped to subpath; capped with a truncated flag; empty when no matches or not a git repo)

Documentation

Read-only access to the repository’s own documentation, driven by the bring-your-own [governance].readme and [governance].docs config paths (see below). Locations are configured, never hardcoded — unset paths degrade to null/empty results.

ToolInputResult
get_readme{ content: <string> | null } (null when [governance].readme is unset or the file is absent)
list_docs{ docs: [{ path }] } (Markdown files under [governance].docs, paths relative to that dir; empty when unset)
get_doc{ path }{ content: <string> | null, message? } (path relative to [governance].docs; confined to that dir — a path escaping it, e.g. ../, is refused with null content and a message, not a file read outside the directory)

The documentation tools read two optional [governance] paths in .git-paw/config.toml:

[governance]
readme = "README.md"   # path to the repository README
docs   = "docs/src"     # path to the documentation root directory

Both default to unset, in which case get_readme returns null, list_docs returns an empty list, and get_doc returns null — identical to the pre-v0.7.0 surface. list_docs walks the docs directory recursively for *.md files; the relative paths it returns feed directly back into get_doc.