api2mcp

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 1, 2026 License: MIT Imports: 10 Imported by: 0

README

api2mcp

Turn an existing HTTP API into an MCP server — OpenAPI-first, framework-agnostic, and curation-first (safe by default).

Why another one?

The existing Go options split into two camps: framework-specific magic (gin-mcp, echo-mcp — low traction, one framework each) and bare OpenAPI→MCP proxies. api2mcp is neither a single-framework toy nor a blind proxy. Its bet is that the hard, valuable part isn't converting an API — it's curating it: exposing only what's safe to hand an LLM, read-only by default, with response shaping so a 200-field JSON payload doesn't blow up the model's context.

It is layered so each concern is replaceable:

L5  Transport      stdio · streamable-HTTP                (mark3labs/mcp-go)
L4  Curation       include/exclude · read-only · shape
L3  Engine         IR → MCP tool + HTTP executor          (framework-agnostic core)
L2  Sources        OpenAPI · swaggo · framework adapters
L1  Adapters       gin · echo · fiber · chi   (+ reflect schema provider)

Everything collapses into one intermediate representation (ir.Operation), so the core has exactly one code path whether the input is a swagger.json or a live Gin router.

Install

go get github.com/promptrails/api2mcp
# or the standalone binary:
go install github.com/promptrails/api2mcp/cmd/api2mcp@latest

Quick start

Library — OpenAPI spec over stdio (Claude Desktop, Cursor, Zed):

src, _ := openapi.FromFile("openapi.yaml")
srv := api2mcp.New(src,
    api2mcp.WithBaseURL("https://api.internal"),
    api2mcp.ReadOnly(),              // only GET/HEAD become tools
    api2mcp.IncludeTags("public"),   // whitelist
)
srv.ServeStdio(context.Background())

CLI — no Go code, just a config file:

api2mcp serve -config examples/api2mcp.yaml
# or the quick path:
api2mcp serve -openapi openapi.yaml -base https://api.internal -read-only -http :8080

Embedded — mount /mcp inside an existing Gin/Echo/Fiber/chi app:

src := ginadapter.New(router)        // routes are the source of tools
srv := api2mcp.New(src, api2mcp.WithBaseURL("http://localhost:8080"), api2mcp.ReadOnly())
h, _ := srv.HTTPHandler(context.Background())
router.Any("/mcp", gin.WrapH(h))

Sources

Source Use it when
source/openapi You have an OpenAPI 3 spec (file or URL). Primary path.
source/swaggo Your app is annotated with swaggo (OpenAPI 2.0).
adapter/{gin,echo,fiber,chi} Embedded mode: the live router is the source.
schema/reflectschema Embedded mode + you want request-body schemas from Go structs.

Curation (the point)

Safe by default. Compose any of:

  • ReadOnly() — drop every mutating operation (POST/PUT/PATCH/DELETE).
  • IncludeTags(...) / ExcludeTags(...)
  • IncludePaths("/public/*") / ExcludePaths("/admin/*")
  • IncludeOperations(id...) / ExcludeOperations(id...)
  • WithFilter(func(ir.Operation) bool) — arbitrary predicate.

Plus:

  • Every tool gets MCP safety annotations (readOnlyHint/destructiveHint/idempotentHint) derived from its HTTP method, so clients can warn before destructive calls.
  • WithMarkDestructive("charge") — force the destructive hint on risky endpoints.
  • WithRateLimit(5, 10) — per-tool token bucket, protects the upstream from a runaway LLM.
  • WithAuditLogger(api2mcp.StdAuditLogger) — log every tool call.

Transport & auth

  • ServeStdio(ctx) for desktop clients; ServeHTTP(ctx, ":8080") for hosted/streamable-HTTP.
  • WithForwardHeaders("Authorization") passes the caller's Bearer/JWT through to the upstream API.
  • WithStaticHeader(k, v) injects a fixed header (e.g. a server-side API key).
  • WithMaxResponseBytes(n) caps each response so large payloads don't flood the model's context.

Examples

  • examples/openapi-stdio — OpenAPI → stdio
  • examples/openapi-http — OpenAPI → streamable-HTTP with auth forwarding
  • examples/embedded-gin — MCP mounted inside a Gin app
  • examples/embedded-echo — MCP mounted inside an Echo app
  • examples/embedded-fiber — MCP mounted inside a Fiber app
  • examples/embedded-chi — MCP mounted inside a chi app
  • examples/api2mcp.yaml — full CLI config

