mcp: typed custom methods and extension registration - #1031
Conversation
…egistry Builds on the custom JSON-RPC method support in modelcontextprotocol#956 in three ways: 1. **Bidirectionality** — the server can now send requests to the client, and the client can register handlers for them, mirroring the existing client→server direction: - AddServerSendingCustomMethod / ServerCallCustomMethod - AddClientReceivingCustomMethod - CustomMethod.RegisterServerSending / .ServerCall - CustomMethod.RegisterClientReceiving 2. **CustomMethod[P, R, T]** — a phantom-type wrapper that captures the method name and parameter/result types once at package level, so call sites never repeat generic arguments or method-name strings: var Method = mcp.NewCustomMethod[*Params, *Result]("acme/method") result, err := Method.Call(ctx, cs, &Params{...}) 3. **Extension registry** — a mechanism for libraries to auto-wire custom methods into every Server/Client without requiring callers to do any manual setup: - RegisterExtension (global, typically called from init) - ServerOptions.Extensions / ClientOptions.Extensions (per-instance) Global extensions are applied first; per-instance extensions after (last writer wins on name collision). The example is restructured to demonstrate the pattern: a latinext sub-package is the "extension author" (defines types, registers via init(), exports a Translate() helper); main.go is the "consumer" (just imports latinext, no generics or method-name strings visible). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
piyushbag
left a comment
There was a problem hiding this comment.
Looked through the extension registry and bidirectional custom-method wiring. The CustomMethod wrapper and latinext example make the #956 follow-up easy to follow.
Local check:
go test ./mcp/... -count=1
All green on my machine.
One non-blocking suggestion: TestCustomMethods only exercises client→server today. A small round-trip test for AddServerSendingCustomMethod + AddClientReceivingCustomMethod (or ServerCallCustomMethod) would lock in the new path.
|
@sambhav thank you for your contribution. |
gotcha, let me remove those bits. I think the rest of the syntactic sugar still helps though. |
Remove server-initiated custom method registration and calls as agreed in PR modelcontextprotocol#1031. Retain the typed CustomMethod wrapper with RegisterServer, RegisterClient, and Call, and consolidate extension application. Merge current main, refresh the Latin example, and test protocol versions, registration order and scope, error propagation, and concurrent extension registration.
Custom method consumers currently repeat the method name and generic types at registration and call sites. This follow-up to #956 packages those details once for extension authors.
As agreed in the review discussion, this PR now covers client-to-server custom methods only. The server-initiated APIs and their additional dispatch maps have been removed.
CustomMethodwraps the existing registration and call functions withRegisterServer,RegisterClient, andCall.RegisterExtensionsupports package-level setup;ServerOptions.ExtensionsandClientOptions.Extensionssupport per-instance setup. Global extensions run first, in registration order; later registrations of a custom method replace earlier ones. Existing instances are unaffected.Translatehelper.mainis merged and conflicts resolved.Validation on Go 1.26.1:
-race, including all supported protocol versions, typed-nil params, registration errors, handler errors, ordering, overrides, scope, and concurrent registration.-race.go build ./...,go vet ./..., formatting checks, and the Latin example pass.127.0.0.1instead of their wildcard listener address. This avoids the environment's proxy while preserving their Host headers and assertions; repository test files are unchanged.