@calltelemetry/pr-manager-mcp
v0.7.4
Published
Pull Request Management MCP Server for GitHub PRs, CI logs, review threads, and Linear policy validation
Readme
@calltelemetry/pr-manager-mcp
A Model Context Protocol (MCP) server for inspecting, accelerating, triaging, and managing GitHub Pull Requests, GitHub Actions CI workflows, review threads, test coverage, and Linear policy validation directly from AI coding agents.
Key Features
- GitHub Actions Acceleration & Triage:
triage_ci_failures: Fast root-cause classification (deterministic_test,infrastructure,timeout,permission,queue_stall) with high-signal excerpts and suggested actions.rerun_failed_jobs: Selectively retries only failing jobs without restarting full suites or passing matrix jobs.cancel_superseded_runs: Automatically frees runner slots by canceling obsolete runs on older commits.enable_auto_merge: Enables GitHub auto-merge (SQUASH/MERGE/REBASE) via GraphQL. On CLEAN no-queue PRs (UNPROCESSABLE: Pull request is in clean status), falls through to an exact-headmergePullRequest.merge_pull_request: Direct protected squash/merge/rebase atexpectedHeadShafor CLEAN PRs that cannot use auto-merge.get_ci_snapshot: Single-call unified health snapshot (active runs, queued runs, stalled queues, blocker breakdown).
- PR Status & Bounded Mergeability: Fetch complete PR metadata, status checks, and handles
mergeable: nullvia automatic bounded polling. - Repository Visibility Guard: Read the live GitHub repository visibility (
public,private, orinternal) before making public/private-surface decisions. - CI Job Status & Step Logs: Query check runs, check suites, and download job execution logs with safe redirect handling and credential redaction.
- PR State Monitor / Wait: Efficiently wait/poll for PR state transitions (
checks_passed,checks_completed,clean_mergeable,bot_review_completed,merged,closed) without busy loops. - Top-Level PR Comments: Post issue comments to trigger bot reviews (e.g.
@cursor-agent review) or attach CI evidence summaries. - Test Coverage Aggregation: Extract test coverage from CI check runs, bot comments (e.g. Codecov), or local
coverage-summary.jsonfiles. - Draft Creation & Promotion Guard: Open draft PRs, run comprehensive prechecks (Linear policy, Customer Data / PII scrub, PR template completeness), and promote draft PRs to "Ready for Review" via GraphQL.
- Canonical Compliance Allowlist: The Customer Data / PII scrub honors the same line-scoped
ct-compliance:allowmarker as the canonicalct-compliance/scripts/scan.sh. Put that literal anywhere on a line that legitimately must contain an otherwise-flagged shape — a required NetworkPolicyipBlock.cidr, or a guard-rail test asserting a token's absence — and the line is counted as allowlisted (surfaced assummary.allowlistedCount) instead of reported as a finding. Marker content is never echoed, because CI logs are readable by anyone with repo access; audit the set withgit grep ct-compliance:allow. The exemption is line-scoped only: it never excuses a different line, and never a filename — a customer-named file must still be renamed. - Pinned Public Upstream Classification: Live precheck can classify one Docker bridge occurrence in the complete newly added public DocsGPT
images/docsgpt/context/overlay/docsgpt/app.pysnapshot, only incalltelemetry/ct-infrastructureand only at SHA-256d6c77b31bc3a7ee378c8692af4b4d962ace01d36785a4809a8f7a303f9c7db7a. The exact source digest, development redirect statement and occurrence position are bound after strict full-file diff reconstruction. Unknown or altered snapshots, paths or repositories; modified/renamed/partial/malformed diff sections; and manual caller claims receive ordinary scanning. This classification affects only that private-IP occurrence, reportssummary.classifiedPublicUpstreamCount, and does not change marker counts or other detectors. The public test fixture uses deterministic gzip/base64 transport; native tests verify both compressed and decoded source digests. It supplies no runtime exemption. - Review Thread Lifecycle: List GraphQL review threads, reply to inline review comments, resolve/unresolve threads, and request bot (Bugbot) or human reviewers.
- Optimistic Concurrency Guards: Write operations (
reply_review_thread,resolve_review_thread,rerun_failed_jobs,enable_auto_merge) supportexpectedHeadShavalidation to prevent acting on stale PR revisions. - Embedded Linear Policy Guard: 100% test-verified parity with Call Telemetry Linear policy rules (
scripts/workflow-policy/linear-workflow-guard.mjs) without needing external CI execution. - Multi-tiered Auth Precedence: Configured token $\rightarrow$
GITHUB_TOKEN$\rightarrow$ localgh auth tokenviaghCLI.
Complete Registered MCP Tool Suite (25 Tools)
| Tool | Category | Description | Key Parameters |
|---|---|---|---|
| diagnose_github_access | Inspection | Read-only account and target-repository access metadata, one pinned credential, explicit unknown write authority | owner, repo, expectedAccount |
| get_pr_details | Inspection | Query PR metadata, body, branch info, mergeable state, review decision | owner, repo, pullNumber, pollTimeoutMs |
| get_repo_visibility | Inspection | Read-only query of repository visibility and private flag | owner, repo |
| get_pr_checks | Inspection | Query check-runs, check-suites, and commit statuses for head SHA | owner, repo, pullNumber or ref |
| get_job_logs | Inspection | Retrieve CI job execution logs with safe redirect handling and truncation | owner, repo, jobId, tailLines, maxBytes |
| get_pr_coverage | Inspection | Extract line, branch, statement coverage from check runs, PR comments, or local reports | owner, repo, pullNumber, ref, localReportPath, thresholdPercent |
| get_ci_snapshot | CI Acceleration | Unified high-signal CI health snapshot and blocker breakdown | owner, repo, pullNumber, includeTriage |
| wait_for_pr_state | Monitoring | Efficiently wait/poll until a target PR state condition is satisfied | owner, repo, pullNumber, targetState, timeoutMs, intervalMs |
| start_pr_wait_job | Monitoring | Start the same wait as a background job and return a jobId immediately (use for targets that may exceed ~45s; avoids gateway timeouts) | owner, repo, pullNumber, targetState, timeoutMs (max 6h), intervalMs |
| get_pr_wait_job | Monitoring | Poll a background wait job: running → elapsed time, finished → full monitor result | jobId |
| cancel_pr_wait_job | Monitoring | Cancel a running background wait job | jobId |
| triage_ci_failures | CI Acceleration | Parse failed CI steps and logs to classify root causes with suggested actions | owner, repo, pullNumber or runId, maxJobs |
| rerun_failed_jobs | CI Acceleration | Selectively rerun only failed jobs in a workflow run | owner, repo, runId, pullNumber, expectedHeadSha |
| cancel_workflow_run | CI Acceleration | Cancel a specific running or queued workflow run | owner, repo, runId |
| cancel_superseded_runs | CI Acceleration | Automatically cancel obsolete runs on older commits for a PR branch | owner, repo, branch, currentHeadSha |
| trigger_workflow_dispatch | CI Acceleration | Trigger custom GitHub Actions workflow dispatches | owner, repo, workflowId, ref, inputs |
| enable_auto_merge | CI Acceleration | Enable auto-merge; CLEAN no-queue PRs fall through to exact-head merge | owner, repo, pullNumber, mergeMethod, expectedHeadSha |
| merge_pull_request | Landing | Exact-head GraphQL merge for CLEAN PRs (no merge queue, no admin merge) | owner, repo, pullNumber, expectedHeadSha, mergeMethod |
| disable_auto_merge | CI Acceleration | Disable auto-merge on a pull request | owner, repo, pullNumber |
| list_review_threads | Reviews | Query GraphQL review comment threads with resolution status | owner, repo, pullNumber, includeResolved |
| reply_review_thread | Reviews | Reply to an existing review thread with expectedHeadSha guard | owner, repo, pullNumber, threadId, body, expectedHeadSha |
| resolve_review_thread | Reviews | Resolve or unresolve a review thread with expectedHeadSha guard | threadId, resolve, expectedHeadSha |
| request_reviewers | Reviews | Request reviews from users, teams, or bot accounts | owner, repo, pullNumber, reviewers, teamReviewers, botReviewers |
| add_pr_comment | Comments | Post top-level comments on a PR to trigger bot reviews or post evidence | owner, repo, pullNumber, body |
| create_draft_pr | Lifecycle | Open a new pull request on GitHub (defaults to draft mode) | owner, repo, title, head, base, body, draft |
| precheck_pr | Lifecycle | Run pre-promotion validation (Linear policy, Customer data/PII scrub, template evidence) | owner, repo, pullNumber (or raw branch/title/body) |
| promote_pr_to_ready | Lifecycle | Promote draft PR to Ready for Review via GraphQL with gated prechecks | owner, repo, pullNumber, force, overrideReason |
| check_linear_policy | Policy | Validate PR branch name, title, and body against Linear policy | sourceBranch, targetBranch, prTitle, prBody |
Scoped GitHub workspace tools
The Bifrost-federated pr-manager can expose three explicit repository tools:
| Tool | Operation | Guard |
|---|---|---|
| get_repo_file | Read a UTF-8 file and blob SHA | 128 KiB maximum; selected server-side credential |
| create_work_branch | Create a new codex/ branch | Exact current base commit SHA; no ref update |
| put_work_file | Commit one UTF-8 file on a codex/ branch | Exact parent and blob SHA; non-forced ref advance rejects concurrent changes; 128 KiB maximum |
These tools are absent by default. Enable registration only with PR_MANAGER_GITHUB_WORKSPACE_TOOLS=enabled after narrowing wildcard pr_manager grants in the Bifrost roster and validating the intended per-key ACLs. They do not merge, change protection, install an App, or choose a different account after a permission failure. If a ref update acknowledgement is lost, the tool reads the branch once: it returns success only when the proposed commit is observed, otherwise UNKNOWN_GITHUB_EFFECT requires inspection before retry. GitHub still enforces the installation's repository and Contents permissions. A 403 Resource not accessible by integration requires an authorized installation or a separate authorized PR route. Register only the intended tool names for each Bifrost virtual key via the existing roster writer; source registration alone does not grant access.
Read-only access diagnostic
Run pr-manager-mcp auth-doctor <owner> <repo> [expected-account], or use the
equivalent diagnose_github_access MCP tool. Both use the existing token resolver
and pin its selection for the operation. Use the same process-scoped credential
environment as the intended Git operation; no account or global Git configuration
is changed. The source category describes the resolver input, not an inferred
upstream broker identity.
Only two bounded GET probes run: authenticated user metadata and the exact target repository. Output preserves 401, 403, hidden-or-missing 404, malformed response, rate-limit and transport outcomes without returning response bodies or secrets. An installation token may not identify a user; account/App identity then remains unknown. Repository role is not proof of the token's effective write scope (fine-grained tokens can be more restrictive). No write is attempted to test it. CLI exit 0 means target metadata is readable and any requested account matches; it does not authorize a push, merge, or privilege change.
See GitHub authentication permissions.
Setup & Configuration
Transports
The containerized server (dist/sse.js, the default CMD in the Docker image)
exposes:
| Endpoint | Transport | Notes |
|---|---|---|
| POST /mcp | MCP Streamable HTTP (spec 2025-03-26) | Session-based; responses stream over SSE with 15s keep-alive frames, so a blocking tool call never lets the connection go idle long enough to trip an intermediary's inactivity timeout. Open with initialize, then send mcp-session-id on subsequent calls. |
| GET /sse + POST /messages?sessionId=… | Legacy HTTP+SSE | Emits : keepalive comment frames every 15s. |
| GET /healthz | Health probe | Returns the active transport set. |
| GET /metrics | Prometheus text (0.0.4) | Counters only — no tokens, request paths, or repository contents. Unauthenticated like /healthz; handled before the MCP session machinery so a scrape never reserves a session slot. |
Tunable via environment or server options: SSE_KEEPALIVE_MS (default 15000),
MAX_STREAMABLE_SESSIONS (default 100), STREAMABLE_SESSION_TTL_MS (default
30 min — idle Streamable HTTP sessions are reaped so abandoned clients cannot
exhaust the cap), and MAX_RUNNING_WAIT_JOBS (default 50 — bound on
concurrently polling background jobs).
Metrics
GET /metrics exposes counters in Prometheus text format, emitted by a
dependency-free registry (src/utils/metrics.ts): the surface is small enough
that prom-client would add weight to a deliberately lean image. Revisit if
histograms or exemplars are needed.
| Metric | Labels | Meaning |
|---|---|---|
| pr_manager_github_requests_total | method | GitHub API requests attempted. The denominator for the failure rate. |
| pr_manager_github_auth_failures_total | status | 401 = token expired, revoked or invalid. 403 = token lacks the required scope. Kept separate because the remediation differs. |
| pr_manager_token_resolution_failures_total | source | Failures to resolve a token at all, by resolution source. |
Counted after the 401 fallback, deliberately. A 401 that the fallback
recovers from is a healthy request; counting the first response would make a
rate alert fire on a working system. Only a surviving 401/403 is counted.
test/unit/auth-metrics.test.ts pins both directions.
Suggested alert: rate(pr_manager_github_auth_failures_total[10m]) > 0 for
10m. A sustained non-zero rate means the credential is stale, revoked, or
under-scoped — the silent failure mode where requests keep succeeding until a
short-lived token expires and only then start failing.
Async wait-job resource bounds
Background wait jobs are reachable by any client of the server, so the store defends itself:
- Concurrent running jobs are capped (
MAX_RUNNING_WAIT_JOBS); over the cap,start_pr_wait_jobis refused. - Job ids are random UUIDs — they are the only handle on a job, so a predictable id would let one client read or cancel another's.
cancel_pr_wait_jobaborts the underlying poll loop (AbortSignalthreaded into the monitor) and is sticky: a cancelled job never flips to satisfied/timeout when the in-flight poll settles.- Each wait binds its first observed head, or the supplied
expectedHeadSha. A head change ends the stage asstale_head; unreadable checks produceunconfirmedrather than reusing prior success. A satisfied condition is an observation, not review or merge approval. - To reuse one owned monitor, supply a private random UUID
monitorScopeIdand an exactexpectedHeadShatostart_pr_wait_job. The same scope/repository/PR/target/head reuses its running job. A different head supersedes only that scope's same stage. Treat the UUID as a capability: keep it within the caller/mission, never use a public issue name, and do not publish it. The server retains only its hash. Omitted or different scopes keep clients independent.
Sync vs async waits
wait_for_pr_state holds its HTTP request open for up to 5 minutes. Every
hop in front of the server bounds idle connection time (the ingress defaults
to timeout server 50s), so on a gatewayed deployment a long synchronous wait
can be cut with a 504 before the tool finishes — even though the transport
keeps the stream alive, an intermediary may still cap total connection time.
For targets that may take longer than ~45 seconds, prefer the async form:
start_pr_wait_job→ returnsjobIdin milliseconds.- Poll
get_pr_wait_jobwith thejobId(~5s interval suggested). - Optionally
cancel_pr_wait_job.
.mcp.json Registration
Add the server to your project's .mcp.json:
{
"mcpServers": {
"pr-manager": {
"type": "stdio",
"command": "node",
"args": [
"pr-manager-mcp/dist/index.js"
],
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}",
"CT_LINEAR_TEAM_PREFIXES": "${CT_LINEAR_TEAM_PREFIXES:-}"
}
}
}
}Building & Running Tests
cd pr-manager-mcp
npm install
npm run build
npm testLanding result truth and fail-closed CI reads
enable_auto_merge and pr_land make at most one enable attempt, then read current identity-bound state. The outcome is queued, merged, armed, refused, or unconfirmed. A null mutation autoMergeRequest is not proof of refusal: protected queue admission and immediate merge can both return it. Queue position/state are preserved when available. Unknown queue/auto-merge/thread metadata are explicit, never an absent queue or a ready-to-merge claim; inspect current state instead of automatically repeating a mutation.
CLEAN direct merge is permitted only after fresh unchanged PR head/base identity and complete successful effective branch-rule pagination prove no merge queue applies. A CLEAN error alone is not that proof. Missing, partial, contradictory, or unreadable state refuses the fallback.
Migration note: CI collection is fail-closed. Permission failures (including 403), 404, transient 5xx, malformed/incomplete pagination and exhausted collection budgets are errors, not empty successful check lists. Existing required-context/App-bound checks, exact-head, review-thread, Linear and customer-data gates still apply. Protected source merge, release, deployment, and live acceptance remain separate receipts.
