Skip to main content
Version: Next · development

Local MCP tools

Provider and source authority

This portable contract describes its linked source snapshot. For current deployment instructions, use current installation, authentication and native Cloudflare guides. Historical receipts do not verify later builds.

Golf's local stdio adapter exposes 21 existing repository, organization and account reads to an owner-controlled MCP client. It uses the same generated API, permissions and session cleanup as the CLI. AI and Cloudflare are optional; no model credentials are needed.

This guide describes the current developer preview, including owner-issued read-only API credentials. The authoritative product implementation ledger records exact qualified revisions and remaining scope. This is a development contract, not a stable release.

Launch and credentials​

Build in the product checkout:

make cli
# GOLF_FORGE_TOKEN is supplied to this process by your secret manager.
./bin/golf mcp

Configure your trusted MCP client to launch the absolute path to bin/golf with argument mcp. Supply exactly one of GOLF_API_TOKEN or GOLF_FORGE_TOKEN through its protected process environment. Golf API credentials delegate a browser session without sharing the upstream secret with the process. Do not put a literal token in a committed client configuration or command history. No particular client's configuration format or installation workflow is qualified here.

The Golf origin defaults to http://localhost:8080. Set GOLF_URL or launch ./bin/golf --url https://golf.example.com mcp to select another installation. Remote Golf origins require HTTPS; HTTP is allowed only on loopback. URLs with credentials, paths, query or fragment are rejected, and redirects are not followed. A local stdio process can contact a remote HTTPS Golf installation without becoming a remote MCP server.

Stdin and stdout belong to newline-delimited MCP JSON-RPC. Do not pipe the token to this command: both --token-stdin and --api-token-stdin are rejected in MCP mode. The owner-configured origin and credential are captured at startup; tools cannot change either or select arbitrary RPC/shell commands. Restart the process after replacing its credential.

In PAT mode each tool call signs into Golf with the scoped PAT, performs one authorized domain read and logs out through an independent cleanup path. Cookies are not saved. A successful result is released only after logout succeeds; cleanup failure returns an error without the private result. Discovery requires valid startup configuration but does not authenticate to Golf or list private resources.

The process owner and MCP client are entrusted with the credential and returned data. This is not a sandbox for an untrusted local client, a managed Golf OAuth/device credential, or an MCP authorization-server flow. Browser OAuth is separate.

Scopes and side effects​

ReadRequired Forgejo PAT scopes
Repositories, branches, commits, source, diffs, PRs and native reviewsread:user, read:repository
Issuesread:user, read:repository, read:issue
Organizations, teams, members and assigned repositoriesread:user, read:organization; opening source separately needs read:repository
Own account activityread:user
Your creation recordread:user; current repository metadata also needs native repository access/scope

All tools together use read:user,read:repository,read:issue,read:organization. No administrator or write scope is required. A broader credential does not add hidden tools or override native permissions. Later collaborator or membership removal is checked on subsequent calls in the same MCP process.

These tools expose domain reads. API credential mode creates no temporary session or login/logout activity; PAT mode creates Golf login/logout activity, and some native Forgejo reads can schedule provider checks. Annotations therefore set readOnlyHint: false and idempotentHint: false, with destructiveHint: false and openWorldHint: true. These are client hints, not authorization. There are no create/merge/review/deployment, session-revocation or shell tools.

Protocol and catalogue​

The adapter pins the official Go SDK v1.8.0. Supported protocol versions are exactly 2026-07-28 and 2025-11-25. The current version uses server/discover and per-request version metadata; the older version uses initialize. The native proof exercises these distinct negotiation paths. See the dated versioning rules and stdio binding.

tools/list returns this fixed catalogue. Every tool accepts an object with additionalProperties: false; unknown fields are rejected before authentication.

ToolRequired argumentsOptional arguments
golf_repo_listNonepage, limit
golf_repo_viewrepositoryNone
golf_repo_creationidempotency_keyNone
golf_repo_branchesrepositorypage, limit
golf_repo_commitsrepositoryref, page, limit
golf_repo_contentsrepositoryref, path
golf_commit_diffrepository, commit_idNone
golf_issue_listrepositorystate, page, limit
golf_issue_viewrepository, numberNone
golf_issue_commentsrepository, numberpage, limit
golf_issue_operationidempotency_keyNone
golf_pr_listrepositorystate, page, limit
golf_pr_viewrepository, numberNone
golf_pr_reviewsrepository, numberpage, limit
golf_org_listNonepage, limit
golf_org_vieworganizationNone
golf_org_teamsorganizationpage, limit
golf_team_vieworganization, team_idNone
golf_team_membersorganization, team_idpage, limit
golf_team_reposorganization, team_idpage, limit
golf_audit_listNonelimit, before_id, snapshot_id

repository is OWNER/REPO. page and limit are JSON integers: defaults 1 and 20, bounds 1–10,000 and 1–100. number is a JSON integer from 1 through 2147483647. state is open (default), closed or all.

