Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,17 @@ Here's how to integrate with some popular clients assuming you have a configurat
}
```

## Structured Tool Output

Every tool declares an `outputSchema` and returns results in two forms on the same response:

- `content` — a human-readable `TextContent` block containing the output serialized as JSON. Kept for backward compatibility with clients that only render text.
- `structuredContent` — the typed, parseable object. New clients should prefer this for programmatic consumption.

Schemas are auto-generated from each tool's Go `Output` struct via [`github.com/google/jsonschema-go`](https://pkg.go.dev/github.com/google/jsonschema-go), which emits **JSON Schema draft 2020-12**. The MCP SDK validates every response against the declared schema before sending, so clients can rely on the shape. Field-level descriptions live as `jsonschema:"..."` tags on the `Output` struct in each tool's `pkg/tools/<name>/tool.go`.

To discover the live schema for any tool, inspect the `outputSchema` field returned by a `tools/list` MCP request against a running server.

## Enabling or disabling specific tools

You can enable or disable specific tools by passing command line parameters, setting environment variables, or customizing the `mcp.yaml` configuration file.
Expand Down
61 changes: 61 additions & 0 deletions pkg/chip/output_schema_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package chip_test

import (
"encoding/json"
"log"
"net/http"
"testing"

"github.com/collibra/chip/pkg/chip"
"github.com/collibra/chip/pkg/tools"
"github.com/google/jsonschema-go/jsonschema"
"github.com/modelcontextprotocol/go-sdk/mcp"
)

// Validates all tool output schemas against 2020-12 schema
func TestAllToolsDeclareValid2020_12OutputSchemas(t *testing.T) {
server := chip.NewServer()
tools.RegisterAll(server, &http.Client{}, &chip.ServerToolConfig{})

t1, t2 := mcp.NewInMemoryTransports()
if _, err := server.Connect(t.Context(), t1, nil); err != nil {
log.Fatal(err)
}
client := mcp.NewClient(&mcp.Implementation{Name: "test", Version: "v0.0.0"}, nil)
session, err := client.Connect(t.Context(), t2, nil)
if err != nil {
log.Fatal(err)
}
defer func() { _ = session.Close() }()

result, err := session.ListTools(t.Context(), nil)
if err != nil {
t.Fatalf("list tools: %v", err)
}
if len(result.Tools) == 0 {
t.Fatal("no tools registered")
}

for _, tool := range result.Tools {
t.Run(tool.Name, func(t *testing.T) {
if tool.OutputSchema == nil {
t.Fatalf("tool %q has no outputSchema", tool.Name)
}

raw, err := json.Marshal(tool.OutputSchema)
if err != nil {
t.Fatalf("marshaling outputSchema: %v", err)
}
var schema jsonschema.Schema
if err := json.Unmarshal(raw, &schema); err != nil {
t.Fatalf("unmarshaling outputSchema: %v", err)
}

// Resolve validates the schema against the 2020-12 meta-schema.
// A non-nil error means the schema is not valid 2020-12.
if _, err := schema.Resolve(nil); err != nil {
t.Fatalf("outputSchema is not valid JSON Schema 2020-12: %v", err)
}
})
}
}
Loading