@@ -16,22 +16,93 @@ import (
16
16
"github.com/modelcontextprotocol/registry/tools/publisher/auth/github"
17
17
)
18
18
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
+
19
61
func 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
+
20
91
var registryURL string
21
92
var mcpFilePath string
22
93
var forceLogin bool
23
94
var authMethod string
24
95
25
96
// 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)" )
30
101
31
- flag .Parse ()
102
+ publishFlags .Parse (os . Args [ 2 :] )
32
103
33
104
if registryURL == "" || mcpFilePath == "" {
34
- flag .Usage ()
105
+ publishFlags .Usage ()
35
106
return
36
107
}
37
108
@@ -79,6 +150,95 @@ func main() {
79
150
log .Println ("Successfully published to registry!" )
80
151
}
81
152
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
+
82
242
// publishToRegistry sends the MCP server details to the registry with authentication
83
243
func publishToRegistry (registryURL string , mcpData []byte , token string ) error {
84
244
// Parse the MCP JSON data
@@ -131,3 +291,68 @@ func publishToRegistry(registryURL string, mcpData []byte, token string) error {
131
291
log .Println (string (body ))
132
292
return nil
133
293
}
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