Add structured content to the Go weather server and client and update to latest SDK and protocol version - #166
Draft
olaservo wants to merge 5 commits into
Conversation
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>
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. |
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>
olaservo
force-pushed
the
structured-output-2026-07-28/go
branch
from
August 1, 2026 23:04
b9254b9 to
5d15e97
Compare
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>
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.
Brings the Go examples onto
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:[ { "event": "Flood Warning", "area": "La Salle County", ... }, { "event": "Heat Advisory", "area": "Wheeler County", ... } ]Through
2025-11-25anOutputSchemahad 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 schema support (SEP-2106) landed after
v1.6.1;v1.7.0is the first stable release carrying it.Schema declaration, and where it is left to inference
get_forecastsets noOutputSchemaon theTool— the SDK infers one from the handler's return type, which is the idiomatic Go spelling and worth showing.get_alertsoverrides it.jsonschema-goinfers["null","array"]for a slice because a nil slice marshals tonull, and the handler always builds one, so the override narrows the root to"array". Re-checked againstv1.7.0: rebuilding with the override removed still yields the nullable form, so it is still required.Three structural fixes
Two build artefacts were committed by mistake in the first commit of this branch —
mcp-client-go.exeandweather-server-go/weather.exe, about 14MB of binaries. Removed here; the ignore rules that keep them out come with #163, which is also what buildsweather-server-go/server.Forecast periods now have their own type.
Forecast.PeriodsreusedForecastPeriod, the struct that decodes the NWS response, so its camelCase json tags leaked into the declared schema —temperatureUnitanddetailedForecastwhere the other four quickstarts publishtemperature_unitanddetailed_forecast. Splitting out aPeriodoutput type follows what this file already does withAlertandAlertProperties: the shape NWS sends and the shape the tool publishes are different contracts. All five examples now advertise identical shapes.The deprecated
loggingcapability is no longer advertised. With a nilServerOptionsthe SDK defaultsServerCapabilitiesto{"logging":{}}— "for historical reasons", per its own doc comment. This server does no logging, and logging is deprecated as of2026-07-28(SEP-2577), so an example for that revision should not claim it. Passing an emptyCapabilitiessuppresses the default;toolsis still inferred from the registered tools.Other changes
Error paths return an error. A tool declaring an
OutputSchemaMUST return conforming structured content, so a path with no data has to fail rather than answer with a bare text result.The client validates. It compiles every declared
OutputSchemaat connect time and checks results against it — the spec's client-side SHOULD.jsonschema-gois already an SDK dependency, so this adds none.Each channel goes to its stated reader.
Contentis forwarded to the model;StructuredContentis used as data, reporting how many items came back.A missing
.envis no longer fatal, andANTHROPIC_API_KEYis checked after connecting rather than before, matching the Python and TypeScript clients. That is what lets the connection be exercised without credentials.Model identifier moves to
claude-sonnet-5. The second call in the tool loop used a hardcodedModelClaude3_7SonnetLatestwhile the first used the package variable; both now use the same one.One caveat
The SDK sends an array-rooted schema as written on every connection rather than projecting it down the way the TypeScript SDK 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
Captured off the raw wire — a real
get_alerts("TX")call against live NWS data, no SDK on the client side:Two blocks because the SDK appends the serialised JSON as a backwards-compatibility fallback alongside the tool's own prose.
Related
One of a set bringing the examples onto
2026-07-28, one PR per language so the four can be compared: #164 (Python), #165 (TypeScript), #166 (Go), #167 (Rust). #163 is the shared prerequisite.CI here is green, but on
mainthe smoke test does not cover the Go examples at all — nothing in this diff is exercised by it, so until #163 lands this PR is verified by the raw-wire capture above rather than by CI. #163 adds the Go server to the suite and installs the Go toolchain, after which this diff is covered.The Ruby examples are not in the set. The
mcpgem 1.1.0 does now negotiate2026-07-28, but its server never emits theresultTypefield that the revision makes mandatory, so a spec-strict client rejects every response includingtools/list. That is an upstream fix, not something an example can work around.🤖 Generated with Claude Code