Repository navigation
Add tool annotations to server-github (26 tools, 0 annotated) #3399
Description
Request: Add tool annotations — @modelcontextprotocol/server-github v0.6.2
Context
We ran an automated scan of this server via live JSON-RPC handshake. This server exposes 26 tools with zero annotations — the largest unannotated tool surface of any official MCP server.
Several of these tools perform irreversible operations on shared state (merge_pull_request, push_files), but provide no metadata to signal this. For reference, @modelcontextprotocol/server-filesystem annotates all 14 of its tools. We're requesting the same treatment here.
Current state — 0/26 tools annotated
Every tool returns empty annotations: {}.
Suggested annotations — per tool
Write / mutate tools (12 tools)
| # | Tool | readOnlyHint |
destructiveHint |
idempotentHint |
openWorldHint |
|---|---|---|---|---|---|
| 1 | create_or_update_file |
false |
false |
true |
true |
| 2 | create_repository |
false |
false |
false |
true |
| 3 | push_files |
false |
false |
false |
true |
| 4 | create_issue |
false |
false |
false |
true |
| 5 | create_pull_request |
false |
false |
false |
true |
| 6 | fork_repository |
false |
false |
true |
true |
| 7 | create_branch |
false |
false |
true |
true |
| 8 | update_issue |
false |
false |
true |
true |
| 9 | add_issue_comment |
false |
false |
false |
true |
| 10 | create_pull_request_review |
false |
false |
false |
true |
| 11 | merge_pull_request |
false |
true |
false |
true |
| 12 | update_pull_request_branch |
false |
false |
true |
true |
Read-only tools (14 tools)
| # | Tool | readOnlyHint |
destructiveHint |
idempotentHint |
openWorldHint |
|---|---|---|---|---|---|
| 13 | search_repositories |
true |
false |
true |
true |
| 14 | get_file_contents |
true |
false |
true |
true |
| 15 | list_commits |
true |
false |
true |
true |
| 16 | list_issues |
true |
false |
true |
true |
| 17 | search_code |
true |
false |
true |
true |
| 18 | search_issues |
true |
false |
true |
true |
| 19 | search_users |
true |
false |
true |
true |
| 20 | get_issue |
true |
false |
true |
true |
| 21 | get_pull_request |
true |
false |
true |
true |
| 22 | list_pull_requests |
true |
false |
true |
true |
| 23 | get_pull_request_files |
true |
false |
true |
true |
| 24 | get_pull_request_status |
true |
false |
true |
true |
| 25 | get_pull_request_comments |
true |
false |
true |
true |
| 26 | get_pull_request_reviews |
true |
false |
true |
true |
Suggested code change — top 3 priority tools
merge_pull_request (highest priority)
server.registerTool(
"merge_pull_request",
{
description: "Merge a pull request",
inputSchema: {
owner: z.string(),
repo: z.string(),
pullNumber: z.number(),
commitTitle: z.string().optional(),
commitMessage: z.string().optional(),
mergeMethod: z.enum(["merge", "squash", "rebase"]).optional(),
},
annotations: {
readOnlyHint: false,
destructiveHint: true,
idempotentHint: false,
openWorldHint: true,
},
},
handler
);push_files
server.registerTool(
"push_files",
{
description: "Push multiple files to a GitHub repository in a single commit",
inputSchema: {
owner: z.string(),
repo: z.string(),
branch: z.string(),
files: z.array(z.object({ path: z.string(), content: z.string() })),
message: z.string(),
},
annotations: {
readOnlyHint: false,
destructiveHint: false,
idempotentHint: false,
openWorldHint: true,
},
},
handler
);get_file_contents (read-only template — same pattern for all 14 read tools)
server.registerTool(
"get_file_contents",
{
description: "Get the contents of a file or directory from a GitHub repository",
inputSchema: { owner: z.string(), repo: z.string(), path: z.string(), branch: z.string().optional() },
annotations: {
readOnlyHint: true,
idempotentHint: true,
openWorldHint: true,
},
},
handler
);The 14 read-only tools all share the same annotation values — readOnlyHint: true, idempotentHint: true, openWorldHint: true — and can be annotated in a single pass.
Rationale for key tools
merge_pull_request → destructiveHint: true
This is the most consequential tool on this server. Merging a PR into a target branch is irreversible without force-push (which requires elevated permissions most tokens don't have). An agent that calls this by mistake or through prompt injection permanently alters the target branch. This is the single most important annotation to add.
push_files → destructiveHint: false, idempotentHint: false
Pushes commits to a remote branch. While not destructive in the "data loss" sense (git preserves history), it modifies shared remote state that other developers rely on. Not idempotent — pushing the same files twice creates two commits.
add_issue_comment → idempotentHint: false
Posting a comment twice creates duplicate comments. In a retry loop, this is publicly visible noise. The annotation signals clients to avoid automatic retries.
create_or_update_file → idempotentHint: true
Upserting the same file with the same content and SHA is a no-op. Safe for retries.
All tools → openWorldHint: true
Every tool on this server makes external API calls to api.github.com. The server has side effects outside the local machine — public repos are visible to everyone, comments trigger notifications, merges trigger CI.
Why this matters — chaining risks in multi-server setups
This server is the most common "write-to-the-outside-world" MCP server. When paired with local data sources, the composition risks become real as MCP adoption grows and agents gain access to more tools simultaneously:
-
filesystem.read_file→github.push_files→ An agent reading local files (config,.env, private keys) could push them to a public repo. WithoutreadOnlyHint: falseonpush_files, annotation-aware clients can't distinguish it from the 14 read-only tools on this server — they all look identical with empty annotations. -
Prompt injection →
github.merge_pull_request→ An agent processing untrusted content could be instructed to merge an open PR.destructiveHint: trueonmerge_pull_requestenables any spec-compliant client to add a confirmation step before irreversible merges. Without it, the merge is indistinguishable fromget_pull_requestto annotation-aware tooling. -
github.get_file_contents→ outbound send → Source code from private repos could be exfiltrated if chained with a network or email tool.readOnlyHint: trueonget_file_contentsclarifies the read is safe in isolation — enabling a "allow reads, gate outbound sends" policy pattern.
These composition risks grow as agents gain access to more servers simultaneously. Annotations are cheap to add now and become increasingly valuable as MCP client tooling matures.
Impact
This is the largest tool surface of any official MCP server. Adding annotations is a metadata-only change — roughly 130 lines of additions across 26 tool registrations. No behavior changes.
Suggested triage order if you want to start small:
merge_pull_request— irreversible, highest consequencepush_files— modifies shared remote state- All 14 read-only tools in one batch — identical values, mechanical change
Found via automated scan with AgentWard. Enumerated via live JSON-RPC handshake, protocol 2024-11-05.
Activity
agentward-ai commentedon Feb 25, 2026
How we found this
This was discovered using AgentWard — an open-source MCP security scanner that enumerates tools via live JSON-RPC handshake and assesses risk levels, data access patterns, and annotation coverage.
Reproduce
AgentWard currently infers risk from tool names and input schemas when annotations are missing. For example,
merge_pull_requestis rated LOW by the heuristic engine because "merge" isn't in the standard destructive-verb set — addingdestructiveHint: truewould fix this for every downstream tool, not just ours.agentward-ai commentedon Feb 25, 2026
report-github.md
Add tool annotations to server-github
olaservo commentedon Sep 29, 2026
Thanks for the report! This server is no longer maintained in this repo. It was moved to servers-archived, which is read-only, so there is no place for this change to land. If you need the behavior, a fork published to the MCP Registry is the way to go.
Closing this issue.