Skip to content

Commit 662957f

Browse files
Copilottoby
andcommitted
Complete create command implementation with package arguments and updated documentation
Co-authored-by: toby <[email protected]>
1 parent 1ec3e19 commit 662957f

File tree

3 files changed

+183
-10
lines changed

3 files changed

+183
-10
lines changed

publisher

5.39 KB
Binary file not shown.

tools/publisher/README.md

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,23 @@ The compiled binary will be placed in the `bin` directory.
1818

1919
## Usage
2020

21+
The tool supports two main commands:
22+
23+
### Publishing a server
24+
2125
```bash
2226
# Basic usage
23-
./bin/mcp-publisher -registry-url <REGISTRY_URL> -mcp-file <PATH_TO_MCP_FILE>
27+
./bin/mcp-publisher publish -registry-url <REGISTRY_URL> -mcp-file <PATH_TO_MCP_FILE>
2428

2529
# Force a new login even if a token exists
26-
./bin/mcp-publisher -registry-url <REGISTRY_URL> -mcp-file <PATH_TO_MCP_FILE> -login
30+
./bin/mcp-publisher publish -registry-url <REGISTRY_URL> -mcp-file <PATH_TO_MCP_FILE> -login
31+
```
32+
33+
### Creating a server.json file
34+
35+
```bash
36+
# Create a new server.json file
37+
./bin/mcp-publisher create --name "io.github.owner/repo" --description "My server" --repo-url "https://github.com/owner/repo"
2738
```
2839

2940
### Command-line Arguments
@@ -33,6 +44,87 @@ The compiled binary will be placed in the `bin` directory.
3344
- `-login`: Force a new GitHub authentication even if a token already exists (overwrites existing token file)
3445
- `-auth-method`: Authentication method to use (default: github-oauth)
3546

47+
## Creating a server.json file
48+
49+
The tool provides a `create` command to help generate a properly formatted `server.json` file. This command takes various flags to specify the server details and generates a complete server.json file that you can then modify as needed.
50+
51+
### Usage
52+
53+
```bash
54+
./bin/mcp-publisher create [flags]
55+
```
56+
57+
### Create Command Flags
58+
59+
#### Required Flags
60+
- `--name`, `-n`: Server name (e.g., io.github.owner/repo-name)
61+
- `--description`, `-d`: Server description
62+
- `--repo-url`: Repository URL
63+
64+
#### Optional Flags
65+
- `--version`, `-v`: Server version (default: "1.0.0")
66+
- `--repo-source`: Repository source (default: "github")
67+
- `--output`, `-o`: Output file path (default: "server.json")
68+
- `--execute`, `-e`: Command to execute the server (generates runtime arguments)
69+
- `--registry`: Package registry name (default: "npm")
70+
- `--package-name`: Package name (defaults to server name)
71+
- `--package-version`: Package version (defaults to server version)
72+
- `--runtime-hint`: Runtime hint (e.g., "docker")
73+
- `--env-var`: Environment variable in format NAME:DESCRIPTION (can be repeated)
74+
- `--package-arg`: Package argument in format VALUE:DESCRIPTION (can be repeated)
75+
76+
### Create Examples
77+
78+
#### Basic NPX Server
79+
80+
```bash
81+
./bin/mcp-publisher create \
82+
--name "io.github.example/my-server" \
83+
--description "My MCP server" \
84+
--repo-url "https://github.com/example/my-server" \
85+
--execute "npx @example/my-server --verbose" \
86+
--env-var "API_KEY:Your API key for the service"
87+
```
88+
89+
#### Docker Server
90+
91+
```bash
92+
./bin/mcp-publisher create \
93+
--name "io.github.example/docker-server" \
94+
--description "Docker-based MCP server" \
95+
--repo-url "https://github.com/example/docker-server" \
96+
--runtime-hint "docker" \
97+
--execute "docker run --mount type=bind,src=/data,dst=/app/data example/server" \
98+
--env-var "CONFIG_PATH:Path to configuration file"
99+
```
100+
101+
#### Server with Package Arguments
102+
103+
```bash
104+
./bin/mcp-publisher create \
105+
--name "io.github.example/full-server" \
106+
--description "Complete server example" \
107+
--repo-url "https://github.com/example/full-server" \
108+
--execute "npx @example/server" \
109+
--package-arg "-s:Specify services and permissions" \
110+
--package-arg "--config:Configuration file path" \
111+
--env-var "API_KEY:Service API key" \
112+
--env-var "DEBUG:Enable debug mode"
113+
```
114+
115+
The `create` command will generate a `server.json` file with:
116+
- Proper structure and formatting
117+
- Runtime arguments parsed from the `--execute` command
118+
- Environment variables with descriptions
119+
- Package arguments for user configuration
120+
- All necessary metadata
121+
122+
After creation, you may need to manually edit the file to:
123+
- Adjust argument descriptions and requirements
124+
- Set environment variable optionality (`is_required`, `is_secret`)
125+
- Add remote server configurations
126+
- Fine-tune runtime and package arguments
127+
36128
## Authentication
37129

