Skip to content

Add tool annotations to server-github (26 tools, 0 annotated) #3399

@agentward-ai

Description

@agentward-ai

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_requestdestructiveHint: 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_filesdestructiveHint: 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_commentidempotentHint: 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_fileidempotentHint: 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_filegithub.push_files → An agent reading local files (config, .env, private keys) could push them to a public repo. Without readOnlyHint: false on push_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: true on merge_pull_request enables any spec-compliant client to add a confirmation step before irreversible merges. Without it, the merge is indistinguishable from get_pull_request to 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: true on get_file_contents clarifies 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:

  1. merge_pull_request — irreversible, highest consequence
  2. push_files — modifies shared remote state
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions