--- name: mcp-api-tool description: Use this skill when adding or changing a ctl-api MCP tool (RegisterMCPTools, mcp_*.go handlers). model: sonnet color: blue --- This skill enforces the ctl-api MCP tool registration pattern for the stateless Streamable HTTP server. ## Steps 1. Pick the domain service under `services/ctl-api/internal/app//service/` that already implements `api.Service` and is in FX `group:"services"`. 2. Add `mcp_.go` with an input struct (`json` + `jsonschema` tags) and a handler matching `func(ctx, *mcp.CallToolRequest, in T) (*mcp.CallToolResult, any, error)`. 3. Read org/account/token role from `keys.*FromContext(ctx)` — never Gin context. 4. Return payloads via `api.MCPJSONResult(...)` (compact JSON text content). 5. Register the tool in that domain’s `mcp_register.go` with `mcp.AddTool(server, api.MCPReadTool(...)|api.MCPWriteTool(...), handler)` so `Title` and `Annotations` are always set. 6. For mutating tools: prefix `Description` with `WRITE OPERATION:`, use `api.MCPWriteTool(..., destructive, idempotent)`, and call `require.Write(ctx)` at the start of the handler. 7. For read tools scoped to an org: use `api.MCPReadTool(...)` and call `require.Read(ctx)` at the start of the handler. 8. Prefer trimmed response shapes over full GORM models when the payload would be large for LLM context. Format times with `api.MCPTime` (UTC RFC3339 `…Z`) — do not serialize `time.Time`, add relative age strings, or localize on the server. 9. List tools must use `api.MCPListPage` (default 20, max 100), `Limit(limit+1)`, and `api.MCPClipList`. Return `has_more` / `next_offset`. Append `api.MCPListToolHint` to the description. Do not loop until the full set is loaded. 10. No new MCP-specific FX wiring — `RegisterMCPTools` is discovered via type assert on `api.MCPService`. 11. Update the tool tables in `docs/guides/agents/tools.mdx` and `bins/cli/cmd/agents_context.md`. Add a prompt to `docs/guides/agents/sample-queries.mdx` if the tool is part of a user-facing flow. Reference examples: `apps/service/mcp_list_apps.go`, `apps/service/mcp_register.go`, `installs/service/mcp_approve_step.go`. ## Anti-Patterns - **Do not** implement tools only in the CLI proxy — tools live in ctl-api; the CLI forwards them. - **Do not** skip `require.Write` on write tools — description prefix / annotations alone are not enough. - **Do not** skip `require.Read` on org-scoped read tools. - **Do not** register tools with bare `&mcp.Tool{Name, Description}` — always use `MCPReadTool` / `MCPWriteTool`. - **Do not** key request state on `Mcp-Session-Id` — the server is `Stateless: true`; use bearer token + `X-Nuon-Org-ID` / `select_org`. - **Do not** print to stdout from tool handlers — MCP protocol owns the stream. - **Do not** serialize `time.Time`, add `*_relative` age fields, or render local timestamps — use `MCPTime`; agents convert UTC to local. - **Do not** `Find` a collection without `api.MCPListPage` / `Limit(limit+1)` / `MCPClipList`. Return `has_more` and `next_offset`; do not loop until the full set is loaded.