@@ -21,8 +21,8 @@ import (
2121
2222var rootCmd = & cobra.Command {
2323 Use : "emcee [spec-path-or-url]" ,
24- Short : "An MCP server for a given OpenAPI specification" ,
25- Long : `emcee is a CLI tool that provides an MCP stdio transport for a given OpenAPI specification.
24+ Short : "Creates an MCP server for an OpenAPI specification" ,
25+ Long : `emcee is a CLI tool that provides an Model Context Protocol ( MCP) stdio transport for a given OpenAPI specification.
2626It takes an OpenAPI specification path or URL as input and processes JSON-RPC requests
2727from stdin, making corresponding API calls and returning JSON-RPC responses to stdout.
2828
@@ -32,11 +32,14 @@ The spec-path-or-url argument can be:
3232- "-" to read from stdin` ,
3333 Args : cobra .ExactArgs (1 ),
3434 RunE : func (cmd * cobra.Command , args []string ) error {
35+ // Set up context and signal handling
3536 ctx , cancel := signal .NotifyContext (cmd .Context (), syscall .SIGINT , syscall .SIGTERM )
3637 defer cancel ()
3738
39+ // Set up error group
3840 g , ctx := errgroup .WithContext (ctx )
3941
42+ // Set up logger
4043 logger := slog .New (slog .NewTextHandler (os .Stderr , & slog.HandlerOptions {
4144 Level : slog .LevelDebug ,
4245 }))
@@ -47,15 +50,19 @@ The spec-path-or-url argument can be:
4750 g .Go (func () error {
4851 var opts []mcp.ServerOption
4952
53+ // Set server info
54+ opts = append (opts , mcp .WithServerInfo (cmd .Name (), version ))
55+
56+ // Set logger
5057 opts = append (opts , mcp .WithLogger (logger ))
5158
59+ // Configure HTTP client
5260 retryClient := retryablehttp .NewClient ()
5361 retryClient .RetryMax = retries
5462 retryClient .RetryWaitMin = 1 * time .Second
5563 retryClient .RetryWaitMax = 30 * time .Second
5664 retryClient .HTTPClient .Timeout = timeout
5765 retryClient .Logger = logger
58-
5966 if rps > 0 {
6067 retryClient .Backoff = func (min , max time.Duration , attemptNum int , resp * http.Response ) time.Duration {
6168 // Ensure we wait at least 1/rps between requests
@@ -66,19 +73,24 @@ The spec-path-or-url argument can be:
6673 return retryablehttp .DefaultBackoff (min , max , attemptNum , resp )
6774 }
6875 }
69-
7076 client := retryClient .StandardClient ()
7177 opts = append (opts , mcp .WithClient (client ))
7278
79+ // Set Authentication header if provided
80+ if auth != "" {
81+ opts = append (opts , mcp .WithAuth (auth ))
82+ }
83+
84+ // Read OpenAPI specification data
85+ var rpcInput io.Reader = os .Stdin
7386 var specData []byte
7487 var err error
75- var rpcInput io.Reader = os .Stdin
7688
7789 if args [0 ] == "-" {
7890 logger .Info ("reading spec from stdin" )
7991
80- // When reading spec from stdin, we need to use /dev/tty for RPC input
81- // because stdin isn't a TTY when reading from a pipe
92+ // When reading the OpenAPI spec from stdin, we need to read RPC input from /dev/tty
93+ // since stdin is being used for the spec data and isn't available for interactive I/O
8294 tty , err := os .Open ("/dev/tty" )
8395 if err != nil {
8496 return fmt .Errorf ("error opening /dev/tty: %w" , err )
@@ -94,6 +106,7 @@ The spec-path-or-url argument can be:
94106 } else if strings .HasPrefix (args [0 ], "http://" ) || strings .HasPrefix (args [0 ], "https://" ) {
95107 logger .Info ("reading spec from URL" , "url" , args [0 ])
96108
109+ // Create HTTP request
97110 req , err := http .NewRequest (http .MethodGet , args [0 ], nil )
98111 if err != nil {
99112 return fmt .Errorf ("error creating request: %w" , err )
@@ -104,6 +117,7 @@ The spec-path-or-url argument can be:
104117 req .Header .Set ("Authorization" , auth )
105118 }
106119
120+ // Make HTTP request
107121 resp , err := client .Do (req )
108122 if err != nil {
109123 return fmt .Errorf ("error downloading spec: %w" , err )
@@ -113,6 +127,7 @@ The spec-path-or-url argument can be:
113127 }
114128 defer resp .Body .Close ()
115129
130+ // Read spec from response body
116131 specData , err = io .ReadAll (resp .Body )
117132 if err != nil {
118133 return fmt .Errorf ("error reading spec from %s: %w" , args [0 ], err )
@@ -142,30 +157,25 @@ The spec-path-or-url argument can be:
142157 return fmt .Errorf ("spec file too large (max 100MB): %s" , cleanPath )
143158 }
144159
160+ // Read spec from file
145161 specData , err = os .ReadFile (cleanPath )
146162 if err != nil {
147163 return fmt .Errorf ("error reading spec file %s: %w" , cleanPath , err )
148164 }
149165 }
150166
151- if len (specData ) == 0 {
152- return fmt .Errorf ("no OpenAPI spec data provided" )
153- }
167+ // Set spec data
154168 opts = append (opts , mcp .WithSpecData (specData ))
155169
156- if auth != "" {
157- opts = append (opts , mcp .WithAuth (auth ))
158- }
159-
160- opts = append (opts , mcp .WithLogger (logger ))
161-
170+ // Create server
162171 server , err := mcp .NewServer (opts ... )
163172 if err != nil {
164173 return fmt .Errorf ("error creating server: %w" , err )
165174 }
166175
176+ // Create and run transport
167177 transport := mcp .NewStdioTransport (rpcInput , os .Stdout , os .Stderr )
168- return transport .Run (ctx , server .Handle )
178+ return transport .Run (ctx , server .HandleRequest )
169179 })
170180
171181 return g .Wait ()
0 commit comments