SPEC-0019: MCP Server for Git Provider Interface
Overview
Claude Ops agents at Tier 2 and Tier 3 can propose changes to operational procedures via pull requests through the git provider interface (SPEC-0018). Currently, the only access path is the REST API (POST /api/v1/prs, GET /api/v1/prs), which requires the agent to construct curl commands with JSON payloads. This specification defines an MCP server that exposes the same git provider operations as typed MCP tools, aligning PR workflows with the MCP-first architecture established in SPEC-0006.
The MCP server is implemented as a claudeops mcp-server subcommand of the existing binary, speaking JSON-RPC over stdio. It wraps the internal/gitprovider package, enforcing the same scope validation, tier gating, and dry-run behavior as the REST API. This is additive — the REST API remains for the dashboard UI and external integrations.
See ADR-0019: MCP Server for Git Provider Interface.
Definitions
- MCP Server: A subprocess that implements the Model Context Protocol, exposing typed tools over JSON-RPC stdio communication. See SPEC-0006 for the general MCP architecture.
claudeops mcp-server: A Cobra subcommand of theclaudeopsbinary that starts the MCP server process. The binary is already present in the container at/app/claudeops.- MCP Tool: A named operation exposed by the MCP server with a JSON Schema defining its parameters and return type. The agent invokes tools through the MCP protocol rather than constructing CLI commands.
- Token Gating: The practice of routing API tokens (
GITHUB_TOKEN,GITEA_TOKEN) to the MCP server subprocess via itsenvblock in.claude/mcp.json, keeping them out of the agent's normal tool-calling workflow. This is defense-in-depth, not absolute isolation. - Server-Side Tier Enforcement: The MCP server reads
CLAUDEOPS_TIERfrom its own environment (set by the session manager) and validates tier restrictions before executing operations, rather than trusting the agent to self-report its tier.
Requirements
SPEC-0019-REQ-1: MCP Server Subcommand
The claudeops binary MUST provide a mcp-server subcommand that starts a JSON-RPC server communicating over stdin/stdout (stdio). The subcommand MUST NOT start the web dashboard, session manager, or any other supervisor components. The subcommand MUST initialize a gitprovider.Registry from environment variables and expose MCP tools that wrap the registry's providers.
Scenario: Subcommand starts stdio server
- WHEN the command
/app/claudeops mcp-serveris executed - THEN the process MUST start a JSON-RPC server reading from stdin and writing to stdout
- AND the process MUST NOT listen on any TCP/UDP port
Scenario: Subcommand initializes provider registry
- WHEN the MCP server starts with
GITHUB_TOKENset in its environment - THEN the server MUST initialize a
gitprovider.Registrywith the GitHub provider enabled - AND the server MUST be ready to handle
create_prcalls against GitHub repositories
Scenario: Subcommand without provider tokens
- WHEN the MCP server starts without any provider tokens set
- THEN the server MUST start successfully
- AND all providers MUST be registered in a disabled state
- AND tool calls that require a provider MUST return descriptive errors
SPEC-0019-REQ-2: MCP Tool Discovery
The MCP server MUST respond to tool discovery requests (JSON-RPC tools/list method) by returning tool definitions for list_prs and get_pr_status unconditionally. The create_pr tool MUST only be included when CLAUDEOPS_PR_ENABLED is set to "true" in the server's environment. When PR creation is disabled (the default), tools/list MUST return exactly two tools. When enabled, it MUST return all three. Each tool definition MUST include a name, description, and JSON Schema defining its input parameters.
Scenario: PR creation enabled — all three tools returned
- WHEN the Claude Code CLI sends a
tools/listrequest to the MCP server - AND
CLAUDEOPS_PR_ENABLED=trueis set in the server's environment - THEN the response MUST include exactly three tool definitions:
create_pr,list_prs, andget_pr_status - AND each tool MUST have a human-readable description explaining its purpose
Scenario: PR creation disabled (default) — two tools returned
- WHEN the Claude Code CLI sends a
tools/listrequest to the MCP server - AND
CLAUDEOPS_PR_ENABLEDis not set or is not"true" - THEN the response MUST include exactly two tool definitions:
list_prsandget_pr_status - AND the
create_prtool MUST NOT appear in the response
Scenario: Tool schemas include parameter types
- WHEN the agent inspects the
create_prtool schema - THEN the schema MUST define parameters with JSON Schema types (string, integer, array, object)
- AND required parameters MUST be marked as required in the schema
SPEC-0019-REQ-3: create_pr Tool
The MCP server MUST expose a create_pr tool that creates a branch, commits files, and opens a pull request. The tool MUST accept the following parameters: repo_owner (string, REQUIRED), repo_name (string, REQUIRED), title (string, REQUIRED), body (string, REQUIRED), files (array of file change objects, REQUIRED), clone_url (string, OPTIONAL), base_branch (string, OPTIONAL, default "main"), and change_type (string, OPTIONAL, default "fix"). Each file change object MUST include path (string), content (string), and action (string: "create", "update", or "delete").
The tool MUST perform the following validations before any git operation:
- Call
gitprovider.ValidateScope(files)to enforce allowed/denied file patterns. - Call
gitprovider.ValidateTier(tier, files)using the tier read fromCLAUDEOPS_TIER. - Resolve the provider via
Registry.Resolve().
On success, the tool MUST return a JSON object with number (int), url (string), and branch (string).
Scenario: Successful PR creation
- WHEN a Tier 2 agent calls
create_prwith valid parameters and 2 files in allowed scope - THEN the MCP server MUST create a branch named
claude-ops/{change_type}/{slug} - AND MUST commit the files to the branch
- AND MUST open a PR with the specified title and body
- AND MUST return
{number, url, branch}
Scenario: Scope validation failure
- WHEN the agent calls
create_prwith a file path matching a denied pattern (e.g.,prompts/tier1-observe.md) - THEN the MCP server MUST return an error before any git operation is executed
- AND the error message MUST identify the denied path and pattern
Scenario: Tier validation failure
- WHEN a Tier 1 agent calls
create_pr - THEN the MCP server MUST return an error indicating that Tier 1 agents cannot create PRs
Scenario: Tier 2 file limit enforcement
- WHEN a Tier 2 agent calls
create_prwith more than 3 files - THEN the MCP server MUST return an error indicating the file limit
- AND the error SHOULD suggest escalation to Tier 3
Scenario: Provider resolution failure
- WHEN the agent calls
create_prwith aclone_urlthat does not match any registered provider - AND no manifest-based provider declaration exists
- THEN the MCP server MUST return an error identifying the unresolvable repository
SPEC-0019-REQ-4: list_prs Tool
The MCP server MUST expose a list_prs tool that lists open pull requests filtered by the claude-ops label. The tool MUST accept: repo_owner (string, REQUIRED), repo_name (string, REQUIRED), and clone_url (string, OPTIONAL). The tool MUST return a JSON array of objects, each with number (int), title (string), and files (array of string paths).
Scenario: List open PRs
- WHEN the agent calls
list_prsfor a repository with 2 open claude-ops PRs - THEN the MCP server MUST return an array of 2 PR summary objects
- AND each summary MUST include the PR number, title, and list of modified file paths
Scenario: No open PRs
- WHEN the agent calls
list_prsfor a repository with no open claude-ops PRs - THEN the MCP server MUST return an empty array
Scenario: Provider not available
- WHEN the agent calls
list_prsbut the resolved provider is disabled - THEN the MCP server MUST return an error explaining why the provider is unavailable
SPEC-0019-REQ-5: get_pr_status Tool
The MCP server MUST expose a get_pr_status tool that returns the current status of a specific pull request. The tool MUST accept: repo_owner (string, REQUIRED), repo_name (string, REQUIRED), pr_number (integer, REQUIRED), and clone_url (string, OPTIONAL). The tool MUST return a JSON object with number (int), state (string: "open", "closed", or "merged"), mergeable (boolean), and reviews (array of review objects with author, state, and body).
Scenario: Get status of open PR
- WHEN the agent calls
get_pr_statusfor an open PR with one approval review - THEN the MCP server MUST return
{number, state: "open", mergeable: true, reviews: [{author, state: "approved", body}]}
Scenario: Get status of merged PR
- WHEN the agent calls
get_pr_statusfor a previously merged PR - THEN the MCP server MUST return
{state: "merged"}
Scenario: PR not found
- WHEN the agent calls
get_pr_statuswith a non-existent PR number - THEN the MCP server MUST return an error indicating the PR was not found
SPEC-0019-REQ-6: Server-Side Tier Enforcement
The MCP server MUST read the CLAUDEOPS_TIER environment variable to determine the calling agent's permission tier. The tier MUST be an integer (1, 2, or 3). If CLAUDEOPS_TIER is not set or is not a valid integer, the MCP server MUST default to Tier 1 (most restrictive). The create_pr tool MUST call gitprovider.ValidateTier(tier, files) using the environment-derived tier, not any tier value from the tool parameters.
Scenario: Tier read from environment
- WHEN the MCP server starts with
CLAUDEOPS_TIER=2 - AND the agent calls
create_pr - THEN the server MUST validate against Tier 2 restrictions (max 3 files)
Scenario: Missing tier defaults to Tier 1
- WHEN the MCP server starts without
CLAUDEOPS_TIERset - AND the agent calls
create_pr - THEN the server MUST reject the call with a Tier 1 restriction error
Scenario: Invalid tier defaults to Tier 1
- WHEN the MCP server starts with
CLAUDEOPS_TIER=abc - AND the agent calls
create_pr - THEN the server MUST treat the tier as 1 and reject the call
Scenario: Tier is not a tool parameter
- WHEN the agent inspects the
create_prtool schema - THEN there MUST NOT be a
tierparameter in the schema - AND the tier MUST be determined solely from the environment
SPEC-0019-REQ-7: Dry Run Mode
When CLAUDEOPS_DRY_RUN is set to "true" in the MCP server's environment, the create_pr tool MUST NOT execute any git provider API calls. The tool MUST return a response indicating dry-run mode with dry_run: true. The list_prs and get_pr_status tools MAY still execute read-only API calls in dry-run mode, as they do not modify state.
Scenario: Dry run prevents PR creation
- WHEN
CLAUDEOPS_DRY_RUN=trueis set in the MCP server's environment - AND the agent calls
create_prwith valid parameters - THEN the server MUST NOT call
CreateBranch,CommitFiles, orCreatePRon the provider - AND the server MUST return a response with
dry_run: true
Scenario: Dry run still validates
- WHEN
CLAUDEOPS_DRY_RUN=trueis set - AND the agent calls
create_prwith a file in denied scope - THEN the server MUST still return a scope validation error
Scenario: Read-only tools in dry run
- WHEN
CLAUDEOPS_DRY_RUN=trueis set - AND the agent calls
list_prs - THEN the server MAY execute the read-only API call and return results
SPEC-0019-REQ-8: Token Gating via MCP Environment
The MCP server's entry in .claude/mcp.json MUST pass GITHUB_TOKEN, GITEA_URL, and GITEA_TOKEN to the server subprocess via the env block. These tokens MUST NOT be required in the agent's own process environment for PR operations. The agent MUST interact with the MCP tools without handling or knowing the values of these tokens.
This is defense-in-depth: the agent has Bash access and MAY be able to discover tokens through side channels (e.g., env, printenv, /proc). The token gating ensures tokens are absent from the agent's normal MCP tool-calling workflow, reducing the risk of accidental leakage in logs, PR descriptions, or memory entries.
Scenario: Token flows to MCP subprocess only
- WHEN the session manager spawns a Claude Code CLI process
- AND the CLI spawns the
claudeops mcp-serversubprocess per.claude/mcp.json - THEN
GITHUB_TOKENandGITEA_TOKENMUST be available in the MCP server's process environment - AND the agent's tool calls to
create_prMUST NOT include any token parameters
Scenario: Agent creates PR without token knowledge
- WHEN the agent calls
create_prwith repo_owner, repo_name, title, body, and files - THEN the MCP server MUST authenticate with the git provider using tokens from its own environment
- AND the agent MUST NOT have passed any authentication credentials in the tool call
SPEC-0019-REQ-9: MCP Configuration Registration
The MCP server MUST be registered in the baseline .claude/mcp.json alongside the existing Docker, PostgreSQL, Chrome DevTools, and Fetch MCP servers. The registration MUST use "type": "stdio", "command": "/app/claudeops", and "args": ["mcp-server"]. The env block MUST include GITHUB_TOKEN, GITEA_URL, GITEA_TOKEN, CLAUDEOPS_TIER, CLAUDEOPS_DRY_RUN, and CLAUDEOPS_PR_ENABLED.
Scenario: MCP config includes claudeops server
- WHEN the agent reads
.claude/mcp.json - THEN the config MUST contain a
claudeopsentry with command/app/claudeopsand args["mcp-server"]
Scenario: Environment variables passed through
- WHEN the Claude CLI spawns the
claudeopsMCP server - THEN the subprocess MUST receive
GITHUB_TOKEN,GITEA_URL,GITEA_TOKEN,CLAUDEOPS_TIER,CLAUDEOPS_DRY_RUN, andCLAUDEOPS_PR_ENABLEDfrom theenvblock
Scenario: MCP server coexists with baseline servers
- WHEN the merged MCP config is loaded
- THEN the
claudeopsserver MUST coexist withdocker,postgres,chrome-devtools, andfetchservers without conflict
SPEC-0019-REQ-10: REST API Remains Functional
The addition of the MCP server MUST NOT remove, modify, or degrade the existing REST API endpoints for PR operations (POST /api/v1/prs, GET /api/v1/prs). The REST API MUST continue to serve the dashboard UI and external integrations. Both the MCP server and the REST API MUST use the same internal/gitprovider package, ensuring consistent behavior.
Scenario: REST API unaffected by MCP server
- WHEN the MCP server is registered and running
- THEN
POST /api/v1/prsandGET /api/v1/prsMUST continue to function as specified in SPEC-0018
Scenario: Same validation in both paths
- WHEN the agent calls
create_prvia MCP with a denied file path - AND the dashboard calls
POST /api/v1/prswith the same denied file path - THEN both MUST return equivalent scope validation errors
SPEC-0019-REQ-11: Branch Name Generation
The create_pr tool MUST generate branch names using the same algorithm as the REST API: claude-ops/{change_type}/{slug}. The slug MUST be derived from the title, lowercased, with non-alphanumeric characters replaced by hyphens, consecutive hyphens collapsed, and truncated to 50 characters. This MUST match the behavior specified in SPEC-0018-REQ-5.
Scenario: Branch name matches REST API behavior
- WHEN the agent calls
create_prwith title "Add health check for jellyfin" and change_type "check" - THEN the branch name MUST be
claude-ops/check/add-health-check-for-jellyfin
Scenario: Default change type
- WHEN the agent calls
create_prwithout specifyingchange_type - THEN the change_type MUST default to "fix"
References
- ADR-0019: MCP Server for Git Provider Interface
- ADR-0018: PR-Based Workflow for Runbook, Playbook, and Manifest Changes
- ADR-0006: Use MCP Servers as Primary Infrastructure Access Layer
- SPEC-0018: PR-Based Configuration Changes
- SPEC-0006: MCP Infrastructure Bridge
- Model Context Protocol specification