38130
The tool has been simplified to use **GitHub OAuth device flow authentication exclusively**. Previous versions supported multiple authentication methods, but this version focuses solely on GitHub OAuth for better security and user experience.
@@ -45,7 +137,9 @@ The tool has been simplified to use **GitHub OAuth device flow authentication ex
45137

46138
**Note**: Authentication is performed via GitHub OAuth App, which you must authorize for the respective resources (e.g., organization access if publishing organization repositories).
47139

48-
## Example
140+
## Publishing Example
141+
142+
To publish an existing server.json file to the registry:
49143

50144
1. Prepare your `server.json` file with your server details:
51145

@@ -90,7 +184,7 @@ The tool has been simplified to use **GitHub OAuth device flow authentication ex
90184
2. Run the publisher tool:
91185

92186
```bash
93-
./bin/mcp-publisher --registry-url "https://mcp-registry.example.com" --mcp-file "./server.json"
187+
./bin/mcp-publisher publish --registry-url "https://mcp-registry.example.com" --mcp-file "./server.json"
94188
```
95189

96190
3. Follow the authentication instructions in the terminal if prompted.

tools/publisher/main.go

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ type Package struct {
4747
Version string `json:"version"`
4848
RuntimeHint string `json:"runtime_hint,omitempty"`
4949
RuntimeArguments []RuntimeArgument `json:"runtime_arguments,omitempty"`
50+
PackageArguments []RuntimeArgument `json:"package_arguments,omitempty"`
5051
EnvironmentVariables []EnvironmentVariable `json:"environment_variables,omitempty"`
5152
}
5253

@@ -170,6 +171,7 @@ func createCommand() {
170171

171172
// Repeatable flags
172173
var envVars []string
174+
var packageArgs []string
173175

174176
createFlags.StringVar(&name, "name", "", "Server name (e.g., io.github.owner/repo-name) (required)")
175177
createFlags.StringVar(&name, "n", "", "Server name (shorthand)")
@@ -194,6 +196,12 @@ func createCommand() {
194196
envVars = append(envVars, value)
195197
return nil
196198
})
199+
200+
// Custom flag for package arguments
201+
createFlags.Func("package-arg", "Package argument in format VALUE:DESCRIPTION (can be repeated)", func(value string) error {
202+
packageArgs = append(packageArgs, value)
203+
return nil
204+
})
197205

198206
createFlags.Parse(os.Args[2:])
199207

@@ -217,7 +225,7 @@ func createCommand() {
217225
}
218226

219227
// Create server structure
220-
server := createServerStructure(name, description, version, repoURL, repoSource, registryName, packageName, packageVersion, runtimeHint, execute, envVars)
228+
server := createServerStructure(name, description, version, repoURL, repoSource, registryName, packageName, packageVersion, runtimeHint, execute, envVars, packageArgs)
221229

222230
// Convert to JSON
223231
jsonData, err := json.MarshalIndent(server, "", " ")
@@ -292,7 +300,7 @@ func publishToRegistry(registryURL string, mcpData []byte, token string) error {
292300
return nil
293301
}
294302