All four embedded examples mount /mcp in the same process as the API and are verified end-to-end (an MCP tools/call reaches the app's own handler).

License

MIT

Documentation

Overview

Package api2mcp turns an existing HTTP API into an MCP server.

It is OpenAPI-first and layered: a Source (an OpenAPI spec, a live router, a manual list) is normalized into a framework-agnostic intermediate representation, the engine maps that to MCP tools backed by real HTTP calls, and a curation layer decides which tools are safe to expose and how their responses are shaped. The MCP protocol itself is handled by mark3labs/mcp-go.

Minimal use:

src, _ := openapi.FromFile("openapi.yaml")
srv := api2mcp.New(src, api2mcp.WithBaseURL("https://api.internal"))
srv.ServeStdio(context.Background())

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func StdAuditLogger

func StdAuditLogger(e engine.AuditEvent)

StdAuditLogger logs each tool call to the standard logger in a single line:

api2mcp: getUser GET /users/{id} -> 200 (12ms)

Pass it to WithAuditLogger for out-of-the-box observability.

Types

type Option

type Option func(*options)

Option configures a Server.

func ExcludeOperations

func ExcludeOperations(ids ...string) Option

ExcludeOperations drops the operations with the given ids.

func ExcludePaths

func ExcludePaths(globs ...string) Option

ExcludePaths drops operations whose path matches one of the globs (e.g. "/admin/*", "/internal/*").

func ExcludeTags

func ExcludeTags(tags ...string) Option

ExcludeTags drops operations carrying any of the given tags.

func IncludeOperations

func IncludeOperations(ids ...string) Option

IncludeOperations keeps only the operations with the given ids.

func IncludePaths

func IncludePaths(globs ...string) Option

IncludePaths keeps only operations whose path matches one of the globs (e.g. "/public/*").

func IncludeTags

func IncludeTags(tags ...string) Option

IncludeTags keeps only operations carrying at least one of the given tags (a whitelist). Combine with other Include* options — they are OR-ed.

func ReadOnly

func ReadOnly() Option

ReadOnly exposes only side-effect free operations (GET/HEAD). This is the recommended default when pointing an LLM at a real API.

func WithAnnotator

func WithAnnotator(fn engine.AnnotateFunc) Option

WithAnnotator adjusts each tool's safety annotations after they are derived from the HTTP method. For the common case, prefer WithMarkDestructive.

func WithAuditLogger

func WithAuditLogger(fn engine.AuditFunc) Option

WithAuditLogger registers a callback invoked after every tool call with the operation, upstream status, duration and any error — so LLM-driven traffic against your API is observable. Pass api2mcp.StdAuditLogger for a sane default.

func WithBaseURL

func WithBaseURL(url string) Option

WithBaseURL sets the upstream API root that tool calls are sent to. Required unless the Source already resolves absolute URLs.

func WithEndpointPath

func WithEndpointPath(p string) Option

WithEndpointPath sets the HTTP path the streamable-HTTP transport listens on (default "/mcp").

func WithFilter

func WithFilter(keep func(ir.Operation) bool) Option

WithFilter adds a custom predicate; operations for which it returns false are dropped. (Implemented as an exclude of the negation.)

func WithForwardHeaders

func WithForwardHeaders(names ...string) Option

WithForwardHeaders forwards the named headers from the incoming MCP HTTP request to every upstream call — typically "Authorization" so a client's Bearer/JWT reaches your protected API. Only meaningful with ServeHTTP.

func WithHTTPClient

func WithHTTPClient(c *http.Client) Option

WithHTTPClient overrides the HTTP client used for upstream calls.

func WithMarkDestructive

func WithMarkDestructive(operationIDs ...string) Option

WithMarkDestructive forces the destructiveHint (and clears readOnlyHint) on the named operations — for endpoints whose method understates their risk, e.g. a POST /charge or POST /accounts/{id}/close. MCP clients use this to warn or require confirmation before the LLM invokes them.

func WithMaxResponseBytes

func WithMaxResponseBytes(n int) Option

WithMaxResponseBytes caps the size of each tool's rendered response so a large upstream payload cannot blow up the model's context. 0 means no limit.

func WithName

func WithName(name string) Option

WithName sets the MCP server name advertised to clients.

func WithRateLimit

func WithRateLimit(perSecond float64, burst int) Option

WithRateLimit caps how often each tool may be invoked, using a per-tool token bucket. perSecond is the sustained rate; burst is the most back-to-back calls allowed. A denied call fails fast with a clear message rather than blocking. This protects the upstream API from a runaway LLM.

func WithResponseShaper

func WithResponseShaper(s engine.ShapeFunc) Option

WithResponseShaper overrides how upstream responses are rendered for the LLM.

func WithStaticHeader

func WithStaticHeader(key, value string) Option

WithStaticHeader adds a header sent on every upstream call (e.g. an API key).

func WithVersion

func WithVersion(v string) Option

WithVersion sets the MCP server version advertised to clients.

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server wires a Source to the engine and an MCP transport.

func New

func New(src source.Source, opts ...Option) *Server

New creates a Server from a Source and options.

func (*Server) HTTPHandler

func (s *Server) HTTPHandler(ctx context.Context) (http.Handler, error)

HTTPHandler returns the streamable-HTTP handler so callers can mount the MCP endpoint inside their own http.Server / router instead of calling ServeHTTP.

func (*Server) MCPServer

func (s *Server) MCPServer(ctx context.Context) (*server.MCPServer, error)

MCPServer builds a ready-to-serve mcp-go server with all tools registered.

func (*Server) ServeHTTP

func (s *Server) ServeHTTP(ctx context.Context, addr string) error

ServeHTTP builds the server and serves it over the MCP streamable-HTTP transport at addr (e.g. ":8080"). This is the load-balancer-friendly transport suitable for remote/hosted deployments. Forwarded auth headers (see WithForwardHeaders) are passed through to upstream calls per request.

func (*Server) ServeStdio

func (s *Server) ServeStdio(ctx context.Context) error

ServeStdio builds the server and serves it over stdio (for desktop clients like Claude Desktop, Cursor, Zed).

func (*Server) Tools

func (s *Server) Tools(ctx context.Context) ([]engine.Tool, error)

Tools resolves the Source and builds the MCP tools (after curation).

Directories

Path Synopsis
adapter
chiadapter
Package chiadapter exposes a live chi router as an api2mcp Source.
Package chiadapter exposes a live chi router as an api2mcp Source.
echoadapter
Package echoadapter exposes a live *echo.Echo as an api2mcp Source.
Package echoadapter exposes a live *echo.Echo as an api2mcp Source.
fiberadapter
Package fiberadapter exposes a live *fiber.App as an api2mcp Source.
Package fiberadapter exposes a live *fiber.App as an api2mcp Source.
ginadapter
Package ginadapter exposes a live *gin.Engine as an api2mcp Source, for embedded mode: mount an MCP endpoint inside an existing Gin app without a separate process or an OpenAPI spec.
Package ginadapter exposes a live *gin.Engine as an api2mcp Source, for embedded mode: mount an MCP endpoint inside an existing Gin app without a separate process or an OpenAPI spec.
cmd
api2mcp command
Command api2mcp is a standalone binary that serves an existing HTTP API as an MCP server, configured entirely from a YAML file — no Go code required.
Command api2mcp is a standalone binary that serves an existing HTTP API as an MCP server, configured entirely from a YAML file — no Go code required.
Package engine is the framework-agnostic core: it turns ir.Operations into MCP tools (with raw JSON Schema inputs) and wires each tool's handler to the HTTP Executor.
Package engine is the framework-agnostic core: it turns ir.Operations into MCP tools (with raw JSON Schema inputs) and wires each tool's handler to the HTTP Executor.
examples
embedded-chi command
Command embedded-chi mounts an MCP endpoint at /mcp inside an existing chi router.
Command embedded-chi mounts an MCP endpoint at /mcp inside an existing chi router.
embedded-echo command
Command embedded-echo mounts an MCP endpoint at /mcp inside an existing Echo app.
Command embedded-echo mounts an MCP endpoint at /mcp inside an existing Echo app.
embedded-fiber command
Command embedded-fiber mounts an MCP endpoint at /mcp inside an existing Fiber app.
Command embedded-fiber mounts an MCP endpoint at /mcp inside an existing Fiber app.
embedded-gin command
Command embedded-gin shows embedded mode: an existing Gin API mounts its own MCP endpoint at /mcp in the same process, with no OpenAPI spec and no separate server.
Command embedded-gin shows embedded mode: an existing Gin API mounts its own MCP endpoint at /mcp in the same process, with no OpenAPI spec and no separate server.
openapi-http command
Command openapi-http serves an OpenAPI spec as an MCP server over the streamable-HTTP transport, forwarding the caller's Authorization header to the upstream API and capping response size.
Command openapi-http serves an OpenAPI spec as an MCP server over the streamable-HTTP transport, forwarding the caller's Authorization header to the upstream API and capping response size.
openapi-stdio command
Command openapi-stdio is the M0 spike: it loads an OpenAPI spec and serves every operation as an MCP tool over stdio.
Command openapi-stdio is the M0 spike: it loads an OpenAPI spec and serves every operation as an MCP tool over stdio.
Package ir defines the framework-agnostic intermediate representation that every Source normalizes into.
Package ir defines the framework-agnostic intermediate representation that every Source normalizes into.
Package policy is the curation layer (L4): it decides which operations are safe and appropriate to expose as MCP tools.
Package policy is the curation layer (L4): it decides which operations are safe and appropriate to expose as MCP tools.
schema
reflectschema
Package reflectschema enriches a Source's operations with request-body JSON Schemas reflected from Go structs.
Package reflectschema enriches a Source's operations with request-body JSON Schemas reflected from Go structs.
Package source defines how operations are discovered.
Package source defines how operations are discovered.
openapi
Package openapi turns an OpenAPI 3 document into ir.Operations.
Package openapi turns an OpenAPI 3 document into ir.Operations.
route
Package route holds the shared logic every framework adapter uses to turn a (method, path) route into an ir.Operation.
Package route holds the shared logic every framework adapter uses to turn a (method, path) route into an ir.Operation.
swaggo
Package swaggo turns a swaggo-generated Swagger (OpenAPI 2.0) document into an api2mcp Source.
Package swaggo turns a swaggo-generated Swagger (OpenAPI 2.0) document into an api2mcp Source.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL