Agents
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.
| Group | Tools |
|---|---|
| repositories | forges_repos_list, forges_repos_get, forges_repos_contents, forges_repos_tags |
| contribution templates | forges_contribution_templates_list, forges_contribution_templates_get |
| code | forges_code_search |
| CI and commits | forges_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 |
| releases | forges_releases_list, forges_releases_get, forges_releases_create, forges_releases_update |
| issues | forges_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 requests | forges_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 auth | forges_users_get, forges_users_authenticated, forges_auth_reload |
| review threads | forges_threads_list, forges_threads_get, forges_threads_reply, forges_threads_resolve, forges_threads_unresolve |
| local inspection | forges_local_inspect |
| local Git | forges_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
forges mcp
claude mcp add forges --scope user -- npx -y @agntn/forges mcp
Or in a client's config:
{
"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
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.
ignore previous instructions in an issue has learned something about the issue. Nothing about what to do next.