feat: add structured output schemas to read-only tools - #2382
Draft
SamMorrowDrums wants to merge 8 commits into
Draft
feat: add structured output schemas to read-only tools#2382SamMorrowDrums wants to merge 8 commits into
SamMorrowDrums wants to merge 8 commits into
Conversation
Replace the server instructions system with MCP skill resources that follow the skill:// URI convention for discovery by MCP clients. Each skill resource covers a domain of tools and provides YAML frontmatter (name, description, allowed-tools) plus markdown guidance. All tools remain available by default (all tools mode). Skills provide contextual guidance that clients can discover via resources/list. 16 skills covering all toolsets: context, repos, issues, pull-requests, code-security, actions, discussions, projects, notifications, gists, users-orgs, security-advisories, labels, git, stargazers, copilot. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update all 16 skill definitions to use explicit backtick-formatted tool names with '## Available Tools' sections instead of bold markdown references. This makes tool names more reliably parseable by models that consume the SKILL.md content via load_skill. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…kills Replace 16 toolset-oriented skills (repos, issues, pull-requests, etc.) with 27 workflow-oriented skills that map to real user intents: PR workflows: create-pr, review-pr, self-review-pr, address-pr-feedback, merge-pr Issue workflows: triage-issues, create-issue, manage-sub-issues CI/CD: debug-ci, trigger-workflow Security: security-audit, fix-dependabot, research-vulnerability Code exploration: explore-repo, search-code, trace-history Projects: manage-project Notifications: handle-notifications Releases: prepare-release Repository management: manage-repo, manage-labels Collaboration: contribute-oss, browse-discussions, delegate-to-copilot Discovery: discover-github, share-snippet, get-context Each skill focuses on a specific workflow with non-obvious guidance, anti-patterns, and identity-aware instructions (e.g., reviewing someone else's PR vs self-reviewing your own). Tools overlap across skills based on workflow needs rather than API domain. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update all 27 skill descriptions to include both what the skill does AND when to use it, following the agent skills specification guidance that descriptions are the primary triggering mechanism. Descriptions now explicitly list user intents that should activate each skill. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Skills now only include tools that are actually registered with the MCP server, based on the active toolset configuration. Skills with no available tools are skipped entirely. This prevents skills from referencing non-existent tools (e.g., gist tools when the gists toolset isn't enabled). RegisterSkillResources now accepts a map of available tool names built from the inventory at startup time. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When skills handle progressive disclosure, the server should expose all tools via tools/list regardless of toolset configuration. The client decides tool visibility through skill allowedTools, not the server. This replaces the previous filtering approach — now inv.AllTools() registers every tool with the MCP server, and skills reference the full tool surface. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
All toolsets are now enabled by default (Default: true). This ensures every tool is registered out of the box, which is required for skills to work — skills handle progressive tool discovery, so the server should expose the full surface. Users can still restrict with --toolsets if needed. Reverts the inv.AllTools() registration workaround from server.go since the normal RegisterAll path now picks up everything. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add TypedRegisterFunc to ServerTool that uses mcp.AddTool[In, Out]() for typed tool registration. The SDK auto-generates OutputSchema from the Go Out type and populates StructuredContent on tool results, while preserving existing TextContent for backwards compatibility. Tools updated with typed structured output: - get_me (MinimalUser) - list_issues (MinimalIssuesResponse) - list_pull_requests (ListPullRequestsResult) - search_issues (IssueSearchResult) - search_pull_requests (PullRequestSearchResult) - search_code (CodeSearchResult) Tools like issue_read and pull_request_read keep Out=any since they are multi-method tools returning different shapes per method. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
SamMorrowDrums
force-pushed
the
sammorrowdrums/structured-output-schemas
branch
from
April 24, 2026 18:08
6f83796 to
c94b89a
Compare
pachecocordovamoiseseduardo-byte
approved these changes
May 23, 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds structured output (OutputSchema + StructuredContent) to read-only tools by leveraging the go-sdk's
mcp.AddTool[In, Out]()generic registration path.How it works
TypedRegisterFunc— A new closure field onServerToolthat captures Go type parameters (In,Out) and callsmcp.AddTool[In, Out]()instead of the untypeds.AddTool(). Set automatically inNewServerToolWithContextHandlerandNewServerTool.RegisterFuncpreference — WhenTypedRegisterFuncis set,RegisterFuncuses it. This enables the SDK to auto-generateOutputSchemafrom theOuttype and populateStructuredContenton tool results.Backwards compatibility — Tools still return
MarshalledTextResult(...)forContent(text). The SDK addsStructuredContentalongside without overwriting existing content.Tools with structured output
get_meMinimalUserlist_issuesMinimalIssuesResponselist_pull_requestsListPullRequestsResultsearch_issuesIssueSearchResultsearch_pull_requestsPullRequestSearchResultsearch_codeCodeSearchResultTools unchanged (Out=any, no structured output)
issue_read— multi-method tool (get, get_comments, get_sub_issues, get_labels) returning different shapespull_request_read— multi-method tool (8 methods) returning different shapesChanges
pkg/inventory/server_tool.go— AddedTypedRegisterFuncfield, updatedRegisterFuncto prefer it, set it inNewServerToolWithContextHandlerandNewServerToolpkg/github/minimal_types.go— Added wrapper output structs (ListPullRequestsResult,CodeSearchResult,IssueSearchResult,PullRequestSearchResult) andconvertToCodeSearchResultpkg/github/context_tools.go—get_mereturnsMinimalUseras typed outputpkg/github/issues.go—list_issuesreturnsMinimalIssuesResponse,search_issuesreturnsIssueSearchResultpkg/github/pullrequests.go—list_pull_requestsreturnsListPullRequestsResult,search_pull_requestsreturnsPullRequestSearchResultpkg/github/search.go—search_codereturnsCodeSearchResultpkg/github/search_utils.go—searchHandlernow also returns*github.IssuesSearchResultfor callers to convertpkg/github/tools_validation_test.go— Tests verifyingTypedRegisterFuncis set on all tools and structured output tools register without panicspkg/github/pullrequests_test.go— Updated test to match new wrapped output format