StartHTTPServer starts a streaming HTTP MCP server on the given listener
(ctx context.Context, agentFilename, agentName string, runConfig *config.RuntimeConfig, ln net.Listener)
| 55 | |
| 56 | // StartHTTPServer starts a streaming HTTP MCP server on the given listener |
| 57 | func StartHTTPServer(ctx context.Context, agentFilename, agentName string, runConfig *config.RuntimeConfig, ln net.Listener) error { |
| 58 | slog.DebugContext(ctx, "Starting HTTP MCP server", "agent", agentFilename, "addr", ln.Addr()) |
| 59 | |
| 60 | server, cleanup, err := createMCPServer(ctx, agentFilename, agentName, runConfig) |
| 61 | if err != nil { |
| 62 | return err |
| 63 | } |
| 64 | defer cleanup() |
| 65 | |
| 66 | fmt.Printf("MCP HTTP server listening on http://%s\n", ln.Addr()) |
| 67 | |
| 68 | // Wrap with otelhttp so the MCP-over-HTTP transport extracts |
| 69 | // `traceparent` / `baggage` from incoming requests just like the |
| 70 | // stdio transport extracts them from `params._meta`. Without this |
| 71 | // HTTP-mode MCP clients lose trace context at the boundary. |
| 72 | httpServer := &http.Server{ |
| 73 | Handler: otelhttp.NewHandler( |
| 74 | mcp.NewStreamableHTTPHandler(func(_ *http.Request) *mcp.Server { |
| 75 | return server |
| 76 | }, nil), |
| 77 | "mcp.http", |
| 78 | ), |
| 79 | ReadHeaderTimeout: 10 * time.Second, |
| 80 | } |
| 81 | |
| 82 | errCh := make(chan error, 1) |
| 83 | go func() { |
| 84 | errCh <- httpServer.Serve(ln) |
| 85 | }() |
| 86 | |
| 87 | select { |
| 88 | case <-ctx.Done(): |
| 89 | // ctx is done; detach from its cancellation but keep its trace |
| 90 | // context so the graceful shutdown can still run. Bound it so a |
| 91 | // hung client connection can't block shutdown indefinitely. |
| 92 | shutdownCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second) |
| 93 | defer cancel() |
| 94 | return httpServer.Shutdown(shutdownCtx) |
| 95 | case err := <-errCh: |
| 96 | if errors.Is(err, http.ErrServerClosed) { |
| 97 | return nil |
| 98 | } |
| 99 | return err |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | func createMCPServer(ctx context.Context, agentFilename, agentName string, runConfig *config.RuntimeConfig) (*mcp.Server, func(), error) { |
| 104 | agentSource, err := config.Resolve(agentFilename, nil) |
no test coverage detected