feat(server): add mcpToolNameResolver helper for signature auth#916
Merged
feat(server): add mcpToolNameResolver helper for signature auth#916
Conversation
The RFC 9421 signing composition helpers (`verifySignatureAsAuthenticator`, `requireSignatureWhenPresent`, `requireAuthenticatedOrSigned`) all take a `resolveOperation` callback that, for MCP agents, parses `req.rawBody` as JSON-RPC and returns `params.name` when `method === 'tools/call'`. Every adapter ships the same 6-line parser; the SDK JSDoc inlines it four separate times. Export `mcpToolNameResolver` so adapter authors pass `resolveOperation: mcpToolNameResolver` instead of copy-pasting the parser. A2A agents use a different envelope — bespoke resolvers still apply there. JSDoc examples in `auth-signature.ts` are rewritten to use the helper. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2 tasks
This was referenced Apr 24, 2026
bokelley
added a commit
that referenced
this pull request
Apr 24, 2026
…edOrSigned Apply the review feedback on #914 against the SDK ergonomics shipped in #916 (mcpToolNameResolver) and #917 (defaults + createAgentSignedFetch preset): SIGNING-GUIDE.md - Step 3 (buyer-side): lead with createAgentSignedFetch one-call preset for the single-seller case; keep buildAgentSigningFetch as the multi-seller fallback. Drops the verbose CapabilityCache wiring from the headline example. - Step 4 (seller-side, Express middleware): drop explicit InMemoryReplayStore / InMemoryRevocationStore (now default per #917). Use mcpToolNameResolver instead of the inlined JSON-RPC parser. Add a one-liner about swapping in shared stores for horizontally scaled fleets. - Step 4 (composing with bearer auth): replace the three-variable manual composition (signatureAuth / bearerAuth / requireSignatureWhenPresent) with requireAuthenticatedOrSigned in one call. Now also enforces the spec's request_signature_required 401 via requiredFor: [...MUTATING_TASKS] — previously absent from the example. BUILD-AN-AGENT.md - Mirror the requireAuthenticatedOrSigned + mcpToolNameResolver + MUTATING_TASKS pattern in the Request Signing snippet so the two guides agree. Functional surface unchanged; the manual composition still works for callers that need finer control. The recommended path is just shorter.
bokelley
added a commit
that referenced
this pull request
Apr 25, 2026
* docs: add request signing guide and surface it from README + BUILD-AN-AGENT Adds docs/guides/SIGNING-GUIDE.md covering the full RFC 9421 signing workflow: key generation, JWKS/brand.json publication, client-side signing, server-side verification, webhook signing, capability declaration, key rotation, and conformance testing. Updates README.md to link the guide from the AI Agents section and rewrites the Security > Request Signing section with better orientation. Adds a Request Signing section to BUILD-AN-AGENT.md showing requireSignatureWhenPresent composition and webhook signer config. * chore: add empty changeset for docs-only PR * docs(signing): collapse manual auth composition to requireAuthenticatedOrSigned Apply the review feedback on #914 against the SDK ergonomics shipped in #916 (mcpToolNameResolver) and #917 (defaults + createAgentSignedFetch preset): SIGNING-GUIDE.md - Step 3 (buyer-side): lead with createAgentSignedFetch one-call preset for the single-seller case; keep buildAgentSigningFetch as the multi-seller fallback. Drops the verbose CapabilityCache wiring from the headline example. - Step 4 (seller-side, Express middleware): drop explicit InMemoryReplayStore / InMemoryRevocationStore (now default per #917). Use mcpToolNameResolver instead of the inlined JSON-RPC parser. Add a one-liner about swapping in shared stores for horizontally scaled fleets. - Step 4 (composing with bearer auth): replace the three-variable manual composition (signatureAuth / bearerAuth / requireSignatureWhenPresent) with requireAuthenticatedOrSigned in one call. Now also enforces the spec's request_signature_required 401 via requiredFor: [...MUTATING_TASKS] — previously absent from the example. BUILD-AN-AGENT.md - Mirror the requireAuthenticatedOrSigned + mcpToolNameResolver + MUTATING_TASKS pattern in the Request Signing snippet so the two guides agree. Functional surface unchanged; the manual composition still works for callers that need finer control. The recommended path is just shorter. * fix(docs+signing): apply expert review on signing guide Six must-fix items raised by code-reviewer + ad-tech-protocol-expert on the post-polish state: 1. MUTATING_TASKS imports were wrong — only @adcp/client (root) exports it, not @adcp/client/server. Buyer copies would have compile-errored. Split the imports. 2. Capability override key is `request_signing`, not `signed_requests`. Wrong key gets silently dropped — verifier wires up but get_adcp_capabilities advertises nothing, so buyers don't sign. Fixed in both guides; add a clarifying sentence so the trap is visible. 3. Widen `mcpToolNameResolver` parameter type from `IncomingMessage & { rawBody?: string }` to `{ rawBody?: string }`. Function only reads rawBody, so widening lets it satisfy both the `verifySignatureAsAuthenticator` (IncomingMessage-shaped) and `createExpressVerifier` (ExpressLike-shaped) call sites without casts. No runtime change. 4. Error code table was wrong — `missing_signature` / `invalid_signature` etc. don't exist. The real codes (cross-checked against compliance/cache/3.0.0/test-vectors/ request-signing/negative/) are `request_signature_*`. Replace the seven-row table with the full 15-row table from the vectors and note that they're a separate signature-error namespace surfaced via WWW-Authenticate (not entries in enums/error-code.json). 5. Signature coverage misstated. content-digest is conditional on covers_content_digest, not always covered. Reword. 6. brand.json shape was wrong. agents[] requires {type, url, id}; `capabilities` and `adcp_use` arrays don't exist in schemas/cache/3.0.0/brand.json. Fix the example, list the `type` enum. Also reframed the "mandatory in 3.1+" claim to "4.0, spend-committing operations" per schemas/cache/3.0.0/protocol/get-adcp-capabilities-response.json, and softened `requiredFor: [...MUTATING_TASKS]` to a narrow example with MUTATING_TASKS as the upper bound (the spec stance in 3.0 is "empty by default; populate selectively per counterparty"). Conformance vector counts (12 positive + 27 negative = 39) verified against the disk layout. --------- Co-authored-by: Brian O'Kelley <bokelley@scope3.com>
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
mcpToolNameResolverfrom@adcp/client/server— a defaultresolveOperationcallback for MCP agents wiring RFC 9421 signature auth. Parsesreq.rawBodyas JSON-RPC and returnsparams.namewhenmethod === 'tools/call'.src/lib/server/auth-signature.tsto use the helper instead of copy-pasting the same 6-line parser.tools/callmethod, missing params, non-string name, malformed JSON, missing/empty rawBody).Motivation
The RFC 9421 signing composition helpers (
verifySignatureAsAuthenticator,requireSignatureWhenPresent,requireAuthenticatedOrSigned) take aresolveOperationcallback whose MCP implementation is identical across every seller agent:That block appears four times in
auth-signature.tsJSDoc alone, plus once verbatim intest/auth-signature-compose.test.js, plus every time it's pasted into a new adapter. Ships as a one-line import instead:A2A agents use a different envelope and still need a bespoke resolver — the helper is explicitly MCP.
Follow-up to #914 (docs-only signing guide): once this lands I'll propose a rewrite of Step 4 of
SIGNING-GUIDE.mdusingrequireAuthenticatedOrSigned+mcpToolNameResolver, collapsing the ~25-line manual composition to ~8 lines.Test plan
node --test test/lib/mcp-tool-name-resolver.test.js(7/7 pass locally)auth-signature-compose.test.jsstill green (37/37)npm run buildcleantsc --noEmitclean (ran in pre-push)🤖 Generated with Claude Code