Guide

Agents

The same 55 tools over MCP, Pi, OMP and the CLI, what they hold back, and how tokens and hosts stay out of a model's hands.

55 tools, four surfaces

Every tool is written once, in src/tools.ts. @agntn/tools hands that one definition to the MCP server, both extensions and the forges CLI. Same schema, same description, same error text. Fix it once, fixed everywhere.

GroupTools
repositoriesforges_repos_list, forges_repos_get, forges_repos_contents, forges_repos_tags
contribution templatesforges_contribution_templates_list, forges_contribution_templates_get
codeforges_code_search
CI and commitsforges_ci_runs_list, forges_ci_jobs_list, forges_ci_jobs_log, forges_commits_search, forges_commits_list, forges_commits_compare, forges_commits_get, forges_commits_checks, forges_commits_patch
releasesforges_releases_list, forges_releases_get, forges_releases_create, forges_releases_update
issuesforges_issues_list, forges_issues_search, forges_issues_search_global, forges_issues_get, forges_issues_comments, forges_issues_comments_get, forges_issues_reactions, forges_issues_comments_create, forges_issues_create, forges_issues_update
pull requestsforges_pull_requests_list, forges_pull_requests_search, forges_pull_requests_search_global, forges_pull_requests_get, forges_pull_requests_files, forges_pull_requests_checks, forges_pull_requests_reviews, forges_pull_requests_reviews_get, forges_pull_requests_comments, forges_pull_requests_comments_get, forges_pull_requests_reactions, forges_pull_requests_comments_create, forges_pull_requests_create, forges_pull_requests_update, forges_pull_requests_merge
users and authforges_users_get, forges_users_authenticated, forges_auth_reload
review threadsforges_threads_list, forges_threads_get, forges_threads_reply, forges_threads_resolve, forges_threads_unresolve
local inspectionforges_local_inspect
local Gitforges_local_merge_verify

Two shortcuts, because models kept tripping over the long form. platform defaults to github. And repo takes the slug everyone writes anyway, { "repo": "agntn/keys" }. An owner next to it may repeat the slug's owner. Naming someone else fails. No guessing which one you meant.

Cloudflare Artifacts? Git without the forge around it. So only the five Git reads take platform: "artifacts", and the rest don't even list it. The Artifacts page has the details.

Checks that finish

Is CI green yet? Ask forges_pull_requests_checks again and again, and it says "still running" again and again. Pass waitSeconds instead, up to 300. The call comes back once every check concludes, or when the budget runs out. A timeout isn't an error. You still get the page, plus a wait field with what's left.

Every page of checks counts. A green first page with page two still building isn't done.

Right after a push there's a catch. For a moment the pull request can still point at the old head, and that head's checks are all done. Green, settled and wrong. So pass the SHA you just pushed as headSha. Until the pull request catches up, you get no rows, a staleHead naming the commit it still shows, and the wait keeps going.

Local Git

Two tools read a checkout instead of a forge. No token, no fetch, no writes.

forges_local_inspect gives status, tracked files and the last few commit messages in one call. Point paths at *AGENTS.md and it finds every instruction file. A big repository comes back in pages.

forges_local_merge_verify asks something narrower. Is the merge commit on the target? Did these paths land exactly as they were on the PR head? Fetch the target first, then pass the head, the merge commit and the paths.

A match isn't a license to clean up, though. Paths you didn't list weren't checked, and refs move. Look at the live PR before deleting anything. The library has both as inspectLocal and verifyLocalMerge in @agntn/forges/local.

MCP

shell
forges mcp
claude mcp add forges --scope user -- npx -y @agntn/forges mcp

Or in a client's config:

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

That's stdio. A host with its own transport takes createMcpServer() from @agntn/forges/mcp.

Issue and pull request lists and searches leave bodies out and name the tool that reads one in full. A page of busy issues with every body inside would push the conversation that asked for it out of the window. Two small calls beat one huge one. Comments, reviews and threads keep their bodies, cut to twelve lines and 4,000 characters each, so a heavily reviewed PR wants a small perPage.

Reads, writes and whose account

Reads go through the usual token chain. Nothing found? They go out anonymous. That anonymous provider never gets reused for a write, not even by accident. Artifacts has no guest mode, so no token there means a clear error.

Twelve tools write, and their MCP annotations say so. Run a create or a reply twice and you get two of them. Updates and the merge are marked destructive. A merge doesn't come back.

Before a model writes anything, forges_users_authenticated tells you whose account it'll use. Two GitHub logins in gh? A write can pick one with account. forges then asks /user whose token that really is and refuses on a mismatch. That check is the point. A GH_TOKEN in the environment still wins the lookup, and a reply signed by the wrong person looks exactly like a success. GitLab, Gitea and Artifacts don't take account.

The credential stays pinned until forges_auth_reload swaps it. Even an expired one. So a rejected token comes back with where it came from and which login to redo. Then the reload. A bare 401 tells a model nothing.

Hosts stay local

A self hosted forge comes from FORGES_GITHUB_BASE_URL, FORGES_GITLAB_BASE_URL or FORGES_GITEA_BASE_URL in the agent's environment. FORGES_ARTIFACTS_BASE_URL does the same for the Cloudflare API. Never a tool argument, so a model can't aim a call somewhere else. Errors name the status and, on a rate limit, when to retry. Never the endpoint.

A failed call is a sentence for the model. The session survives.

Pi and OMP

shell
pi install npm:@agntn/forges

Both extensions draw calls and results in the terminal. A repository row, pull requests with their states, a thread with its comments. Pi stops before six writes: creating, editing or merging a pull request, editing an issue, creating or editing a release. It shows what goes out and waits for a yes. No UI to ask? No write. OMP puts its own approval in front of every write.

What comes back

Normalized objects, not prose. Want to check a model's summary against the source? The Explorer shows the same objects for any public repository.

Issue bodies, comments and review threads come from whoever has an account. Data to report, not instructions to follow. An agent that reads ignore previous instructions in an issue has learned something about the issue. Nothing about what to do next.