mcp: bidirectional custom methods, CustomMethod type, and extension registry#1031
Open
sambhav wants to merge 1 commit into
Open
mcp: bidirectional custom methods, CustomMethod type, and extension registry#1031sambhav wants to merge 1 commit into
sambhav wants to merge 1 commit into
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
approved these changes
Jun 28, 2026
piyushbag
left a comment
Contributor
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.
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.
This PR builds on the custom JSON-RPC method support added in #956, addressing the review comments I left there. It adds three related capabilities.
What's new
1. Bidirectional custom methods
#956 added client→server only. This PR adds the server→client direction:
Under the hood:
Servergains asendMethodsmap (parallel to its existingreceiveMethods);Clientgains areceiveMethodsmap (parallel to its existingsendMethods). The per-session dispatch functions (sendingMethodInfos/receivingMethodInfos) now read from these maps under the server/client mutex rather than returning the static global maps.2.
CustomMethod[P, R, T]— ergonomic type-safe wrapperExtension authors typically define a package-level var:
This captures the name and types once. Every registration and call site then uses the var rather than repeating generic type arguments or the method-name string:
The lower-level
Add*/Call*free functions remain exported for callers who don't want the wrapper.3. Extension registry
A library can now package a custom method as an extension — importers get everything wired automatically:
Two registration scopes:
RegisterExtension): process-wide, typically called frominit. Applied first.ServerOptions.Extensions/ClientOptions.Extensions): applied after global, so per-instance wins on collision. Useful for tests or programs that instantiate multiple servers with different behaviors.Example restructure
The
examples/server/custom-methodexample is rewritten to demonstrate the pattern:latinext/— the extension-author package: types,Methodvar,init()registration,Translate()helpermain.go— the consumer: just importslatinext;NewServer/NewClientneed no manual setupDesign notes
CustomMethodis a phantom-type struct —P,R,Tappear only in the type parameters, not as stored fields. This is purely a compile-time ergonomics device.Extension.Server/Extension.Clientare independently nullable — an extension can be server-only or client-only.NewServer/NewClientpanic, matching the pattern established byAddReceivingMiddlewareand similar setup calls.runExtensionsis a generic helper shared between the global-registry path and the per-instance path to avoid duplicating the nil-check + call + panic loop.cc @guglielmo-san — tagging you as the author of #956 and SDK maintainer.
Closes the review comments from #956.