Skip to content

feat: Add output schema to select readonly tools under opt-in feature flag - #2468

Open
RossTarrant wants to merge 3 commits into
mainfrom
rosstarrant/add-output-schema
Open

feat: Add output schema to select readonly tools under opt-in feature flag#2468
RossTarrant wants to merge 3 commits into
mainfrom
rosstarrant/add-output-schema

Conversation

@RossTarrant

@RossTarrant RossTarrant commented May 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds feature-gated MCP outputSchema for selected read-only tools.

Why

Output schemas allow LLMs to anticipate the response from a tool. This proves useful to Code Mode, which needs machine-readable MCP responses for multi-step workflows.

What changed

  • Added the output_schemas feature flag and inventory registration support for gated tool output schemas.
  • Added minimal structured response types and schema inference helpers.
  • Added structured outputs for selected context, issue, label, pull request, repository, and search tools.
  • Preserved existing text content responses for back-compat when structured output is enabled.
  • Added tests for feature-flag resolution, schema registration, toolsnap handling, and structured content gating.

MCP impact

  • No tool or API changes
  • Tool schema or behavior changed
    • When output_schemas is enabled, selected tools include outputSchema in tool definitions and return matching structuredContent.
  • New tool added

Prompts tested (tool changes only)

  • "List open issues in github/github-mcp-server"
  • "Search pull requests in github/github-mcp-server about output schemas"
  • "Search code in github/github-mcp-server for StructuredContent"
  • "List recent releases for github/github-mcp-server"

Security / limits

  • No security or limits impact
  • Auth / permissions considered
    • Existing tool scopes and permissions are unchanged.
  • Data exposure, filtering, or token/size limits considered
    • Structured payloads are feature-gated and use minimal response shapes while keeping legacy content populated.

Tool renaming

  • I am renaming tools as part of this PR (e.g. a part of a consolidation effort)
  • I have added the new tool aliases in deprecated_tool_aliases.go
  • I am not renaming tools as part of this PR

Lint & tests

  • Linted locally with ./script/lint
  • Tested locally with ./script/test

Docs

  • Not needed
  • Updated (README / docs / examples)

@RossTarrant RossTarrant changed the title [WIP] Add output schema to select readonly tools feat: Add output schema to select readonly tools under feature flag May 18, 2026
@RossTarrant RossTarrant changed the title feat: Add output schema to select readonly tools under feature flag feat: Add output schema to select readonly tools under opt-in feature flag May 18, 2026
@RossTarrant
RossTarrant marked this pull request as ready for review May 18, 2026 14:42
@RossTarrant
RossTarrant requested a review from a team as a code owner May 18, 2026 14:42
Copilot AI review requested due to automatic review settings May 18, 2026 14:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds opt-in MCP outputSchema support for selected read-only GitHub tools, with matching gated structuredContent responses while preserving legacy text responses.

Changes:

  • Adds output_schemas feature flag plumbing and registration-time output schema attachment.
  • Introduces schema inference/result helpers plus minimal response wrapper types.
  • Adds structured outputs for selected context, issue, label, PR, repository, release, and search tools, with tests and toolsnap normalization updates.
Show a summary per file
File Description
pkg/inventory/server_tool.go Adds OutputSchema, registration options, and gated schema attachment.
pkg/inventory/registry.go Enables output schema registration based on feature flag.
pkg/inventory/registry_test.go Tests feature-gated registration behavior.
pkg/inventory/builder.go Adds inventory-side output schema feature flag constant.
pkg/http/middleware/cors.go Adds a new allowed CORS request header.
pkg/github/tools_validation_test.go Validates expected tools have gated output schemas.
pkg/github/search.go Adds structured outputs/schemas to search tools.
pkg/github/search_utils.go Passes dependencies to shared search handler for feature-gated structured output.
pkg/github/repositories.go Adds structured outputs/schemas to selected repository/release tools.
pkg/github/pullrequests.go Adds structured outputs/schemas to PR listing/search tools.
pkg/github/output_schema.go Adds output schema inference and structured text result helpers.
pkg/github/output_schema_test.go Tests schema inference and structured content gating.
pkg/github/minimal_types.go Adds minimal response wrapper/result types and converters.
pkg/github/labels.go Adds structured output/schema for get_label.
pkg/github/issues.go Adds structured outputs/schemas to selected issue tools.
pkg/github/feature_flags.go Adds output_schemas to allowed feature flags.
pkg/github/feature_flags_test.go Tests explicit output schema feature resolution.
pkg/github/context_tools.go Adds structured outputs/schemas to selected context tools.
internal/toolsnaps/toolsnaps.go Normalizes snapshots and strips gated outputSchema.
internal/toolsnaps/toolsnaps_test.go Tests output schema stripping in toolsnap normalization.