295-
func createServerStructure(name, description, version, repoURL, repoSource, registryName, packageName, packageVersion, runtimeHint, execute string, envVars []string) ServerJSON {
303+
func createServerStructure(name, description, version, repoURL, repoSource, registryName, packageName, packageVersion, runtimeHint, execute string, envVars []string, packageArgs []string) ServerJSON {
296304
// Parse environment variables
297305
var environmentVariables []EnvironmentVariable
298306
for _, envVar := range envVars {
@@ -311,16 +319,50 @@ func createServerStructure(name, description, version, repoURL, repoSource, regi
311319
}
312320
}
313321

322+
// Parse package arguments
323+
var packageArguments []RuntimeArgument
324+
for i, pkgArg := range packageArgs {
325+
parts := strings.SplitN(pkgArg, ":", 2)
326+
value := parts[0]
327+
description := fmt.Sprintf("Package argument %d", i+1)
328+
if len(parts) == 2 {
329+
description = parts[1]
330+
}
331+
332+
packageArguments = append(packageArguments, RuntimeArgument{
333+
Description: description,
334+
IsRequired: true, // Package arguments are typically required
335+
Format: "string",
336+
Value: value,
337+
Default: value,
338+
Type: "positional",
339+
ValueHint: value,
340+
})
341+
}
342+
314343
// Parse execute command to create runtime arguments
315344
var runtimeArguments []RuntimeArgument
316345
if execute != "" {
317-
// Split the execute command into parts
318-
parts := strings.Fields(execute)
346+
// Split the execute command into parts, handling quoted strings
347+
parts := smartSplit(execute)
319348
if len(parts) > 1 {
320-
// Add each argument as a runtime argument
349+
// Skip the first part (command) and add each argument as a runtime argument
321350
for i, arg := range parts[1:] {
351+
description := fmt.Sprintf("Runtime argument %d", i+1)
352+
353+
// Try to provide better descriptions based on common patterns
354+
if strings.HasPrefix(arg, "--") {
355+
description = fmt.Sprintf("Command line flag: %s", arg)
356+
} else if strings.HasPrefix(arg, "-") && len(arg) == 2 {
357+
description = fmt.Sprintf("Command line option: %s", arg)
358+
} else if strings.Contains(arg, "=") {
359+
description = fmt.Sprintf("Configuration parameter: %s", arg)
360+
} else if i > 0 && strings.HasPrefix(parts[i], "-") {
361+
description = fmt.Sprintf("Value for %s", parts[i])
362+
}
363+
322364
runtimeArguments = append(runtimeArguments, RuntimeArgument{
323-
Description: fmt.Sprintf("Runtime argument %d", i+1),
365+
Description: description,
324366
IsRequired: false,
325367
Format: "string",
326368
Value: arg,
@@ -339,6 +381,7 @@ func createServerStructure(name, description, version, repoURL, repoSource, regi
339381
Version: packageVersion,
340382
RuntimeHint: runtimeHint,
341383
RuntimeArguments: runtimeArguments,
384+
PackageArguments: packageArguments,
342385
EnvironmentVariables: environmentVariables,
343386
}
344387

@@ -356,3 +399,39 @@ func createServerStructure(name, description, version, repoURL, repoSource, regi
356399
Packages: []Package{pkg},
357400
}
358401
}
402+
403+
// smartSplit splits a command string into parts, handling quoted strings and common shell patterns
404+
func smartSplit(command string) []string {
405+
var parts []string
406+
var current strings.Builder
407+
var inQuotes bool
408+
var quoteChar rune
409+
410+
for _, char := range command {
411+
switch {
412+
case char == '"' || char == '\'':
413+
if !inQuotes {
414+
inQuotes = true
415+
quoteChar = char
416+
} else if char == quoteChar {
417+
inQuotes = false
418+
quoteChar = 0
419+
} else {
420+
current.WriteRune(char)
421+
}
422+
case char == ' ' && !inQuotes:
423+
if current.Len() > 0 {
424+
parts = append(parts, current.String())
425+
current.Reset()
426+
}
427+
default:
428+
current.WriteRune(char)
429+
}
430+
}
431+
432+
if current.Len() > 0 {
433+
parts = append(parts, current.String())
434+
}
435+
436+
return parts
437+
}

0 commit comments

Comments
 (0)