Skip to content

Commit 040fbec

Browse files
tadasantclaude
andcommitted
refactor: Simplify remote URL templating using 'variables' array
Replace the complex arguments/environmentVariables approach with a clean 'variables' array that directly maps variable names to {curly_braces} in URLs. Key changes: - Replace 'arguments' and 'environmentVariables' with single 'variables' array - Update Transport struct to include Variables field - Fix Go validators to support template variables in remote URLs - Update example to use path-based templating for better namespace validation - All validation and schema checks now pass This provides a much cleaner API: variables array contains KeyValueInput objects where the 'name' field maps directly to {variable_name} in the URL. Example: { "url": "https://api.example.github.io/mcp/{tenant_id}", "variables": [{"name": "tenant_id", "description": "Tenant ID", "isRequired": true}] } 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 416a87b commit 040fbec

File tree

24 files changed

+127
-115
lines changed

24 files changed

+127
-115
lines changed

cmd/publisher/commands/init.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ func createServerJSON(
309309
URL: repoURL,
310310
Source: repoSource,
311311
},
312-
Version: version,
312+
Version: version,
313313
Packages: []model.Package{pkg},
314314
}
315315
}

cmd/publisher/commands/logout.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ func LogoutCommand() error {
1313
}
1414

1515
tokenPath := filepath.Join(homeDir, TokenFileName)
16-
16+
1717
// Check if token file exists
1818
if _, err := os.Stat(tokenPath); os.IsNotExist(err) {
1919
_, _ = fmt.Fprintln(os.Stdout, "Not logged in")
@@ -30,7 +30,7 @@ func LogoutCommand() error {
3030
".mcpregistry_github_token",
3131
".mcpregistry_registry_token",
3232
}
33-
33+
3434
for _, file := range legacyFiles {
3535
path := filepath.Join(homeDir, file)
3636
if _, err := os.Stat(path); err == nil {
@@ -40,4 +40,4 @@ func LogoutCommand() error {
4040

4141
_, _ = fmt.Fprintln(os.Stdout, "✓ Successfully logged out")
4242
return nil
43-
}
43+
}

cmd/publisher/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,4 @@ func printUsage() {
6767
_, _ = fmt.Fprintln(os.Stdout, " publish Publish server.json to the registry")
6868
_, _ = fmt.Fprintln(os.Stdout)
6969
_, _ = fmt.Fprintln(os.Stdout, "Use 'mcp-publisher <command> --help' for more information about a command.")
70-
}
70+
}

deploy/pkg/k8s/ingress.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func SetupIngressController(ctx *pulumi.Context, cluster *providers.ProviderInfo
5151
Values: pulumi.Map{
5252
"controller": pulumi.Map{
5353
"service": pulumi.Map{
54-
"type": serviceType,
54+
"type": serviceType,
5555
"annotations": pulumi.Map{},
5656
},
5757
"config": pulumi.Map{

deploy/pkg/providers/gcp/provider.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,19 @@ type Provider struct{}
2222
// createGCPProvider creates a GCP provider with explicit credentials if configured
2323
func createGCPProvider(ctx *pulumi.Context, name string) (*gcp.Provider, error) {
2424
gcpConf := config.New(ctx, "gcp")
25-
25+
2626
// Get project ID from config
2727
projectID := gcpConf.Get("project")
2828
if projectID == "" {
2929
return nil, fmt.Errorf("GCP project ID not configured. Set gcp:project")
3030
}
31-
31+
3232
// Get region from config or use default
3333
region := gcpConf.Get("region")
3434
if region == "" {
3535
region = "us-central1"
3636
}
37-
37+
3838
// Get credentials from config (base64 encoded service account JSON)
3939
credentials := gcpConf.Get("credentials")
4040
if credentials != "" {
@@ -45,7 +45,7 @@ func createGCPProvider(ctx *pulumi.Context, name string) (*gcp.Provider, error)
4545
}
4646
credentials = string(decodedCreds)
4747
}
48-
48+
4949
// Create a GCP provider with explicit credentials if provided
5050
if credentials != "" {
5151
return gcp.NewProvider(ctx, name, &gcp.ProviderArgs{
@@ -54,7 +54,7 @@ func createGCPProvider(ctx *pulumi.Context, name string) (*gcp.Provider, error)
5454
Credentials: pulumi.String(credentials),
5555
})
5656
}
57-
57+
5858
return nil, nil
5959
}
6060

docs/reference/server-json/generic-server-json.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -682,12 +682,11 @@ This example demonstrates URL templating for remote servers, useful for multi-te
682682
"remotes": [
683683
{
684684
"type": "streamable-http",
685-
"url": "https://{tenant_host}/api/mcp",
686-
"arguments": [
685+
"url": "https://api.example.github.io/mcp/{tenant_id}",
686+
"variables": [
687687
{
688-
"type": "positional",
689-
"valueHint": "tenant_host",
690-
"description": "Tenant-specific hostname (e.g., 'us-cell1.example.com')",
688+
"name": "tenant_id",
689+
"description": "Tenant identifier (e.g., 'us-cell1', 'emea-cell1')",
691690
"isRequired": true
692691
}
693692
]
@@ -696,7 +695,7 @@ This example demonstrates URL templating for remote servers, useful for multi-te
696695
}
697696
```
698697

699-
Clients would configure the tenant host value, and the `{tenant_host}` variable in the URL gets replaced with the provided argument value to connect to the appropriate deployment (e.g., `us-cell1.example.com` or `emea.example.com`).
698+
Clients configure the tenant identifier, and the `{tenant_id}` variable in the URL gets replaced with the provided variable value to connect to the appropriate tenant endpoint (e.g., `https://api.example.github.io/mcp/us-cell1` or `https://api.example.github.io/mcp/emea-cell1`).
700699

701700
### Deprecated Server Example
702701

docs/reference/server-json/server.schema.json

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@
377377
},
378378
"url": {
379379
"type": "string",
380-
"description": "URL template for the streamable-http transport. Variables in {curly_braces} reference argument valueHints from 'arguments' array or names from 'environmentVariables' array. If arguments/environmentVariables are not provided, {curly_braces} should be treated as literal text. After variable substitution, this should produce a valid URI.",
380+
"description": "URL template for the streamable-http transport. Variables in {curly_braces} reference variable names from the 'variables' array. If variables are not provided, {curly_braces} should be treated as literal text. After variable substitution, this should produce a valid URI.",
381381
"example": "https://api.example.com/mcp"
382382
},
383383
"headers": {
@@ -387,16 +387,9 @@
387387
"$ref": "#/definitions/KeyValueInput"
388388
}
389389
},
390-
"arguments": {
390+
"variables": {
391391
"type": "array",
392-
"description": "Arguments that can be referenced in URL template {curly_braces} via their valueHint property",
393-
"items": {
394-
"$ref": "#/definitions/Argument"
395-
}
396-
},
397-
"environmentVariables": {
398-
"type": "array",
399-
"description": "Environment variables that can be referenced in URL template {curly_braces} via their name property",
392+
"description": "Configuration variables that can be referenced in URL template {curly_braces} via their name property",
400393
"items": {
401394
"$ref": "#/definitions/KeyValueInput"
402395
}
@@ -421,7 +414,7 @@
421414
"url": {
422415
"type": "string",
423416
"format": "uri",
424-
"description": "Server-Sent Events endpoint URL template. Variables in {curly_braces} reference argument valueHints from 'arguments' array or names from 'environmentVariables' array. If arguments/environmentVariables are not provided, {curly_braces} should be treated as literal text. After variable substitution, this should produce a valid URI.",
417+
"description": "Server-Sent Events endpoint URL template. Variables in {curly_braces} reference variable names from the 'variables' array. If variables are not provided, {curly_braces} should be treated as literal text. After variable substitution, this should produce a valid URI.",
425418
"example": "https://mcp-fs.example.com/sse"
426419
},
427420
"headers": {
@@ -431,16 +424,9 @@
431424
"$ref": "#/definitions/KeyValueInput"
432425
}
433426
},
434-
"arguments": {
435-
"type": "array",
436-
"description": "Arguments that can be referenced in URL template {curly_braces} via their valueHint property",
437-
"items": {
438-
"$ref": "#/definitions/Argument"
439-
}
440-
},
441-
"environmentVariables": {
427+
"variables": {
442428
"type": "array",
443-
"description": "Environment variables that can be referenced in URL template {curly_braces} via their name property",
429+
"description": "Configuration variables that can be referenced in URL template {curly_braces} via their name property",
444430
"items": {
445431
"$ref": "#/definitions/KeyValueInput"
446432
}

internal/api/handlers/v0/publish.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,14 @@ func buildPermissionErrorMessage(attemptedResource string, permissions []auth.Pe
7575
permissionStrs = append(permissionStrs, perm.ResourcePattern)
7676
}
7777
}
78-
78+
7979
errorMsg := "You do not have permission to publish this server"
8080
if len(permissionStrs) > 0 {
8181
errorMsg += ". You have permission to publish: " + strings.Join(permissionStrs, ", ")
8282
} else {
8383
errorMsg += ". You do not have any publish permissions"
8484
}
8585
errorMsg += ". Attempting to publish: " + attemptedResource
86-
86+
8787
return errorMsg
8888
}

internal/api/handlers/v0/publish_integration_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,9 @@ func TestPublishIntegration(t *testing.T) {
160160

161161
t.Run("publish fails with invalid token", func(t *testing.T) {
162162
publishReq := apiv0.ServerJSON{
163-
Name: "io.github.domdomegg/test-server",
164-
Description: "Test server",
165-
Version: "1.0.0",
163+
Name: "io.github.domdomegg/test-server",
164+
Description: "Test server",
165+
Version: "1.0.0",
166166
}
167167

168168
body, err := json.Marshal(publishReq)
@@ -183,7 +183,7 @@ func TestPublishIntegration(t *testing.T) {
183183
publishReq := apiv0.ServerJSON{
184184
Name: "io.github.other/test-server",
185185
Description: "A test server",
186-
Version: "1.0.0",
186+
Version: "1.0.0",
187187
Repository: model.Repository{
188188
URL: "https://github.com/example/test-server",
189189
Source: "github",
@@ -219,8 +219,8 @@ func TestPublishIntegration(t *testing.T) {
219219
publishReq := apiv0.ServerJSON{
220220
Name: "io.github.domdomegg/airtable-mcp-server",
221221
Description: "A test server with MCPB package",
222-
Version: "1.7.2",
223-
Status: model.StatusActive,
222+
Version: "1.7.2",
223+
Status: model.StatusActive,
224224
Packages: []model.Package{
225225
{
226226
RegistryType: model.RegistryTypeMCPB,

internal/api/handlers/v0/publish_registry_validation_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func TestPublishRegistryValidation(t *testing.T) {
4747
publishReq := apiv0.ServerJSON{
4848
Name: "com.example/test-server-with-npm",
4949
Description: "A test server with invalid npm package reference",
50-
Version: "1.0.0",
50+
Version: "1.0.0",
5151
Packages: []model.Package{
5252
{
5353
RegistryType: model.RegistryTypeNPM,
@@ -88,7 +88,7 @@ func TestPublishRegistryValidation(t *testing.T) {
8888
publishReq := apiv0.ServerJSON{
8989
Name: "com.example/test-server-mcpb-validation",
9090
Description: "A test server with MCPB package and registry validation enabled",
91-
Version: "0.0.36",
91+
Version: "0.0.36",
9292
Packages: []model.Package{
9393
{
9494
RegistryType: model.RegistryTypeMCPB,
@@ -138,7 +138,7 @@ func TestPublishRegistryValidation(t *testing.T) {
138138
publishReq := apiv0.ServerJSON{
139139
Name: "com.example/test-server-multiple-packages",
140140
Description: "A test server with multiple packages where second fails",
141-
Version: "1.0.0",
141+
Version: "1.0.0",
142142
Packages: []model.Package{
143143
{
144144
RegistryType: model.RegistryTypeMCPB,
@@ -189,7 +189,7 @@ func TestPublishRegistryValidation(t *testing.T) {
189189
publishReq := apiv0.ServerJSON{
190190
Name: "com.example/test-server-first-package-fails",
191191
Description: "A test server where first package fails",
192-
Version: "1.0.0",
192+
Version: "1.0.0",
193193
Packages: []model.Package{
194194
{
195195
RegistryType: model.RegistryTypeNPM,

0 commit comments

Comments
 (0)