-
Notifications
You must be signed in to change notification settings - Fork 9.7k
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.