Add structured content to the Go weather server and client and update to latest SDK and protocol version - #166
Open
olaservo wants to merge 5 commits into
Conversation
This was referenced Jul 26, 2026
There was a problem hiding this comment.
Pull request overview
Updates the Go weather server and Go MCP client examples to support MCP protocol revision 2026-07-28, adding structured tool outputs and aligning dependencies with the latest Go SDK prerelease needed for non-object output schemas.
Changes:
- Weather server tools now return structured outputs (
get_forecastas an object,get_alertsas a top-level JSON array) and convert prior “no data” paths into tool errors. - Go client compiles/validates each tool’s declared
outputSchemaat connect time and usesStructuredContentas application data while forwardingContentto the model. - Dependency updates to
github.com/modelcontextprotocol/go-sdk v1.7.0-pre.3andgithub.com/google/jsonschema-go v0.4.3.
Reviewed changes
Copilot reviewed 6 out of 10 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| weather-server-go/README.md | Documents structured output behavior and protocol/client caveat for array-rooted schemas. |
| weather-server-go/main.go | Adds structured output types, error-on-failure behavior, and explicit array-root schema override for get_alerts. |
| weather-server-go/go.mod | Bumps Go SDK and pins jsonschema-go directly. |
| weather-server-go/go.sum | Updates dependency checksums for bumped modules. |
| mcp-client-go/README.md | Documents client-side schema compilation/validation and channel handling. |
| mcp-client-go/main.go | Adds output schema compilation + result validation and separates model-facing Content from app-facing StructuredContent. |
| mcp-client-go/go.mod | Bumps Go SDK and jsonschema-go versions. |
| mcp-client-go/go.sum | Updates dependency checksums for bumped modules. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| ## Structured output | ||
|
|
||
| Both tools declare an `OutputSchema` and return `StructuredContent`. `get_forecast` returns an object; `get_alerts` returns a top-level JSON array, which protocol revision `2026-07-28` is the first to allow — see [Structured Content](https://modelcontextprotocol.io/specification/draft/server/tools#structured-content) in the spec. |
olaservo
force-pushed
the
structured-output-2026-07-28/go
branch
from
August 1, 2026 23:04
b9254b9 to
5d15e97
Compare
Both tools declare an OutputSchema and return StructuredContent alongside the text. get_alerts returns []Alert, so it answers with a top-level JSON array rather than an array nested in an object, which protocol revision 2026-07-28 is the first to allow. "No alerts" is simply []. get_forecast returns an object, for contrast. The declared schema narrows the inferred ["null","array"] root to "array": jsonschema-go infers the nullable form because a nil slice is valid Go, but the handler always builds a slice. Error paths return an error: a tool declaring an OutputSchema MUST return conforming structured content, so a path with no data has to fail. The client compiles every declared OutputSchema at connect time and validates results against it, using jsonschema-go - already an SDK dependency, so no new one. Each channel goes to its stated reader: Content is forwarded to the model, StructuredContent is used as data, reporting how many items came back. The client no longer treats a missing .env as fatal and checks for ANTHROPIC_API_KEY after connecting rather than before, matching the Python and TypeScript clients, so the connection can be exercised without credentials. Requires go-sdk v1.7.0-pre.3: non-object output schema support (SEP-2106) landed after v1.6.1 and is in no stable release. Model identifier moves to claude-sonnet-5; the second call in the tool loop used a hardcoded ModelClaude3_7SonnetLatest while the first used the package variable, so both now use the same one. Note that the SDK sends an array-rooted schema as written on every connection rather than projecting it down, so an older client rejects the tool list. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
v1.7.0 shipped on 2026-07-27, so the pin moves off v1.7.0-pre.3. Removes mcp-client-go.exe and weather-server-go/weather.exe, which were committed by accident in the previous commit — 14MB of build output that should never have been in the tree. The tests branch adds the ignore rules that keep them out. Also from review: the server README claimed both tools set an OutputSchema. Only get_alerts does; get_forecast relies on inference from the handler return type, which is the idiomatic spelling and worth saying so. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Forecast.Periods reused ForecastPeriod, the struct that decodes the NWS response, so its camelCase json tags leaked into the declared outputSchema: temperatureUnit and detailedForecast where the other four quickstarts publish temperature_unit and detailed_forecast. Splits out a Period output type, for the same reason Alert is already separate from AlertProperties: the shape NWS sends and the shape the tool publishes are different contracts and should not be one struct. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
go-sdk defaults ServerCapabilities to {"logging":{}} when ServerOptions is
nil — "for historical reasons", as its own doc comment puts it. This server
does no logging, and logging is deprecated as of 2026-07-28 (SEP-2577), so an
example for that revision should not be advertising it.
Passing an empty Capabilities suppresses the default. The tools capability is
still inferred from the registered tools, so server/discover now reports
{"tools":{"listChanged":true}} rather than logging alongside it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The spec never uses the phrase "structured output". It defines two separate things under Tool Result: "Structured Content" (the `structuredContent` field) and "Output Schema" (the `outputSchema` field). Collapsing them into "structured output" conflates the two. It is also actively confusing here. In LLM tooling "structured output" means constrained decoding — making the *model* emit conforming JSON. These clients call a model API, so a reader could reasonably take the phrase to mean the tool constrains the model's response, which is the opposite of what is going on: the tool describes the shape of its own result. Headings and prose now say "structured content". References to real identifiers are left alone: the Python SDK's own docs page is called Structured Output and lives at docs/servers/structured-output.md, and its decorator parameter is `structured_output`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
olaservo
force-pushed
the
structured-output-2026-07-28/go
branch
from
August 10, 2026 00:45
dbae15e to
a0ce1f8
Compare
olaservo
marked this pull request as ready for review
August 10, 2026 02:57
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.
Updates the Go examples to
go-sdk v1.7.0and protocol revision2026-07-28, and gives both tools a declaredOutputSchemawith matchingStructuredContent.get_alertsreturns[]Alert, so it answers with a top-level JSON array. Before2026-07-28an output schema had to be object-rooted, so a tool returning a list had to invent a key to hang it off.get_forecastreturns an object, for contrast. Non-object output schemas (SEP-2106) landed afterv1.6.1;v1.7.0is the first stable release with them.Schema inference, and where it's overridden.
get_forecastsets noOutputSchema— the SDK infers one from the handler's return type, which is the idiomatic Go spelling and worth showing.get_alertsoverrides it, becausejsonschema-goinfers["null","array"]for a slice (a nil slice marshals tonull) and this handler always builds one. Re-checked atv1.7.0: removing the override still yields the nullable form, so it's still needed.Three structural fixes
mcp-client-go.exe,weather-server-go/weather.exe). Removed; the ignore rules landed with Assert on structured content in the smoke tests and update to v2 #163.Forecast.Periodsreused the struct that decodes the NWS response, leaking its camelCase json tags into the declared schema —temperatureUnitwhere the other four publishtemperature_unit. Splitting out aPeriodtype follows what this file already does withAlertvsAlertProperties: what NWS sends and what the tool publishes are different contracts.loggingcapability. A nilServerOptionsdefaults capabilities to{"logging":{}}— "for historical reasons", per the SDK's own doc comment. This server does no logging, and2026-07-28deprecates it (SEP-2577). An emptyCapabilitiessuppresses the default;toolsis still inferred.Also
OutputSchemaMUST return conforming structured content.jsonschema-gois already an SDK dependency, so this adds none.Contentgoes to the model,StructuredContentis used as data..envis no longer fatal, and the key is checked after connecting — matching Python and TypeScript, and what lets the connection be exercised without credentials.claude-sonnet-5(the two calls in the tool loop had drifted apart).One caveat
The SDK sends an array-rooted schema as written rather than projecting it down the way TypeScript does, so a
2025-11-25client rejects the tool list:Use an object root if you need to serve both eras. The README says so.
Verification
A real
get_alerts("TX")call against live NWS, captured off the raw wire with no SDK on the client side:outputSchema.type = "array",resultType: "complete",ttlMs: 0,cacheScope: "public",structuredContentan 8-item array,contenttwo blocks — the SDK appends serialised JSON as a compatibility fallback alongside the tool's prose. CI also covers this now: #163 added the Go server to the smoke test and installed the Go toolchain, so both tools are called and asserted on.One of a set, one PR per language: #164 (Python, merged), #165 (TypeScript), #166 (Go), #167 (Rust); #163 was the shared test prerequisite and has merged. Ruby is not in the set — the
mcpgem never emits the mandatoryresultType, so a spec-strict client rejects every response. That needs an upstream fix.🤖 Generated with Claude Code