Repository navigation
Conversation
…ools
Every tool whose handler returns a concrete Go type now advertises an
outputSchema reflected from that type and returns the result as
structuredContent, with the same JSON as its text content. Harnesses that
compose tool calls in code (pi's Codemode, tool-search clients) can then
program against a result's shape without calling the tool first.
Results that are not JSON objects (slices, maps) are wrapped as
{"result": ...}, as MCP requires structuredContent to be an object; nil
slices and maps there become [] and {}. A nil struct pointer is returned as
the zero value, as go-sdk's typed handlers do. string, any and
*mcp.CallToolResult results stay text-only.
Strict clients (the TypeScript and Python SDKs) reject a result that does
not match its schema, so the schema has to describe the marshalled JSON,
not just the struct:
- fields encoding/json can emit as null (nil pointers, slices, maps without
omitempty, and nilable slice/map elements) accept null
- fields promoted through a nil embedded pointer are not required
- TextMarshaler types are strings; other json.Marshaler types are
unconstrained unless they describe themselves with JSONSchema()
Each schema is checked at construction against values of its type populated
to every depth, so a mismatch fails at startup and in every test run.
Results are also validated at call time; a mismatch is logged rather than
failing the call.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
query_prometheus and query_prometheus_histogram returned a bare
model.Value, so a caller could not tell an instant query's vector, scalar
or string from a range query's matrix without inspecting the shape, and the
interface had no output schema. Both now return the Prometheus HTTP API's
data object, {"resultType": ..., "result": ...}, described by a
hand-written schema matching how prometheus/common marshals each value
type. The histogram tool's result field is renamed to data to match.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alerting_rules_read/write, alerting_silences_read/write and alerting_manage_routing returned any, since each operation returns a different type, so they could not advertise an output schema. Each now returns a result struct with one field per operation (rules, rule, versions, savedRule, message; silences, silence, silenceID, status; notificationPolicies, contactPoints, contactPointIntegrations, timeIntervals, timeInterval). List operations report [] rather than omitting their field when nothing matches. Silence deletion now reports silenceID rather than silence_id, matching create and update. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
create_annotation returned the go-openapi response wrapper as any, which
marshalled as {"Payload": {...}} and had no output schema. It now returns
the {id, message} body for both standard and Graphite annotations.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
|
cursor review |
Prior to this commit, a typed-nil model.Matrix or model.Vector, which
backends return for queries with no results, marshalled as
{"resultType": ..., "result": null}. The advertised output schema requires
result to be an array, so strict MCP clients rejected empty
query_prometheus and query_prometheus_histogram results.
This commit makes PrometheusValue marshal typed-nil matrices and vectors
as [], matching the Prometheus HTTP API, and adds typed-nil cases to the
schema conformance test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
|
cursor review |
Contributor
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit f9dfb29. Configure here.
This branch has not been deployed
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
Every tool whose handler returns a concrete Go type now advertises an
outputSchemareflected from that type and returns its result asstructuredContent, alongside the existing text content (same JSON). Code-mode and other programmatic clients can then write code against a result's shape without calling the tool first.Closes #1278. Part of #1281.
Changes
tools_output.go: builds an output schema from the handler's return type inConvertTool.{"result": ...}, because MCP requiresstructuredContentto be an object.string,anyand*mcp.CallToolResultresults stay text-only.omitemptyacceptnull;TextMarshalertypes are strings; otherjson.Marshalertypes are unconstrained unless they implementJSONSchema().MustToolvars, so a mismatch fails at startup and in every test run.anyormodel.Value, so they get schemas too:query_prometheusandquery_prometheus_histogramcreate_annotationOutput shape changes (for release notes)
These change what tools return compared with v2.0.0. They ship in a minor release, not as breaking changes, because tool output is not a stable API:
query_prometheusandquery_prometheus_histogramreturn the Prometheus APIdataobject:{"resultType": ..., "result": ...}. The histogram tool'sresultfield is renamed todata.alerting_rules_*,alerting_silences_*andalerting_manage_routingreturn a result struct with one field per operation. List operations report[]instead of omitting the field. Silence deletion reportssilenceIDinstead ofsilence_id.create_annotationreturns{id, message}instead of{"Payload": {...}}.Testing
go test ./ ./tools/...passes (unit tests).tools_output_test.gocover:golangci-lint run: 0 issues.Note: this branch was originally built on
v2; it's now rebased ontomainafter the v2.0.0 squash-merge, and still checks all 127 tools onmainat construction.