Copilot's findings

  • Files reviewed: 20/20 changed files
  • Comments generated: 2

// A shallow copy of the tool is made to avoid mutating the original ServerTool.
// Panics if the tool has no handler - all tools should have handlers.
func (st *ServerTool) RegisterFunc(s *mcp.Server, deps any) {
st.RegisterFuncWithOptions(s, deps, RegisterToolOptions{})
"Mcp-Session-Id",
"Mcp-Protocol-Version",
"Last-Event-ID",
"X-Custom-Auth-Headers",
@RossTarrant RossTarrant self-assigned this May 18, 2026
@olaservo olaservo mentioned this pull request Jul 26, 2026
13 tasks
olaservo added a commit to olaservo/github-mcp-server that referenced this pull request Aug 10, 2026
Adds feature-gated MCP `outputSchema` and `structuredContent`, targeting the
tools whose response shape varies by their `method` argument — the ones both
existing upstream attempts (github#2382, github#2468) explicitly set aside as
"multi-method tool returning different shapes".

Protocol revision 2026-07-28 (SEP-2106) is what makes these expressible.
2025-11-25 typed `outputSchema` as a closed object shape restricted to
`type: "object"` at the root, and `structuredContent` as a JSON object; the
draft widens both to any valid JSON Schema 2020-12 and any JSON value.

Two orthogonal gates:

  - `output_schemas` feature flag controls rollout.
  - The negotiated protocol version, read per request, controls which schema
    shapes are legal. Object-root schemas ship to every client; non-object
    roots and non-object structuredContent are withheld below 2026-07-28.

Unions use `anyOf`, never `oneOf`. `oneOf` requires exactly one branch to
match and provably fails here: actions_run_trigger has four structurally
identical branches, an empty array satisfies every array branch, and an
issue_read `get` on a sub-issue also satisfies the `get_parent` branch. A
test asserts no schema ever contains "oneOf".

Handlers are unchanged. Rather than thread typed values out of ~20 call
sites, structuredContent is mirrored from the serialized-JSON text block each
handler already emits — the relationship the spec itself describes. This is
byte-exact, so it cannot drift from the text content and preserves number
formatting a re-marshal would lose. It applies only to tools that declare a
schema, leaving the other ~120 tools byte-identical on the wire.

Because the schema lives on ServerTool and is copied onto a duplicate
mcp.Tool at registration, Tool.OutputSchema stays nil in the default surface
and none of the 123 committed toolsnaps change.

Tools covered: actions_get, actions_list, actions_run_trigger,
discussion_comment_write, issue_dependency_read (single shape, not a union),
issue_read, pull_request_read.

Also fixes three adjacent bugs found along the way:

  - GetSubIssues could emit the literal `null`: go-github declares
    `var subIssues []*SubIssue` and leaves it nil on an empty body, which
    marshals to null rather than []. Every sibling method normalises; this
    one did not.
  - sub_issue_write's `method` property had no `enum`, so its values existed
    only in prose while dispatch accepted exactly add/remove/reprioritize.
  - convertJSONTextResultToCSV cleared StructuredContent, which would have
    made csv_output silently disable output_schemas for every list_* tool.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants