Skip to content

The MCP server never publishes tool annotations or output schemas — it already classifies every tool read/write/destructive and tells no host #561

Description

@serge-ivo

The MCP server withholds two things the spec has for it, and it already knows both

The owner connected ProAgentStore to ChatGPT. It works — all 135 tools registered with their input schemas, OAuth used, Developer Mode on, Connected 14 Aug 2026. But ChatGPT's settings page flags every single tool with "Output schema recommended", and its permission control reads "Allow low-risk actions" with no way to tell which actions are low-risk.

Both are spec features PAGS does not use. Verified by grep over workers/mcp/src/:

outputSchema      → 0 occurrences
structuredContent → 0 occurrences
readOnlyHint / destructiveHint / idempotentHint / openWorldHint → 0 occurrences

1. Tool annotations — the data already exists

workers/mcp/src/safety.ts:3 declares MCP_SCOPES = ["read", "write", "runtime", "destructive"], every tool is classified against it, and the scope is gated before dispatch. DEFAULT_SCOPES deliberately excludes destructive so delete-agent / overwrite-repo can never run on a default connection (safety.ts:6-9). Destructive tools additionally require an exact confirm string.

So the server knows, per tool, whether it reads, writes, or destroys — and publishes none of it in tools/list. MCP's annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) are exactly the channel for this, and they are what a host's "allow low-risk actions" setting reads.

The consequence is concrete: a host must treat list_agents and cancel_instance identically — prompting for both, or trusting both. The owner is running 135 tools in that state.

This is a projection of data that already exists, not new information. That is what makes it worth doing first.

2. outputSchema / structuredContent

Every tool declares inputSchema only, through one registration path (this.server.tool(...), workers/mcp/src/index.ts:138 onward). MCP 2025-06-18 pairs outputSchema with structuredContent so a host can parse a result rather than re-read prose.

Establish before building whether this is cosmetic (rendering), a quality issue (tool selection), or a correctness issue, and from the MCP spec rather than from ChatGPT's warning text alone. A pags-ba agent is separately establishing what OpenAI's Apps SDK does with it. If the honest answer is "it improves rendering and nothing else", say so and scope accordingly — 135 hand-written schemas would be a poor trade for cosmetics, whereas a helper on the shared registration path may be cheap.

Not a gap, stated so nobody adds it reflexively

The server exposes no resources and no prompts. Those are optional MCP features and PAGS is a tool surface; their absence is a design choice, not a standards failure. Do not add them under this issue.

Acceptance criteria

  1. tools/list carries annotations derived from the existing scope classification — one source, not a second hand-maintained list. A tool that changes scope changes its annotation automatically.
  2. A destructive tool is annotated destructive; a read tool is annotated read-only. Assert it across the whole registered surface, not a sample — index.test.ts:496 already drives the real registration path and asserts h.tools.size === MCP_TOOL_COUNT, so the denominator is available (ADR 0002).
  3. Whether outputSchema is adopted is decided on evidence and stated. If adopted, it goes through the shared registration path.
  4. MCP_TOOL_COUNT and the /health figure stay correct — tool-count.ts exists because that number rotted once already.
  5. No behaviour change to gating: annotations are advisory to the host and must not become the enforcement path. safety.ts stays the authority.

Verified vs inferred

  • Verified: all four grep results; safety.ts:3-9; the registration path; SDK pinned at @modelcontextprotocol/sdk 1.29.0; and ChatGPT's own settings page showing 135 tools registered with input schemas and "Output schema recommended" on each.
  • Inferred: that annotations are what ChatGPT's "Allow low-risk actions" reads. That is the documented purpose of the field, but I have not confirmed ChatGPT's specific implementation — the parallel BA investigation covers it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions