Complete create command implementation with package arguments and updated documentation
Co-authored-by: toby <83556+toby@users.noreply.github.com>
This commit is contained in:
parent
1ec3e19dcf
commit
662957fa7f
2 changed files with 183 additions and 10 deletions
|
|
@ -18,12 +18,23 @@ The compiled binary will be placed in the `bin` directory.
|
|||
|
||||
## Usage
|
||||
|
||||
The tool supports two main commands:
|
||||
|
||||
### Publishing a server
|
||||
|
||||
```bash
|
||||
# Basic usage
|
||||
./bin/mcp-publisher -registry-url <REGISTRY_URL> -mcp-file <PATH_TO_MCP_FILE>
|
||||
./bin/mcp-publisher publish -registry-url <REGISTRY_URL> -mcp-file <PATH_TO_MCP_FILE>
|
||||
|
||||
# Force a new login even if a token exists
|
||||
./bin/mcp-publisher -registry-url <REGISTRY_URL> -mcp-file <PATH_TO_MCP_FILE> -login
|
||||
./bin/mcp-publisher publish -registry-url <REGISTRY_URL> -mcp-file <PATH_TO_MCP_FILE> -login
|
||||
```
|
||||
|
||||
### Creating a server.json file
|
||||
|
||||
```bash
|
||||
# Create a new server.json file
|
||||
./bin/mcp-publisher create --name "io.github.owner/repo" --description "My server" --repo-url "https://github.com/owner/repo"
|
||||
```
|
||||
|
||||
### Command-line Arguments
|
||||
|
|
@ -33,6 +44,87 @@ The compiled binary will be placed in the `bin` directory.
|
|||
- `-login`: Force a new GitHub authentication even if a token already exists (overwrites existing token file)
|
||||
- `-auth-method`: Authentication method to use (default: github-oauth)
|
||||
|
||||
## Creating a server.json file
|
||||
|
||||
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.
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
./bin/mcp-publisher create [flags]
|
||||
```
|
||||
|
||||
### Create Command Flags
|
||||
|
||||
#### Required Flags
|
||||
- `--name`, `-n`: Server name (e.g., io.github.owner/repo-name)
|
||||
- `--description`, `-d`: Server description
|
||||
- `--repo-url`: Repository URL
|
||||
|
||||
#### Optional Flags
|
||||
- `--version`, `-v`: Server version (default: "1.0.0")
|
||||
- `--repo-source`: Repository source (default: "github")
|
||||
- `--output`, `-o`: Output file path (default: "server.json")
|
||||
- `--execute`, `-e`: Command to execute the server (generates runtime arguments)
|
||||
- `--registry`: Package registry name (default: "npm")
|
||||
- `--package-name`: Package name (defaults to server name)
|
||||
- `--package-version`: Package version (defaults to server version)
|
||||
- `--runtime-hint`: Runtime hint (e.g., "docker")
|
||||
- `--env-var`: Environment variable in format NAME:DESCRIPTION (can be repeated)
|
||||
- `--package-arg`: Package argument in format VALUE:DESCRIPTION (can be repeated)
|
||||
|
||||
### Create Examples
|
||||
|
||||
#### Basic NPX Server
|
||||
|
||||
```bash
|
||||
./bin/mcp-publisher create \
|
||||
--name "io.github.example/my-server" \
|
||||
--description "My MCP server" \
|
||||
--repo-url "https://github.com/example/my-server" \
|
||||
--execute "npx @example/my-server --verbose" \
|
||||
--env-var "API_KEY:Your API key for the service"
|
||||
```
|
||||
|
||||
#### Docker Server
|
||||
|
||||
```bash
|
||||
./bin/mcp-publisher create \
|
||||
--name "io.github.example/docker-server" \
|
||||
--description "Docker-based MCP server" \
|
||||
--repo-url "https://github.com/example/docker-server" \
|
||||
--runtime-hint "docker" \
|
||||
--execute "docker run --mount type=bind,src=/data,dst=/app/data example/server" \
|
||||
--env-var "CONFIG_PATH:Path to configuration file"
|
||||
```
|
||||
|
||||
#### Server with Package Arguments
|
||||
|
||||
```bash
|
||||
./bin/mcp-publisher create \
|
||||
--name "io.github.example/full-server" \
|
||||
--description "Complete server example" \
|
||||
--repo-url "https://github.com/example/full-server" \
|
||||
--execute "npx @example/server" \
|
||||
--package-arg "-s:Specify services and permissions" \
|
||||
--package-arg "--config:Configuration file path" \
|
||||
--env-var "API_KEY:Service API key" \
|
||||
--env-var "DEBUG:Enable debug mode"
|
||||
```
|
||||
|
||||
The `create` command will generate a `server.json` file with:
|
||||
- Proper structure and formatting
|
||||
- Runtime arguments parsed from the `--execute` command
|
||||
- Environment variables with descriptions
|
||||
- Package arguments for user configuration
|
||||
- All necessary metadata
|
||||
|
||||
After creation, you may need to manually edit the file to:
|
||||
- Adjust argument descriptions and requirements
|
||||
- Set environment variable optionality (`is_required`, `is_secret`)
|
||||
- Add remote server configurations
|
||||
- Fine-tune runtime and package arguments
|
||||
|
||||
## Authentication
|
||||
|
||||
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
|
|||
|
||||
**Note**: Authentication is performed via GitHub OAuth App, which you must authorize for the respective resources (e.g., organization access if publishing organization repositories).
|
||||
|
||||
## Example
|
||||
## Publishing Example
|
||||
|
||||
To publish an existing server.json file to the registry:
|
||||
|
||||
1. Prepare your `server.json` file with your server details:
|
||||
|
||||
|
|
@ -90,7 +184,7 @@ The tool has been simplified to use **GitHub OAuth device flow authentication ex
|
|||
2. Run the publisher tool:
|
||||
|
||||
```bash
|
||||
./bin/mcp-publisher --registry-url "https://mcp-registry.example.com" --mcp-file "./server.json"
|
||||
./bin/mcp-publisher publish --registry-url "https://mcp-registry.example.com" --mcp-file "./server.json"
|
||||
```
|
||||
|
||||
3. Follow the authentication instructions in the terminal if prompted.
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ type Package struct {
|
|||
Version string `json:"version"`
|
||||
RuntimeHint string `json:"runtime_hint,omitempty"`
|
||||
RuntimeArguments []RuntimeArgument `json:"runtime_arguments,omitempty"`
|
||||
PackageArguments []RuntimeArgument `json:"package_arguments,omitempty"`
|
||||
EnvironmentVariables []EnvironmentVariable `json:"environment_variables,omitempty"`
|
||||
}
|
||||
|
||||
|
|
@ -170,6 +171,7 @@ func createCommand() {
|
|||
|
||||
// Repeatable flags
|
||||
var envVars []string
|
||||
var packageArgs []string
|
||||
|
||||
createFlags.StringVar(&name, "name", "", "Server name (e.g., io.github.owner/repo-name) (required)")
|
||||
createFlags.StringVar(&name, "n", "", "Server name (shorthand)")
|
||||
|
|
@ -194,6 +196,12 @@ func createCommand() {
|
|||
envVars = append(envVars, value)
|
||||
return nil
|
||||
})
|
||||
|
||||
// Custom flag for package arguments
|
||||
createFlags.Func("package-arg", "Package argument in format VALUE:DESCRIPTION (can be repeated)", func(value string) error {
|
||||
packageArgs = append(packageArgs, value)
|
||||
return nil
|
||||
})
|
||||
|
||||
createFlags.Parse(os.Args[2:])
|
||||
|
||||
|
|
@ -217,7 +225,7 @@ func createCommand() {
|
|||
}
|
||||
|
||||
// Create server structure
|
||||
server := createServerStructure(name, description, version, repoURL, repoSource, registryName, packageName, packageVersion, runtimeHint, execute, envVars)
|
||||
server := createServerStructure(name, description, version, repoURL, repoSource, registryName, packageName, packageVersion, runtimeHint, execute, envVars, packageArgs)
|
||||
|
||||
// Convert to JSON
|
||||
jsonData, err := json.MarshalIndent(server, "", " ")
|
||||
|
|
@ -292,7 +300,7 @@ func publishToRegistry(registryURL string, mcpData []byte, token string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func createServerStructure(name, description, version, repoURL, repoSource, registryName, packageName, packageVersion, runtimeHint, execute string, envVars []string) ServerJSON {
|
||||
func createServerStructure(name, description, version, repoURL, repoSource, registryName, packageName, packageVersion, runtimeHint, execute string, envVars []string, packageArgs []string) ServerJSON {
|
||||
// Parse environment variables
|
||||
var environmentVariables []EnvironmentVariable
|
||||
for _, envVar := range envVars {
|
||||
|
|
@ -311,16 +319,50 @@ func createServerStructure(name, description, version, repoURL, repoSource, regi
|
|||
}
|
||||
}
|
||||
|
||||
// Parse package arguments
|
||||
var packageArguments []RuntimeArgument
|
||||
for i, pkgArg := range packageArgs {
|
||||
parts := strings.SplitN(pkgArg, ":", 2)
|
||||
value := parts[0]
|
||||
description := fmt.Sprintf("Package argument %d", i+1)
|
||||
if len(parts) == 2 {
|
||||
description = parts[1]
|
||||
}
|
||||
|
||||
packageArguments = append(packageArguments, RuntimeArgument{
|
||||
Description: description,
|
||||
IsRequired: true, // Package arguments are typically required
|
||||
Format: "string",
|
||||
Value: value,
|
||||
Default: value,
|
||||
Type: "positional",
|
||||
ValueHint: value,
|
||||
})
|
||||
}
|
||||
|
||||
// Parse execute command to create runtime arguments
|
||||
var runtimeArguments []RuntimeArgument
|
||||
if execute != "" {
|
||||
// Split the execute command into parts
|
||||
parts := strings.Fields(execute)
|
||||
// Split the execute command into parts, handling quoted strings
|
||||
parts := smartSplit(execute)
|
||||
if len(parts) > 1 {
|
||||
// Add each argument as a runtime argument
|
||||
// Skip the first part (command) and add each argument as a runtime argument
|
||||
for i, arg := range parts[1:] {
|
||||
description := fmt.Sprintf("Runtime argument %d", i+1)
|
||||
|
||||
// Try to provide better descriptions based on common patterns
|
||||
if strings.HasPrefix(arg, "--") {
|
||||
description = fmt.Sprintf("Command line flag: %s", arg)
|
||||
} else if strings.HasPrefix(arg, "-") && len(arg) == 2 {
|
||||
description = fmt.Sprintf("Command line option: %s", arg)
|
||||
} else if strings.Contains(arg, "=") {
|
||||
description = fmt.Sprintf("Configuration parameter: %s", arg)
|
||||
} else if i > 0 && strings.HasPrefix(parts[i], "-") {
|
||||
description = fmt.Sprintf("Value for %s", parts[i])
|
||||
}
|
||||
|
||||
runtimeArguments = append(runtimeArguments, RuntimeArgument{
|
||||
Description: fmt.Sprintf("Runtime argument %d", i+1),
|
||||
Description: description,
|
||||
IsRequired: false,
|
||||
Format: "string",
|
||||
Value: arg,
|
||||
|
|
@ -339,6 +381,7 @@ func createServerStructure(name, description, version, repoURL, repoSource, regi
|
|||
Version: packageVersion,
|
||||
RuntimeHint: runtimeHint,
|
||||
RuntimeArguments: runtimeArguments,
|
||||
PackageArguments: packageArguments,
|
||||
EnvironmentVariables: environmentVariables,
|
||||
}
|
||||
|
||||
|
|
@ -356,3 +399,39 @@ func createServerStructure(name, description, version, repoURL, repoSource, regi
|
|||
Packages: []Package{pkg},
|
||||
}
|
||||
}
|
||||
|
||||
// smartSplit splits a command string into parts, handling quoted strings and common shell patterns
|
||||
func smartSplit(command string) []string {
|
||||
var parts []string
|
||||
var current strings.Builder
|
||||
var inQuotes bool
|
||||
var quoteChar rune
|
||||
|
||||
for _, char := range command {
|
||||
switch {
|
||||
case char == '"' || char == '\'':
|
||||
if !inQuotes {
|
||||
inQuotes = true
|
||||
quoteChar = char
|
||||
} else if char == quoteChar {
|
||||
inQuotes = false
|
||||
quoteChar = 0
|
||||
} else {
|
||||
current.WriteRune(char)
|
||||
}
|
||||
case char == ' ' && !inQuotes:
|
||||
if current.Len() > 0 {
|
||||
parts = append(parts, current.String())
|
||||
current.Reset()
|
||||
}
|
||||
default:
|
||||
current.WriteRune(char)
|
||||
}
|
||||
}
|
||||
|
||||
if current.Len() > 0 {
|
||||
parts = append(parts, current.String())
|
||||
}
|
||||
|
||||
return parts
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue