Skip to content

Expose MCP tool annotations (readOnlyHint, destructiveHint) in PreToolCallDecideHook #62

@jiridanek

Description

@jiridanek

Environment

  • google-antigravity SDK v0.1.3
  • Python 3.14
  • macOS

Problem

The MCP protocol defines ToolAnnotations on tool definitions (spec):

class ToolAnnotations:
    title: str | None
    readOnlyHint: bool | None        # Tool doesn't modify state
    destructiveHint: bool | None     # Tool may perform destructive updates
    idempotentHint: bool | None      # Repeated calls with same args have no additional effect
    openWorldHint: bool | None       # Tool interacts with external entities

MCP servers can annotate their tools with these hints. Hosts are starting to use them for various purposes:

  • Claude Code uses readOnlyHint as a concurrency hint — true enables parallel execution of MCP tools, false or missing forces serialization (docs)
  • Goose classifies tools as low-risk (read) vs high-risk (write/destructive) for its SmartApprove mode — currently heuristic-based, but could be annotation-driven

The Antigravity SDK does not expose these annotations. When an MCP tool call arrives at PreToolCallDecideHook, the ToolCall object only has name, args, and id — no way to access the tool's declared annotations.

Evidence

The SDK's MCP tool handling in local_connection.py only extracts server_name, tool_name, and arguments_json from the proto — annotations are not read or forwarded:

if not found_action and step_update.HasField(_MCP_TOOL_PROTO_FIELD):
    mcp_pb = getattr(step_update, _MCP_TOOL_PROTO_FIELD)
    action_str = _get_mcp_tool_name(mcp_pb.server_name, mcp_pb.tool_name)
    found_action = True
    args = json.loads(mcp_pb.arguments_json or "{}")

No annotation-related fields exist anywhere in the SDK:

$ grep -r "readOnlyHint\|destructiveHint\|idempotentHint\|openWorldHint\|ToolAnnotation" \
    .venv/lib/python3.14/site-packages/google/antigravity/
# (no results)

Use case

I'm building an ACP adapter that wraps the Antigravity SDK for JetBrains IDEs. The PreToolCallDecideHook gates tool calls — safe tools auto-allow, while dangerous tools prompt the user for approval in the IDE UI.

Currently I maintain a hardcoded whitelist of safe tool names (view_file, list_directory, etc.), but for MCP server tools (prefixed mcp_*) I have no way to distinguish list_pages (read-only) from execute_tool (destructive). All MCP tools require manual approval, which is slow and annoying for read-only operations.

If annotations were exposed on ToolCall (or accessible via the agent/hook context), I could auto-approve readOnlyHint=True tools from trusted servers and only prompt for others — rather than reimplementing heuristic classification or hardcoding tool names.

Proposed API

Add an annotations field to ToolCall (or a lookup method on the hook context):

# Option A: on ToolCall directly
@dataclasses.dataclass
class ToolCall:
    name: str
    args: dict
    id: str | None
    annotations: ToolAnnotations | None = None  # NEW

# Option B: on HookContext
class HookContext:
    def get_tool_annotations(self, tool_name: str) -> ToolAnnotations | None: ...

This requires the Go harness to forward annotations from the MCP server's tools/list response through the proto to the Python SDK.

Workaround

The only current option is to hardcode tool names or require user approval for all MCP tools. Neither scales across arbitrary MCP servers.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions