mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-11 21:39:01 +00:00
fix(docs): use canonical wire path capabilities.experimental.padCmdhelp (#342)
Codex caught the same accuracy issue on pad-web that exists in five
spots in this repo: prose described the handshake location as
"serverCapabilities.experimental.padCmdhelp", but per the MCP spec
the InitializeResult shape is
{ result: { capabilities: { experimental: { ... } } } }
There's no `serverCapabilities` field on the wire — `ServerCapabilities`
is the Go-side struct type name in mcp-go; the JSON tag is
`capabilities`. Anyone copying the path out of our docs to navigate
a real JSON-RPC envelope was getting the wrong key.
Updated to `capabilities.experimental.padCmdhelp` (or the fully
qualified `result.capabilities.experimental.padCmdhelp` where the
JSON-RPC envelope context wasn't otherwise obvious) in:
- README.md — public-facing prose
- CLAUDE.md — agent-facing prose
- internal/mcp/version.go — discovery-surfaces doc comment + the
experimentalCapabilityKey doc comment
- internal/mcp/server.go — comment near WithExperimental
- internal/mcp/meta.go — experimentalCapabilities() doc + the wire
shape example (now wrapped under `result` for accuracy)
- internal/mcp/server_test.go — test docstring + failure message
- cmd/pad/mcp.go — comment near RegisterMeta
The Go type `serverCapabilities` in `internal/server/handlers_capabilities.go`
is unrelated (it's the response shape for `GET /api/v1/server/capabilities`)
and stays as-is.
No code/behaviour changes; pure prose accuracy fix. `make check` clean.
Companion fix to pad-web PR #42, which Codex flagged the same issue on.
This commit is contained in:
@@ -191,7 +191,7 @@ Surface:
|
||||
- **Resources:** `pad://workspace/{ws}/items/{ref}`, `pad://workspace/{ws}/items`, `pad://workspace/{ws}/dashboard`, `pad://workspace/{ws}/collections`, plus the server-wide `pad://_meta/version`.
|
||||
- **Prompts:** `pad_plan`, `pad_ideate`, `pad_retro`, `pad_onboard` — multi-step workflows lifted from `skills/pad/SKILL.md`.
|
||||
|
||||
**Stability contract.** The cmdhelp tool surface is versioned via `internal/mcp.CmdhelpVersion` (currently `"0.1"`). Bump the major when tool names, argument shapes, or resource URIs change incompatibly — external agents (Cursor, Claude Desktop, the future Pad Cloud remote MCP) pin against it. The version is advertised on the wire two ways: under `serverCapabilities.experimental.padCmdhelp` in the initialize handshake, and as a JSON document at `pad://_meta/version`.
|
||||
**Stability contract.** The cmdhelp tool surface is versioned via `internal/mcp.CmdhelpVersion` (currently `"0.1"`). Bump the major when tool names, argument shapes, or resource URIs change incompatibly — external agents (Cursor, Claude Desktop, the future Pad Cloud remote MCP) pin against it. The version is advertised on the wire two ways: at `capabilities.experimental.padCmdhelp` in the initialize handshake, and as a JSON document at `pad://_meta/version`.
|
||||
|
||||
Code lives in `internal/mcp/` (built on `github.com/mark3labs/mcp-go`). Public docs at `getpad.dev/mcp/local`.
|
||||
|
||||
|
||||
@@ -246,7 +246,7 @@ pad mcp install claude-desktop # or: cursor, windsurf, --all
|
||||
```
|
||||
|
||||
The server advertises a tool-surface stability tier (`cmdhelp_version`)
|
||||
in the initialize handshake under `serverCapabilities.experimental.padCmdhelp`,
|
||||
in the initialize handshake at `capabilities.experimental.padCmdhelp`,
|
||||
and as a queryable JSON document at `pad://_meta/version`. External agents
|
||||
can pin against this so a future tool rename doesn't break them silently.
|
||||
|
||||
|
||||
+2
-2
@@ -300,8 +300,8 @@ Shuts down cleanly on EOF, SIGINT, or SIGTERM.`,
|
||||
|
||||
// Tool-surface stability metadata (TASK-963). Static
|
||||
// resource at pad://_meta/version. Complements the
|
||||
// handshake's serverCapabilities.experimental.padCmdhelp
|
||||
// for clients that prefer reading a JSON document.
|
||||
// handshake's capabilities.experimental.padCmdhelp for
|
||||
// clients that prefer reading a JSON document.
|
||||
mcpserver.RegisterMeta(srv.MCP(), fullVersion())
|
||||
|
||||
if err := srv.Run(cmd.Context()); err != nil {
|
||||
|
||||
+13
-10
@@ -95,19 +95,22 @@ func RegisterMeta(srv *server.MCPServer, padVersion string) {
|
||||
})
|
||||
}
|
||||
|
||||
// experimentalCapabilities returns the map advertised in the
|
||||
// initialize handshake's serverCapabilities.experimental field. Lets
|
||||
// clients discover the cmdhelp tier in one round-trip without reading
|
||||
// the meta resource.
|
||||
// experimentalCapabilities returns the map advertised at
|
||||
// capabilities.experimental in the initialize handshake's result
|
||||
// envelope. Lets clients discover the cmdhelp tier in one round-trip
|
||||
// without reading the meta resource.
|
||||
//
|
||||
// Wire shape:
|
||||
//
|
||||
// "capabilities": {
|
||||
// "experimental": {
|
||||
// "padCmdhelp": {
|
||||
// "version": "0.1",
|
||||
// "tool_surface_stable": true
|
||||
// }
|
||||
// "result": {
|
||||
// "capabilities": {
|
||||
// "experimental": {
|
||||
// "padCmdhelp": {
|
||||
// "version": "0.1",
|
||||
// "tool_surface_stable": true
|
||||
// }
|
||||
// },
|
||||
// ...
|
||||
// },
|
||||
// ...
|
||||
// }
|
||||
|
||||
@@ -59,10 +59,9 @@ func NewServer(opts Options) *Server {
|
||||
// subprocesses with arbitrary client-supplied args.
|
||||
server.WithRecovery(),
|
||||
// Tool-surface stability tier (TASK-963). Surfaces the cmdhelp
|
||||
// contract directly in the handshake under
|
||||
// serverCapabilities.experimental.padCmdhelp, so external
|
||||
// agents can detect compatibility without reading
|
||||
// pad://_meta/version first.
|
||||
// contract directly in the handshake at
|
||||
// capabilities.experimental.padCmdhelp, so external agents can
|
||||
// detect compatibility without reading pad://_meta/version first.
|
||||
server.WithExperimental(experimentalCapabilities()),
|
||||
)
|
||||
return &Server{mcp: mcp, debug: opts.Debug}
|
||||
|
||||
@@ -39,8 +39,8 @@ func TestNewServer_Construction(t *testing.T) {
|
||||
// TestServer_InitializeHandshake is the contract test: a real
|
||||
// initialize round-trip through the stdio pipeline must return
|
||||
// serverInfo with our canonical name + the version we passed in,
|
||||
// AND must advertise the cmdhelp tool-surface tier under
|
||||
// serverCapabilities.experimental.padCmdhelp.
|
||||
// AND must advertise the cmdhelp tool-surface tier at
|
||||
// capabilities.experimental.padCmdhelp in the initialize result.
|
||||
//
|
||||
// This is what every MCP client (Claude Desktop, Cursor, Windsurf,
|
||||
// mcp-inspector) sees on connect — if it regresses, downstream
|
||||
@@ -64,7 +64,7 @@ func TestServer_InitializeHandshake(t *testing.T) {
|
||||
// directly from the handshake.
|
||||
exp := res.Capabilities.Experimental
|
||||
if exp == nil {
|
||||
t.Fatalf("serverCapabilities.experimental missing — cmdhelp tier not advertised")
|
||||
t.Fatalf("capabilities.experimental missing — cmdhelp tier not advertised")
|
||||
}
|
||||
rawNS, ok := exp[experimentalCapabilityKey]
|
||||
if !ok {
|
||||
|
||||
@@ -30,9 +30,9 @@ const FallbackVersion = "0.0.0-dev"
|
||||
//
|
||||
// - "0.1" — initial cmdhelp-derived surface from PLAN-942.
|
||||
//
|
||||
// Discovery surfaces:
|
||||
// Discovery surfaces (paths into the JSON-RPC envelope):
|
||||
//
|
||||
// - serverCapabilities.experimental.padCmdhelp.version (handshake).
|
||||
// - result.capabilities.experimental.padCmdhelp.version (handshake).
|
||||
// - pad://_meta/version resource (queryable JSON document).
|
||||
const CmdhelpVersion = "0.1"
|
||||
|
||||
@@ -48,7 +48,8 @@ const MetaVersionURI = "pad://_meta/version"
|
||||
// reads it dynamically (see meta.go) so the value never drifts from
|
||||
// what the library actually advertises.
|
||||
|
||||
// experimentalCapabilityKey is the namespace under
|
||||
// serverCapabilities.experimental that carries the cmdhelp tier.
|
||||
// Namespaced so other servers' experimental capabilities don't collide.
|
||||
// experimentalCapabilityKey is the JSON object key under
|
||||
// capabilities.experimental that carries the cmdhelp tier in the
|
||||
// initialize handshake. Namespaced so other servers' experimental
|
||||
// capabilities don't collide.
|
||||
const experimentalCapabilityKey = "padCmdhelp"
|
||||
|
||||
Reference in New Issue
Block a user