Skip to content

Commit e1c17e0

Browse files
authored
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>
1 parent c01fd18 commit e1c17e0

16 files changed

Lines changed: 1218 additions & 147 deletions

File tree

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,34 @@ This repository provides a multi-language reference implementation of the varian
1313
| TypeScript | `typescript/sdk/` | `@ext-modelcontextprotocol/variants` | Planned |
1414

1515

16+
## Examples (Go)
17+
18+
The Go implementation includes runnable example servers under [`go/sdk/examples/server/`](go/sdk/examples/server/):
19+
20+
### [`model-optimized/`](go/sdk/examples/server/model-optimized/)
21+
22+
Same tools, different descriptions per LLM family.
23+
24+
https://github.com/user-attachments/assets/5bb60cd2-291f-4940-abfe-f53852d35470
25+
26+
### [`github/`](go/sdk/examples/server/github/)
27+
28+
Different tool sets per variant with custom ranking.
29+
30+
https://github.com/user-attachments/assets/d770ac55-e988-4d89-bdf8-bcb3cf1e2e53
31+
32+
### [`research/`](go/sdk/examples/server/research/)
33+
34+
Context budget management via description verbosity.
35+
36+
### [`trading/`](go/sdk/examples/server/trading/)
37+
38+
API versioning, lifecycle statuses, and deprecation info.
39+
40+
### [`variants-stdio/`](go/sdk/examples/server/variants-stdio/)
41+
42+
Minimal single-variant setup.
43+
1644
## CI/CD
1745

1846
This monorepo uses **path-based CI workflows** to efficiently test only what changes:

go/sdk/README.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,7 @@ In **stateful mode** (default, stdio and HTTP), per-session inner connections ar
138138

139139
## Examples
140140

141-
See [`examples/server/`](examples/server/) for runnable examples:
142-
143-
- [`variants/`](examples/server/variants/) — stdio transport (single client)
144-
- [`variants-http/`](examples/server/variants-http/) — HTTP transport (multiple concurrent clients)
141+
See [`examples/server/`](examples/server/) for runnable examples.
145142

146143
## API
147144

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# GitHub Developer Platform
2+
3+
A variant-aware MCP server that exposes different tool sets for different agent types, mirroring the GitHub MCP Server pattern described in SEP-2053's Prior Art section.
4+
5+
**Pattern demonstrated:** Different tool sets per variant with custom ranking.
6+
7+
## Variants
8+
9+
| Variant | Tools | Description |
10+
|---|---|---|
11+
| `code-review` | `list_pull_requests`, `get_diff`, `add_review_comment` | PR operations and code review |
12+
| `project-management` | `list_issues`, `create_issue`, `add_label` | Issue tracking and labels |
13+
| `security-readonly` | `list_security_alerts`, `get_advisory` | Security scanning (read-only) |
14+
| `ci-automation` | `list_workflow_runs`, `trigger_workflow` | CI/CD workflow management |
15+
16+
## Custom Ranking
17+
18+
Clients send a `"domain"` hint during initialization. The ranking function boosts variants whose `domain` hint matches the client's requested domain, falling back to priority order.
19+
20+
## Run
21+
22+
```bash
23+
go run ./examples/server/github
24+
```
25+
26+
Connect any MCP client to `http://localhost:8080`.
27+
28+
## Demo
29+
30+
See [mcp-inspector-variants-demo.mp4](mcp-inspector-variants-demo.mp4) for a walkthrough using MCP Inspector.

go/sdk/examples/server/variants-http/main.go renamed to go/sdk/examples/server/github/main.go

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
// Example: A developer productivity platform (à la GitHub) that exposes
2-
// different tool sets for different agent types using MCP server variants,
3-
// served over HTTP for multiple concurrent clients.
1+
// Example: GitHub MCP Server — a developer productivity platform that exposes
2+
// different tool sets for different agent types using MCP server variants.
3+
// This mirrors the GitHub MCP Server pattern described in SEP-2053's Prior Art
4+
// section, where a single server exposes code review, project management,
5+
// security, and CI/CD tools as separate variants with custom ranking.
6+
//
7+
// Capability demonstrated: Different tool sets per variant, custom ranking.
48
//
59
// Variants:
610
// - code-review: PR operations, diffs, and review comments
@@ -10,18 +14,20 @@
1014
//
1115
// Run:
1216
//
13-
// go run .
17+
// go run ./examples/server/github
1418
//
1519
// Then connect any MCP client to http://localhost:8080.
1620
package main
1721

