@@ -16,22 +16,93 @@ import (
1616 "github.com/modelcontextprotocol/registry/tools/publisher/auth/github"
1717)
1818
19+ // Server structure types for JSON generation
20+ type Repository struct {
21+ URL string `json:"url"`
22+ Source string `json:"source"`
23+ }
24+
25+ type VersionDetail struct {
26+ Version string `json:"version"`
27+ }
28+
29+ type EnvironmentVariable struct {
30+ Name string `json:"name"`
31+ Description string `json:"description"`
32+ }
33+
34+ type RuntimeArgument struct {
35+ Description string `json:"description"`
36+ IsRequired bool `json:"is_required"`
37+ Format string `json:"format"`
38+ Value string `json:"value"`
39+ Default string `json:"default"`
40+ Type string `json:"type"`
41+ ValueHint string `json:"value_hint"`
42+ }
43+
44+ type Package struct {
45+ RegistryName string `json:"registry_name"`
46+ Name string `json:"name"`
47+ Version string `json:"version"`
48+ RuntimeHint string `json:"runtime_hint,omitempty"`
49+ RuntimeArguments []RuntimeArgument `json:"runtime_arguments,omitempty"`
50+ EnvironmentVariables []EnvironmentVariable `json:"environment_variables,omitempty"`
51+ }
52+
53+ type ServerJSON struct {
54+ Name string `json:"name"`
55+ Description string `json:"description"`
56+ Repository Repository `json:"repository"`
57+ VersionDetail VersionDetail `json:"version_detail"`
58+ Packages []Package `json:"packages"`
59+ }
60+
1961func main () {
62+ if len (os .Args ) < 2 {
63+ printUsage ()
64+ return
65+ }
66+
67+ command := os .Args [1 ]
68+ switch command {
69+ case "publish" :
70+ publishCommand ()
71+ case "create" :
72+ createCommand ()
73+ default :
74+ printUsage ()
75+ }
76+ }
77+
78+ func printUsage () {
79+ fmt .Println ("MCP Registry Publisher Tool" )
80+ fmt .Println ()
81+ fmt .Println ("Usage:" )
82+ fmt .Println (" mcp-publisher publish [flags] Publish a server.json file to the registry" )
83+ fmt .Println (" mcp-publisher create [flags] Create a new server.json file" )
84+ fmt .Println ()
85+ fmt .Println ("Use 'mcp-publisher <command> --help' for more information about a command." )
86+ }
87+
88+ func publishCommand () {
89+ publishFlags := flag .NewFlagSet ("publish" , flag .ExitOnError )
90+
2091 var registryURL string
2192 var mcpFilePath string
2293 var forceLogin bool
2394 var authMethod string
2495
2596 // Command-line flags for configuration
26- flag .StringVar (& registryURL , "registry-url" , "" , "URL of the registry (required)" )
27- flag .StringVar (& mcpFilePath , "mcp-file" , "" , "path to the MCP file (required)" )
28- flag .BoolVar (& forceLogin , "login" , false , "force a new login even if a token exists" )
29- flag .StringVar (& authMethod , "auth-method" , "github-oauth" , "authentication method to use (default: github-oauth)" )
97+ publishFlags .StringVar (& registryURL , "registry-url" , "" , "URL of the registry (required)" )
98+ publishFlags .StringVar (& mcpFilePath , "mcp-file" , "" , "path to the MCP file (required)" )
99+ publishFlags .BoolVar (& forceLogin , "login" , false , "force a new login even if a token exists" )
100+ publishFlags .StringVar (& authMethod , "auth-method" , "github-oauth" , "authentication method to use (default: github-oauth)" )
30101
31- flag .Parse ()
102+ publishFlags .Parse (os . Args [ 2 :] )
32103
33104 if registryURL == "" || mcpFilePath == "" {
34- flag .Usage ()
105+ publishFlags .Usage ()
35106 return
36107 }
37108
@@ -79,6 +150,95 @@ func main() {
79150 log .Println ("Successfully published to registry!" )
80151}
81152
153+ func createCommand () {
154+ createFlags := flag .NewFlagSet ("create" , flag .ExitOnError )
155+
156+ // Basic server information flags
157+ var name string
158+ var description string
159+ var version string
160+ var repoURL string
161+ var repoSource string
162+ var output string
163+
164+ // Package information flags
165+ var registryName string
166+ var packageName string
167+ var packageVersion string
168+ var runtimeHint string
169+ var execute string
170+
171+ // Repeatable flags
172+ var envVars []string
173+
174+ createFlags .StringVar (& name , "name" , "" , "Server name (e.g., io.github.owner/repo-name) (required)" )
175+ createFlags .StringVar (& name , "n" , "" , "Server name (shorthand)" )
176+ createFlags .StringVar (& description , "description" , "" , "Server description (required)" )
177+ createFlags .StringVar (& description , "d" , "" , "Server description (shorthand)" )
178+ createFlags .StringVar (& version , "version" , "1.0.0" , "Server version" )
179+ createFlags .StringVar (& version , "v" , "1.0.0" , "Server version (shorthand)" )
180+ createFlags .StringVar (& repoURL , "repo-url" , "" , "Repository URL (required)" )
181+ createFlags .StringVar (& repoSource , "repo-source" , "github" , "Repository source" )
182+ createFlags .StringVar (& output , "output" , "server.json" , "Output file path" )
183+ createFlags .StringVar (& output , "o" , "server.json" , "Output file path (shorthand)" )
184+
185+ createFlags .StringVar (& registryName , "registry" , "npm" , "Package registry name" )
186+ createFlags .StringVar (& packageName , "package-name" , "" , "Package name (defaults to server name)" )
187+ createFlags .StringVar (& packageVersion , "package-version" , "" , "Package version (defaults to server version)" )
188+ createFlags .StringVar (& runtimeHint , "runtime-hint" , "" , "Runtime hint (e.g., docker)" )
189+ createFlags .StringVar (& execute , "execute" , "" , "Command to execute the server" )
190+ createFlags .StringVar (& execute , "e" , "" , "Command to execute the server (shorthand)" )
191+
192+ // Custom flag for environment variables
193+ createFlags .Func ("env-var" , "Environment variable in format NAME:DESCRIPTION (can be repeated)" , func (value string ) error {
194+ envVars = append (envVars , value )
195+ return nil
196+ })
197+
198+ createFlags .Parse (os .Args [2 :])
199+
200+ // Validate required flags
201+ if name == "" {
202+ log .Fatal ("Error: --name/-n is required" )
203+ }
204+ if description == "" {
205+ log .Fatal ("Error: --description/-d is required" )
206+ }
207+ if repoURL == "" {
208+ log .Fatal ("Error: --repo-url is required" )
209+ }
210+
211+ // Set defaults
212+ if packageName == "" {
213+ packageName = name
214+ }
215+ if packageVersion == "" {
216+ packageVersion = version
217+ }
218+
219+ // Create server structure
220+ server := createServerStructure (name , description , version , repoURL , repoSource , registryName , packageName , packageVersion , runtimeHint , execute , envVars )
221+
222+ // Convert to JSON
223+ jsonData , err := json .MarshalIndent (server , "" , " " )
224+ if err != nil {
225+ log .Fatalf ("Error marshaling JSON: %v" , err )
226+ }
227+
228+ // Write to file
229+ err = os .WriteFile (output , jsonData , 0644 )
230+ if err != nil {
231+ log .Fatalf ("Error writing file: %v" , err )
232+ }
233+
234+ log .Printf ("Successfully created %s" , output )
235+ log .Println ("You may need to edit the file to:" )
236+ log .Println (" - Add or modify package arguments" )
237+ log .Println (" - Set environment variable requirements" )
238+ log .Println (" - Add remote server configurations" )
239+ log .Println (" - Adjust runtime arguments" )
240+ }
241+
82242// publishToRegistry sends the MCP server details to the registry with authentication
83243func publishToRegistry (registryURL string , mcpData []byte , token string ) error {
84244 // Parse the MCP JSON data
@@ -131,3 +291,68 @@ func publishToRegistry(registryURL string, mcpData []byte, token string) error {
131291 log .Println (string (body ))
132292 return nil
133293}
294+
295+ func createServerStructure (name , description , version , repoURL , repoSource , registryName , packageName , packageVersion , runtimeHint , execute string , envVars []string ) ServerJSON {
296+ // Parse environment variables
297+ var environmentVariables []EnvironmentVariable
298+ for _ , envVar := range envVars {
299+ parts := strings .SplitN (envVar , ":" , 2 )
300+ if len (parts ) == 2 {
301+ environmentVariables = append (environmentVariables , EnvironmentVariable {
302+ Name : parts [0 ],
303+ Description : parts [1 ],
304+ })
305+ } else {
306+ // If no description provided, use a default
307+ environmentVariables = append (environmentVariables , EnvironmentVariable {
308+ Name : parts [0 ],
309+ Description : fmt .Sprintf ("Environment variable for %s" , parts [0 ]),
310+ })
311+ }
312+ }
313+
314+ // Parse execute command to create runtime arguments
315+ var runtimeArguments []RuntimeArgument
316+ if execute != "" {
317+ // Split the execute command into parts
318+ parts := strings .Fields (execute )
319+ if len (parts ) > 1 {
320+ // Add each argument as a runtime argument
321+ for i , arg := range parts [1 :] {
322+ runtimeArguments = append (runtimeArguments , RuntimeArgument {
323+ Description : fmt .Sprintf ("Runtime argument %d" , i + 1 ),
324+ IsRequired : false ,
325+ Format : "string" ,
326+ Value : arg ,
327+ Default : arg ,
328+ Type : "positional" ,
329+ ValueHint : arg ,
330+ })
331+ }
332+ }
333+ }
334+
335+ // Create package
336+ pkg := Package {
337+ RegistryName : registryName ,
338+ Name : packageName ,
339+ Version : packageVersion ,
340+ RuntimeHint : runtimeHint ,
341+ RuntimeArguments : runtimeArguments ,
342+ EnvironmentVariables : environmentVariables ,
343+ }
344+
345+ // Create server structure
346+ return ServerJSON {
347+ Name : name ,
348+ Description : description ,
349+ Repository : Repository {
350+ URL : repoURL ,
351+ Source : repoSource ,
352+ },
353+ VersionDetail : VersionDetail {
354+ Version : version ,
355+ },
356+ Packages : []Package {pkg },
357+ }
358+ }
0 commit comments