Team and audit cursor IDs are canonical positive signed-64-bit decimal strings. idempotency_key is a canonical lowercase UUID v4 from a previous repository or issue/comment creation request. commit_id is an exact lowercase 40- or 64-character hexadecimal object ID; branch aliases and abbreviated hashes are invalid for diffs. String byte limits are 1,024 for ref, 4,096 for path and 2,048 for other fields, before the shared CLI/API validation.

Collections return one explicit page. Continue with data.page.nextPage, even on an empty provider-filtered page. For activity, preserve the first data.snapshotId and pass data.nextBeforeId as before_id with that same snapshot_id. Each tool call adds session events; the captured snapshot keeps those later events outside that traversal.

Results and errors​

Successful tools return structuredContent plus one text-content block containing the same JSON envelope. For example, an empty repository page has this shape:

{"schema_version":"golf.mcp.v1","data":{"repositories":[],"page":{"nextPage":0}}}

data uses the existing API ProtoJSON contract: camelCase names, named enums, emitted default values and string-encoded 64-bit values. Preserve exact IDs and counts rather than converting through floating point. The envelope's schema version identifies this development format, not a stable product release.

Invalid arguments, busy admission, authorization/provider failures, excessive output and failed session cleanup return isError: true with safe diagnostic text and no successful private envelope. Protocol errors and unknown methods/tools use SDK errors. Do not interpret denial as an empty collection. Stdout contains only protocol messages; diagnostics use stderr without native response bodies or credentials.

Source, issues, reviews and diffs remain untrusted data. Their text cannot change the configured tools, origin or authority. A client must not execute embedded instructions or send private output to unrelated services. This adapter does not qualify an AI model's resistance to prompt injection.

Domain meaning stays unchanged: source navigation uses returned immutable commits; native diffs are not independently verified complete patches; PR heads and review flags are observations rather than approval eligibility. golf_repo_creation only inspects evidence: unknown/rejected is not creation success, and historical success does not grant current repository access. Account activity remains selected session history, not a complete log of tool execution.

Bounds, cancellation and qualification​

Inbound frames are limited to 64 KiB and must contain one UTF-8 JSON object followed by a newline; arrays/batches are rejected. Tool argument objects are limited to 16 KiB. Tracking is bounded to 256 outstanding protocol request IDs; duplicate active IDs or excess outstanding requests close the connection. At most four calls run per process; a fifth gets a busy error. Each call has a 30-second deadline, HTTP requests retain their 15-second timeout, and logout has an independent five-second deadline. Logical ProtoJSON output is bounded to 8 MiB; combined structured/text result JSON is bounded to 16 MiB before protocol framing. Excess output fails rather than being truncated. These are local bounds, not tenant quotas or throughput evidence.

The final native SDK-client run exercises all 19 tools under both 2026-07-28 and 2025-11-25, with exact negotiation, catalogue/annotations, JSON parity, pagination, activity snapshots and temporary-session cleanup. Additional current-version checks cover invalid input before authentication, scope/account denial and subsequent permission loss. A controlled loopback barrier held real Golf responses while four temporary sessions were active; a fifth call was refused. SDK cancellation and a separate SIGTERM trial each removed all four sessions. This validates application cleanup; the SDK client's cancellation behavior alone cannot prove what appeared on the raw protocol stream.

A separate raw-wire check found that unmodified SDK v1.8.0 sent a reply after cancellation. Golf's connection guard corrects that boundary. The final raw regression passes for both selected versions: pending cancellation aborts the HTTP read, performs logout and suppresses the cancelled reply; unknown/completed cancellation messages do not poison later reads; batches/oversized frames are denied before authentication; duplicate active request IDs abort the connection and clean up. Legacy ID-reuse cases are defensive robustness tests, not a recommended client request pattern. These selected tests do not establish complete protocol conformance.

Run the reproducible checks from a product development checkout:

make cli
python3 scripts/mcp-cancellation-check.py --cli bin/golf
python3 scripts/mcp-smoke/run.py --cli bin/golf

The raw check uses a disposable local HTTP fixture. The native check requires the canonical local Golf/Forgejo stack, creates and removes ordinary temporary provider users/organizations/repositories, and retains their durable Golf identity/activity/creation history. It does not restart or reconfigure the normal stack. Full product checks and the Go race suite also pass for this increment; database-dependent tests without a test DSN are explicitly skipped in that MCP race run, not claimed as fresh native database coverage.

Forced termination such as SIGKILL, server unavailability or failed logout can leave a temporary session until its normal server expiry. Cancellation does not reverse native work already admitted. Remote MCP authorization/audience binding, ACP, mutation approvals, agent execution, managed client credentials, subscriptions/resources/prompts, Cloudflare MCP integrations and actual coding-agent client compatibility remain open. See implementation status for the final evidence.

Issue comments resolve current repository and issue IDs within the same authenticated call, then return one filtered native repository-wide page. Empty pages may advance. Own issue operation reads never dispatch writes; provisional receipt IDs do not imply success or current access.