1822
import (
23+
"context"
1924
"log"
2025
"net/http"
26+
"slices"
27+
"strings"
2128

2229
"github.com/modelcontextprotocol/go-sdk/mcp"
2330

24-
"github.com/modelcontextprotocol/experimental-ext-variants/go/sdk/examples/server/exampletools"
2531
"github.com/modelcontextprotocol/experimental-ext-variants/go/sdk/variants"
2632
)
2733

@@ -31,52 +37,52 @@ func main() {
3137
mcp.AddTool(codeReviewServer, &mcp.Tool{
3238
Name: "list_pull_requests",
3339
Description: "List open pull requests, optionally filtered by author",
34-
}, exampletools.ListPullRequests)
40+
}, listPullRequests)
3541
mcp.AddTool(codeReviewServer, &mcp.Tool{
3642
Name: "get_diff",
3743
Description: "Get the diff for a pull request, including changed files and line counts",
38-
}, exampletools.GetDiff)
44+
}, getDiff)
3945
mcp.AddTool(codeReviewServer, &mcp.Tool{
4046
Name: "add_review_comment",
4147
Description: "Post a review comment on a specific line of a pull request",
42-
}, exampletools.AddReviewComment)
48+
}, addReviewComment)
4349

4450
// Project management variant: issue-focused tools for PM agents
4551
pmServer := mcp.NewServer(&mcp.Implementation{Name: "devplatform", Version: "v1.0.0"}, nil)
4652
mcp.AddTool(pmServer, &mcp.Tool{
4753
Name: "list_issues",
4854
Description: "List issues, optionally filtered by state and labels",
49-
}, exampletools.ListIssues)
55+
}, listIssues)
5056
mcp.AddTool(pmServer, &mcp.Tool{
5157
Name: "create_issue",
5258
Description: "Create a new issue with title, body, and optional labels",
53-
}, exampletools.CreateIssue)
59+
}, createIssue)
5460
mcp.AddTool(pmServer, &mcp.Tool{
5561
Name: "add_label",
5662
Description: "Add labels to an existing issue",
57-
}, exampletools.AddLabel)
63+
}, addLabel)
5864

5965
// Security variant: read-only security scanning tools
6066
securityServer := mcp.NewServer(&mcp.Implementation{Name: "devplatform", Version: "v1.0.0"}, nil)
6167
mcp.AddTool(securityServer, &mcp.Tool{
6268
Name: "list_security_alerts",
6369
Description: "List code scanning alerts for a repository",
64-
}, exampletools.ListSecurityAlerts)
70+
}, listSecurityAlerts)
6571
mcp.AddTool(securityServer, &mcp.Tool{
6672
Name: "get_advisory",
6773
Description: "Get details of a security advisory",
68-
}, exampletools.GetAdvisory)
74+
}, getAdvisory)
6975

7076
// CI/CD variant: workflow management tools for automation agents
7177
ciServer := mcp.NewServer(&mcp.Implementation{Name: "devplatform", Version: "v1.0.0"}, nil)
7278
mcp.AddTool(ciServer, &mcp.Tool{
7379
Name: "list_workflow_runs",
7480
Description: "List recent workflow runs for a repository",
75-
}, exampletools.ListWorkflowRuns)
81+
}, listWorkflowRuns)
7682
mcp.AddTool(ciServer, &mcp.Tool{
7783
Name: "trigger_workflow",
7884
Description: "Trigger a workflow dispatch event",
79-
}, exampletools.TriggerWorkflow)
85+
}, triggerWorkflow)
8086

8187
vs := variants.NewServer(&mcp.Implementation{Name: "devplatform", Version: "v1.0.0"}).
8288
WithVariant(variants.ServerVariant{
@@ -102,7 +108,23 @@ func main() {
102108
Description: "CI/CD workflow management. Trigger runs, monitor jobs, manage deployments designed for automation agents.",
103109
Hints: map[string]string{"domain": "ci-cd", "accessLevel": "automation"},
104110
Status: variants.Stable,
105-
}, ciServer, 3)
111+
}, ciServer, 3).
112+
// Custom ranking: boost variants whose "domain" hint matches the client's.
113+
WithRanking(func(_ context.Context, hints variants.VariantHints, vs []variants.ServerVariant) []variants.ServerVariant {
114+
requested, _ := variants.HintValue[string](hints, "domain")
115+
slices.SortStableFunc(vs, func(a, b variants.ServerVariant) int {
116+
aMatch := strings.Contains(strings.ToLower(a.Hints["domain"]), strings.ToLower(requested))
117+
bMatch := strings.Contains(strings.ToLower(b.Hints["domain"]), strings.ToLower(requested))
118+
if aMatch != bMatch {
119+
if aMatch {
120+
return -1
121+
}
122+
return 1
123+
}
124+
return a.Priority() - b.Priority()
125+
})
126+
return vs
127+
})
106128

107129
handler := variants.NewStreamableHTTPHandler(vs, nil)
108130

go/sdk/examples/server/exampletools/tools.go renamed to go/sdk/examples/server/github/tools.go

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,4 @@
1-
// Package exampletools defines the shared tool types and handlers used by the
2-
// variant-aware MCP server examples. The tools simulate a developer
3-
// productivity platform (à la GitHub/GitLab) with four variants:
4-
//
5-
// - code-review: PR operations, diffs, and review comments
6-
// - project-management: issue tracking, labels, and assignments
7-
// - security-readonly: security scanning alerts and advisories (read-only)
8-
// - ci-automation: CI/CD workflow management and dispatch
9-
package exampletools
1+
package main
102

113
import (
124
"context"
@@ -34,7 +26,7 @@ type ListPullRequestsOutput struct {
3426
PullRequests []PullRequest `json:"pullRequests"`
3527
}
3628

37-
func ListPullRequests(_ context.Context, _ *mcp.CallToolRequest, in ListPullRequestsInput) (*mcp.CallToolResult, ListPullRequestsOutput, error) {
29+
func listPullRequests(_ context.Context, _ *mcp.CallToolRequest, in ListPullRequestsInput) (*mcp.CallToolResult, ListPullRequestsOutput, error) {
3830
return nil, ListPullRequestsOutput{
3931
PullRequests: []PullRequest{
4032
{Number: 42, Title: "Add retry logic to API client", Author: "alice", State: "open"},
@@ -56,7 +48,7 @@ type GetDiffOutput struct {
5648
Deletions int `json:"deletions"`
5749
}
5850

59-
func GetDiff(_ context.Context, _ *mcp.CallToolRequest, in GetDiffInput) (*mcp.CallToolResult, GetDiffOutput, error) {
51+
func getDiff(_ context.Context, _ *mcp.CallToolRequest, in GetDiffInput) (*mcp.CallToolResult, GetDiffOutput, error) {
6052
return nil, GetDiffOutput{
6153
Diff: "--- a/client.go\n+++ b/client.go\n@@ -45,6 +45,12 @@\n+ for attempt := 0; attempt < maxRetries; attempt++ {\n+ resp, err := c.do(req)\n+ if err == nil { return resp, nil }\n+ time.Sleep(backoff(attempt))\n+ }",
6254
Files: []string{"client.go", "client_test.go"},
@@ -78,7 +70,7 @@ type AddReviewCommentOutput struct {
7870
URL string `json:"url"`
7971
}
8072

81-
func AddReviewComment(_ context.Context, _ *mcp.CallToolRequest, in AddReviewCommentInput) (*mcp.CallToolResult, AddReviewCommentOutput, error) {
73+
func addReviewComment(_ context.Context, _ *mcp.CallToolRequest, in AddReviewCommentInput) (*mcp.CallToolResult, AddReviewCommentOutput, error) {
8274
return nil, AddReviewCommentOutput{
8375
CommentID: 1001,
8476
URL: fmt.Sprintf("https://github.com/%s/pull/%d#discussion_r1001", in.Repo, in.Number),
@@ -105,7 +97,7 @@ type ListIssuesOutput struct {
10597
Issues []Issue `json:"issues"`
10698
}
10799

108-
func ListIssues(_ context.Context, _ *mcp.CallToolRequest, in ListIssuesInput) (*mcp.CallToolResult, ListIssuesOutput, error) {
100+
func listIssues(_ context.Context, _ *mcp.CallToolRequest, in ListIssuesInput) (*mcp.CallToolResult, ListIssuesOutput, error) {
109101
return nil, ListIssuesOutput{
110102
Issues: []Issue{
111103
{Number: 101, Title: "API rate limiting returns wrong status code", State: "open", Labels: []string{"bug", "api"}, Assignee: "alice"},
@@ -127,7 +119,7 @@ type CreateIssueOutput struct {
127119
URL string `json:"url"`
128120
}
129121

130-
func CreateIssue(_ context.Context, _ *mcp.CallToolRequest, in CreateIssueInput) (*mcp.CallToolResult, CreateIssueOutput, error) {
122+
func createIssue(_ context.Context, _ *mcp.CallToolRequest, in CreateIssueInput) (*mcp.CallToolResult, CreateIssueOutput, error) {
131123
return nil, CreateIssueOutput{
132124
Number: 102,
133125
URL: fmt.Sprintf("https://github.com/%s/issues/102", in.Repo),
@@ -144,7 +136,7 @@ type AddLabelOutput struct {
144136
Labels []string `json:"currentLabels"`
145137
}
146138

147-
func AddLabel(_ context.Context, _ *mcp.CallToolRequest, in AddLabelInput) (*mcp.CallToolResult, AddLabelOutput, error) {
139+
func addLabel(_ context.Context, _ *mcp.CallToolRequest, in AddLabelInput) (*mcp.CallToolResult, AddLabelOutput, error) {
148140
return nil, AddLabelOutput{
149141
Labels: append([]string{"bug", "api"}, in.Labels...),
150142
}, nil
@@ -170,7 +162,7 @@ type ListSecurityAlertsOutput struct {
170162
Alerts []SecurityAlert `json:"alerts"`
171163
}
172164

173-
func ListSecurityAlerts(_ context.Context, _ *mcp.CallToolRequest, in ListSecurityAlertsInput) (*mcp.CallToolResult, ListSecurityAlertsOutput, error) {
165+
func listSecurityAlerts(_ context.Context, _ *mcp.CallToolRequest, in ListSecurityAlertsInput) (*mcp.CallToolResult, ListSecurityAlertsOutput, error) {
174166
return nil, ListSecurityAlertsOutput{
175167
Alerts: []SecurityAlert{
176168
{Number: 1, Rule: "sql-injection", Severity: "critical", State: "open", Path: "src/db/query.go"},
@@ -193,7 +185,7 @@ type GetAdvisoryOutput struct {
193185
PatchedIn string `json:"patchedIn"`
194186
}
195187

196-
func GetAdvisory(_ context.Context, _ *mcp.CallToolRequest, in GetAdvisoryInput) (*mcp.CallToolResult, GetAdvisoryOutput, error) {
188+
func getAdvisory(_ context.Context, _ *mcp.CallToolRequest, in GetAdvisoryInput) (*mcp.CallToolResult, GetAdvisoryOutput, error) {
197189
return nil, GetAdvisoryOutput{
198190
ID: in.AdvisoryID,
199191
Summary: "Remote code execution via crafted request payload",
@@ -224,7 +216,7 @@ type ListWorkflowRunsOutput struct {
224216
Runs []WorkflowRun `json:"runs"`
225217
}
226218

227-
func ListWorkflowRuns(_ context.Context, _ *mcp.CallToolRequest, in ListWorkflowRunsInput) (*mcp.CallToolResult, ListWorkflowRunsOutput, error) {
219+
func listWorkflowRuns(_ context.Context, _ *mcp.CallToolRequest, in ListWorkflowRunsInput) (*mcp.CallToolResult, ListWorkflowRunsOutput, error) {
228220
return nil, ListWorkflowRunsOutput{
229221
Runs: []WorkflowRun{
230222
{ID: 5001, Workflow: "ci.yml", Status: "completed", Conclusion: "success", Branch: "main"},
@@ -246,7 +238,7 @@ type TriggerWorkflowOutput struct {
246238
URL string `json:"url"`
247239
}
248240

249-
func TriggerWorkflow(_ context.Context, _ *mcp.CallToolRequest, in TriggerWorkflowInput) (*mcp.CallToolResult, TriggerWorkflowOutput, error) {
241+
func triggerWorkflow(_ context.Context, _ *mcp.CallToolRequest, in TriggerWorkflowInput) (*mcp.CallToolResult, TriggerWorkflowOutput, error) {
250242
return nil, TriggerWorkflowOutput{
251243
RunID: 5004,
252244
URL: fmt.Sprintf("https://github.com/%s/actions/runs/5004", in.Repo),

0 commit comments

Comments
 (0)