Fix MCP tools/list rejection: wrap list_repos slice in object

The go-sdk infers each tool's outputSchema from its handler result
type, and MCP structured output must be type "object". list_repos
returned []repos.State, yielding outputSchema.type "array", which
Claude Desktop rejects at tools/list — taking the whole server down.

Wrap the slice in listReposOutput{Repos: ...} so the schema is an
object, update the round-trip test, and record the struct-result rule
in AGENT.md §8.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-20 08:00:30 -04:00
parent e3336efc25
commit d3abd4416e
3 changed files with 19 additions and 4 deletions
+7
View File
@@ -367,6 +367,13 @@ obeys the safety rules (§1.4).
`get_pending_switch`, `list_prs`.
- Act: `git_status/checkout/commit/push/pull/create_branch`,
`create_pr`, `merge_and_cleanup_pr`, `set_active_project`, `ack_switch`.
- **A tool's result type must be a struct, never a bare slice/map/scalar.** The
go-sdk infers each tool's `outputSchema` from its handler's result type, and MCP
structured output must be a JSON **object** (`type: "object"`). A handler that
returns `[]T` yields `outputSchema.type: "array"`, which Claude Desktop rejects
at `tools/list` — and one bad tool takes the whole server down. Wrap any
collection result in a named output struct (e.g. `list_repos` returns
`listReposOutput{ Repos []repos.State }`, not `[]repos.State`). Fixed 2026-09-20.
- **Destructive tools carry the §1.4 contract into MCP:** they describe exactly
what they will do and default to the safe variant. The confirmation is the
human's — surfaced through Claude and/or the app UI — not something the tool