Skip to content

Commit 0c7c54c

Browse files
committed
Define types and architecture for variant-aware MCP server
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 and ranking API - Implement default priority-based ranking with stable-first enforcement - Define ServerVariant, VariantHints, and related types per SEP spec - Document implementation plan for Server.Run() dispatch architecture Signed-off-by: Kurt Degiorgio <[email protected]>
1 parent 4f98e0a commit 0c7c54c

8 files changed

Lines changed: 576 additions & 7 deletions

File tree

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// Example: Setting up a variant-aware MCP server with multiple variants.
2+
package main
3+
4+
import (
5+
"context"
6+
"log"
7+
8+
mcp "github.com/modelcontextprotocol/go-sdk/mcp"
9+
10+
"github.com/modelcontextprotocol/experimental-ext-variants/go/sdk/variants"
11+
)
12+
13+
// -- Coding variant tool types ------------------------------------------------
14+
15+
type AnalyzeCodeInput struct {
16+
Code string `json:"code" jsonschema:"source code to analyze"`
17+
Language string `json:"language" jsonschema:"programming language"`
18+
}
19+
20+
type AnalyzeCodeOutput struct {
21+
Issues []string `json:"issues"`
22+
Suggestions []string `json:"suggestions"`
23+
}
24+
25+
func AnalyzeCode(_ context.Context, _ *mcp.CallToolRequest, in AnalyzeCodeInput) (*mcp.CallToolResult, AnalyzeCodeOutput, error) {
26+
return nil, AnalyzeCodeOutput{
27+
Issues: []string{"unused variable on line 3"},
28+
Suggestions: []string{"consider using a switch statement"},
29+
}, nil
30+
}
31+
32+
type RefactorInput struct {
33+
Code string `json:"code" jsonschema:"source code to refactor"`
34+
Action string `json:"action" jsonschema:"refactoring action, e.g. extract-function"`
35+
}
36+
37+
type RefactorOutput struct {
38+
Refactored string `json:"refactored"`
39+
}
40+
41+
func Refactor(_ context.Context, _ *mcp.CallToolRequest, in RefactorInput) (*mcp.CallToolResult, RefactorOutput, error) {
42+
return nil, RefactorOutput{Refactored: "// refactored\n" + in.Code}, nil
43+
}
44+
45+
// -- Compact variant tool types -----------------------------------------------
46+
47+
type SummarizeInput struct {
48+
Text string `json:"text" jsonschema:"text to summarize"`
49+
}
50+
51+
type SummarizeOutput struct {
52+
Summary string `json:"summary"`
53+
}
54+
55+
func Summarize(_ context.Context, _ *mcp.CallToolRequest, in SummarizeInput) (*mcp.CallToolResult, SummarizeOutput, error) {
56+
return nil, SummarizeOutput{Summary: in.Text[:min(len(in.Text), 50)]}, nil
57+
}
58+
59+
type LookupInput struct {
60+
Query string `json:"query" jsonschema:"lookup query"`
61+
}
62+
63+
type LookupOutput struct {
64+
Result string `json:"result"`
65+
}
66+
67+
func Lookup(_ context.Context, _ *mcp.CallToolRequest, in LookupInput) (*mcp.CallToolResult, LookupOutput, error) {
68+
return nil, LookupOutput{Result: "result for: " + in.Query}, nil
69+
}
70+
71+
// -----------------------------------------------------------------------------
72+
73+
func main() {
74+
// Server 1: coding-focused tools
75+
server1 := mcp.NewServer(&mcp.Implementation{Name: "my-server", Version: "v1.0.0"}, nil)
76+
mcp.AddTool(server1, &mcp.Tool{Name: "analyze_code", Description: "Perform static analysis on source code"}, AnalyzeCode)
77+
mcp.AddTool(server1, &mcp.Tool{Name: "refactor", Description: "Apply a refactoring action to source code"}, Refactor)
78+
79+
// Server 2: compact / minimal tools
80+
server2 := mcp.NewServer(&mcp.Implementation{Name: "my-server", Version: "v1.0.0"}, nil)
81+
mcp.AddTool(server2, &mcp.Tool{Name: "summarize", Description: "Summarize text"}, Summarize)
82+
mcp.AddTool(server2, &mcp.Tool{Name: "lookup", Description: "Quick fact lookup"}, Lookup)
83+
84+
coding := variants.ServerVariant{
85+
ID: "coding-assistant",
86+
Description: "Optimized for coding workflows",
87+
Hints: map[string]string{"useCase": "coding", "contextSize": "standard"},
88+
Status: variants.Stable,
89+
}
90+
91+
compact := variants.ServerVariant{
92+
ID: "compact",
93+
Description: "Minimal token usage",
94+
Hints: map[string]string{"contextSize": "small"},
95+
Status: variants.Experimental,
96+
}
97+
98+
vs := variants.NewServer().
99+
WithVariant(coding, server1, 0).
100+
WithVariant(compact, server2, 1).
101+
WithRanking(func(_ context.Context, _ variants.VariantHints, vs []variants.ServerVariant) []variants.ServerVariant {
102+
// rank based on client hints, return sorted
103+
// note: this is optional, defults to ranking by priority.
104+
return vs
105+
})
106+
107+
ctx := context.Background()
108+
if err := vs.Run(ctx, nil); err != nil {
109+
log.Fatal(err)
110+
}
111+
}

