Commit graph experimental-ext-variants/go
Author SHA1 Message Date
Sambhav Kothari
cfc05d6f5e
fix: merge Extensions in unionCapabilities (#9)
Some checks failed
Go CI / Linting (push) Has been cancelled
Go CI / Unit Tests (push) Has been cancelled
Go CI / Unit Tests-1 (push) Has been cancelled
Go CI / Race Detection (push) Has been cancelled
## Summary

`unionCapabilities` merges `Experimental` map entries from inner variant
servers but skips the `Extensions` field entirely. This means any
extension registered via `ServerCapabilities.AddExtension()` on an inner
server (e.g. `io.modelcontextprotocol/skills`) is silently dropped from
the front proxy's `InitializeResult`.

- Add the same first-wins merge logic for `Extensions` that already
exists for `Experimental`
- Bump `go-sdk` to v1.4.1 which introduced the `Extensions` field on
`ServerCapabilities`
- Add unit test for `unionCapabilities` extensions merging
- Add integration test verifying extensions survive the variant path
end-to-end

## Test plan

- [x] `TestUnionCapabilities_MergesExtensions` — verifies first-wins
merge semantics for extensions
- [x] `TestIntegration_ExtensionsAdvertised` — verifies extensions
registered on inner servers appear in the client's `InitializeResult`
- [x] All existing tests pass (27/27)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-29 09:17:04 +01:00
Steven Chen
b95923d172
variants: simplify dispatch and session by reducing method boilerplate (#8)
Simplify variant dispatch and session by replacing per-method
boilerplate with generic handlers backed by reflect.

## Motivation and Context
The dispatcher and backend session had separate handler functions for
every MCP method (ListTools, ListResources, CallTool, Subscribe, etc.),
each following an identical pattern: extract params, inject metadata,
call inner handler, cast result. This meant ~250 lines of near-identical
code across dispatch.go and session.go, and every new MCP method
required adding another copy.

## How Has This Been Tested?
All existing unit and integration tests pass (go test -v ./...)

## Breaking Changes
None. All changes are internal to the variants package. Public API is
unchanged.

## Types of changes
Refactor (non-breaking change which fixes an issue)
2026-05-01 12:29:10 +01:00
Steven Chen
e5e8f117e8
variants: redirect inner server sending methods to fix dropping notifications in stateless mode (#7)
Registers a sending middleware on each inner server that intercepts all
outgoing messages and routes them through the front session using a
session-swapped request wrapper. Replaces the previous proxy client
handler approach which dropped log messages in stateless mode.

## Motivation and Context
Tool handlers in variant servers use req.Session (the inner session) to
send notifications. Previously, log messages were silently dropped in
stateless mode because the inner session is not aware of the front
session.
This change injects the sendingMethodHandler from the frontSession into
the context and adds a sending middleware to redirect all sending
requests to the frontSession handler directly.
A few considerations in this implementation:
* Having frontSession in the context allows notifications to be sent to
the correct frontSession, when the same backend session could be used by
multiple front sessions.
* This solution will work for synchronous notifications that happen
within the same context (progress, log, sampling, elicitation) but not
working for asynchronous notifications (tool list changed, resource
updated etc), because asynchronous notifications are sent with a new
context.
* All the operations before the sending method will still happen in the
backend. Most notable impact is in stateless mode, requests from one
client could change the log level for other clients. For this reason, I
default the logging level to debug for now.
* This implementation relies on the MCP Go SDK sendingMethodHandler
taking a generic `Request` not one that tied to specific function. Based
on the current SDK implementation this is unlikely to change.
* The same generic method forwarding mechanism could work for receiving
methods too, but with some caveat. That can be addressed in separate PR.

## How Has This Been Tested?
TestNotificationDelivery — verifies progress and log messages arrive at
the front clients with multiple clients making concurrent request.

New tests added for context preservation that was added in
https://github.com/modelcontextprotocol/experimental-ext-variants/pull/6.
Context preservation is required to make the notification forwarding
work anyway. Since we use the context to carry frontSession handler to
the backend session.

All existing tests pass

## Breaking Changes
None. Tool handlers continue to use req.Session normally.

## Types of changes
Bug fix (non-breaking change which fixes an issue)
2026-03-23 15:39:57 +00:00
Steven Chen
150c110128
Preserve context.Context across variant dispatch by bypassing in-memory transport (#6)
## Summary
- Switched variant dispatch from `ClientSession` RPC calls to direct
inner server handler invocation via a new `backendSession` type,
preserving `context.Context` values (e.g. `RequestContext`) that were
previously lost crossing the in-memory transport boundary
- Added `backendSession` abstraction in `session.go` that mirrors the
`mcp.ClientSession` API but calls the inner server's handler chain
directly, forwarding `RequestExtra` (token info, HTTP headers, etc.)
- Captured the inner server's handler chain via receiving middleware
(`captureMCPMethodHandler`) in `backend.go`, reused across all
connections
- Simplified `innerConnection` to hold `*backendSession` + cleanup;
in-memory transport/client kept alive only for notification forwarding
(progress, logging)
- Refactored `getSession` → `getConnection` returning `*innerConnection`
to reduce indirection in dispatch code
- Moved `injectVariantMeta` calls to after type assertion and nil checks
in all dispatch handlers for consistency and nil-safety

## Test plan
- [x] Existing variant dispatch tests pass (`go test ./variants/...`)
- [x] Verify context values (e.g. `RequestContext`) are available in
tool handlers when invoked through variant dispatch
- [x] Verify `RequestExtra` fields (token info, HTTP headers) are
forwarded to inner server handlers
2026-03-18 10:53:06 +00:00
Kurt Degiorgio
e1c17e047f
Add example servers for SEP-2053 variant patterns (#4)
Add four new example servers demonstrating different SEP-2053 variant
patterns:

- model-optimized (same tools, different descriptions per LLM family)
- trading (API versioning with deprecation lifecycle)
- research (context budget management)
- Refactor the existing variants-http example into a GitHub-style server
with custom ranking.

Signed-off-by: Kurt Degiorgio <kdegiorgio@bloomberg.net>
2026-03-01 12:42:43 +05:30
Kurt Degiorgio
c01fd188d2
Implement variant-aware MCP server (#3)
Summary

Builds on the types and architecture from the previous PR to deliver a
working variant-aware MCP
server that routes requests to inner mcp.Server instances based on
per-request _meta variant selection,
enabling a single MCP endpoint to serve multiple tool/resource/prompt
variants simultaneously.
  
  ```go
 // Create standard mcp.Server instances — one per variant
codeReviewServer := mcp.NewServer(&mcp.Implementation{Name:
"devplatform", Version: "v1.0.0"}, nil)
mcp.AddTool(codeReviewServer, &mcp.Tool{Name: "list_pull_requests",
...}, handler)
mcp.AddTool(codeReviewServer, &mcp.Tool{Name: "get_diff", ...}, handler)

pmServer := mcp.NewServer(&mcp.Implementation{Name: "devplatform",
Version: "v1.0.0"}, nil)
  mcp.AddTool(pmServer, &mcp.Tool{Name: "list_issues", ...}, handler)
  mcp.AddTool(pmServer, &mcp.Tool{Name: "create_issue", ...}, handler)

  // Register them as variants on a single server
vs := variants.NewServer(&mcp.Implementation{Name: "devplatform",
Version: "v1.0.0"}).
      WithVariant(variants.ServerVariant{
          ID:          "code-review",
          Description: "PR and code review operations",
          Status:      variants.Stable,
      }, codeReviewServer, 0).
      WithVariant(variants.ServerVariant{
          ID:          "project-management",
          Description: "Issue and project tracking operations",
          Status:      variants.Stable,
      }, pmServer, 1)

      // Future: register variants backed by remote MCP servers
// WithHTTPVariant(v, mcpServer, priority) — variant backed by an
mcp.Server exposed over HTTP
// WithRemoteVariant(v, "https://...", priority) — variant backed by a
remote MCP endpoint

  // Serve over HTTP for multiple concurrent clients
  handler := variants.NewStreamableHTTPHandler(vs, nil)
  http.ListenAndServe(":8080", handler)

  // Or serve over stdio for single-client use:
  // vs.Run(ctx, &mcp.StdioTransport{})
```
  

  Key additions

  - Backend abstraction (inMemoryBackend) — creates in-memory transport pairs between the variant server and
   inner mcp.Server instances, with notification forwarding for progress and logging events.
  - Request dispatcher (dispatch.go) — handles routing for tools/list, tools/call, resources/list,
  resources/read, resources/subscribe, prompts/list, prompts/get, and completion/complete methods.
  Implements variant-scoped cursor wrapping to correctly paginate across multiple inner servers.
  - Session middleware (session.go) — manages per-session inner connections in stateful mode and shared
  connections in stateless HTTP mode, ensuring proper lifecycle management.
  - Initialize enrichment — injects availableVariants into the server's capabilities response so clients can
   discover which variants are available.
  - Capability discovery — merges inner server capabilities via union to present a unified capability set to
   clients.
  - NewStreamableHTTPHandler — provides an HTTP handler for multi-client serving over Streamable HTTP
  transport.
  - Integration tests (server_test.go, 458 lines) — covers end-to-end variant selection, backward
  compatibility (no variant specified), HTTP transport, and stateless modes.
  - Examples — stdio-based (examples/server/variants/main.go) and HTTP-based
  (examples/server/variants-http/main.go) example servers demonstrating the API.
  - README — rewritten with API documentation and architecture diagrams.


TODO (subsequent PRs):
- Unit Testing 
- More integration tests

Signed-off-by: Kurt Degiorgio <kdegiorgio@bloomberg.net>
2026-02-18 12:57:21 +00:00
Kurt Degiorgio
dbf99bebf0
Define types and architecture for a variant-aware MCP server (#2)
Defines the core types and API for server variants extension, laying
groundwork for multi-variant server implementation. Server.Run() is not
  yet implemented but architecture is designed and documented.

  - Add Server type with variant registration 
  - Implement default priority-based ranking 
  - Define ServerVariant, VariantHints, and related types per SEP spec
  - Add comprehensive example demonstrating intended multi-variant usage
  - Document implementation plan for Server.Run() dispatch architecture
  
  
  Next PRs:
  
  -  Unit Tests
  -  Implementation for Server.Run
  -  Conformance Testing

Signed-off-by: Kurt Degiorgio <kdegiorgio@bloomberg.net>
2026-02-11 23:36:11 +00:00
Kurt Degiorgio
4f98e0aab3
Merge pull request #1 from modelcontextprotocol/chore/scaffolding
Initial project structure for MCP variants
2026-01-27 20:10:28 +00:00