go/sdk/go.mod

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
11
module github.com/modelcontextprotocol/experimental-ext-variants/go/sdk
22

3-
go 1.23.0
3+
go 1.23.0
4+
5+
toolchain go1.24.3
6+
7+
require github.com/modelcontextprotocol/go-sdk v1.2.0
8+
9+
require (
10+
github.com/google/jsonschema-go v0.3.0 // indirect
11+
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
12+
golang.org/x/oauth2 v0.30.0 // indirect
13+
)

go/sdk/go.sum

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
2+
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
3+
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
4+
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
5+
github.com/google/jsonschema-go v0.3.0 h1:6AH2TxVNtk3IlvkkhjrtbUc4S8AvO0Xii0DxIygDg+Q=
6+
github.com/google/jsonschema-go v0.3.0/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
7+
github.com/modelcontextprotocol/go-sdk v1.2.0 h1:Y23co09300CEk8iZ/tMxIX1dVmKZkzoSBZOpJwUnc/s=
8+
github.com/modelcontextprotocol/go-sdk v1.2.0/go.mod h1:6fM3LCm3yV7pAs8isnKLn07oKtB0MP9LHd3DfAcKw10=
9+
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
10+
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
11+
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
12+
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
13+
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
14+
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=

go/sdk/variants/ranking.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2025 The MCP Variants Authors. All rights reserved.
2+
// Use of this source code is governed by a Apache-2.0
3+
// license that can be found in the LICENSE file.
4+
5+
package variants
6+
7+
import (
8+
"context"
9+
"slices"
10+
)
11+
12+
// defaultRankingFunc is the built-in ranking function used when no custom
13+
// RankingFunc is provided. It sorts variants by priority (lowest first),
14+
// using stable-before-experimental-before-deprecated as a tiebreaker.
15+
func defaultRankingFunc(_ context.Context, _ VariantHints, vs []ServerVariant) []ServerVariant {
16+
ranked := slices.Clone(vs)
17+
slices.SortStableFunc(ranked, func(a, b ServerVariant) int {
18+
if a.Priority() != b.Priority() {
19+
return a.Priority() - b.Priority()
20+
}
21+
return statusWeight(a.Status) - statusWeight(b.Status)
22+
})
23+
return ranked
24+
}
25+
26+
// statusWeight returns a sort weight for a VariantStatus.
27+
// Lower is better: stable < experimental < deprecated.
28+
func statusWeight(s VariantStatus) int {
29+
switch s {
30+
case Stable, "":
31+
return 0
32+
case Experimental:
33+
return 1
34+
case Deprecated:
35+
return 2
36+
default:
37+
return 3
38+
}
39+
}

0 commit comments

Comments
 (0)