Documentation
¶
Overview ¶
Package openapimcp synthesizes an in-process MCP server from an OpenAPI or Swagger document: every operation becomes one MCP tool whose handler issues the corresponding HTTP request against the upstream API.
It is the implementation behind the "openapi" MCP connection type. Core calls Factory on every connect of such a client (see schemas.MCPConfig. InProcessServerFactory) and hands it the HTTP client and header resolver it wants the synthesized tools to use; this package never decides credentials or network policy on its own.
Supported documents: Swagger 2.0, OpenAPI 3.0.x, 3.1.x and 3.2.x, in JSON or YAML. Newer 3.x minors are parsed as 3.2 with a warning. Every version is normalized into one Document, so the tool synthesis, request building and server construction do not branch on the spec version.
Not supported, and reported per operation in Synthesis.Unsupported rather than failing the whole document: multipart and binary request bodies, streaming request bodies (3.2 itemSchema), externally referenced schemas, and operations whose only security schemes are OAuth2, OpenID Connect or mutual TLS.
Naming: a tool is named after its operationId, sanitized to [A-Za-z0-9_] (hyphens would break code mode's tool-name parsing) and capped so that the "<client>-<tool>" form the gateway exposes stays within the 64-character limit most model providers enforce. Operations without an operationId are named "<method>_<path>"; duplicates receive a deterministic numeric suffix.
Index ¶
- Constants
- Variables
- func ApplyMetadata(cfg *schemas.MCPOpenAPIConfig, data []byte, doc *Document, syn *Synthesis)
- func BuildDescription(op Operation) string
- func BuildServer(opts ServerOptions) (*server.MCPServer, error)
- func DedupeToolNames(tools []Tool, maxLen int) []string
- func Factory(opts ...FactoryOption) schemas.InProcessServerFactory
- func FlattenInputSchema(op Operation) (json.RawMessage, []ArgBinding, *BodyBinding, []string, error)
- func InjectCredentials(req *http.Request, q url.Values, cookies *[]*http.Cookie, ...)
- func LoadBytes(data []byte) ([]byte, error)
- func LoadFile(path, baseDir string) ([]byte, error)
- func LoadURL(ctx context.Context, client *http.Client, specURL string) ([]byte, string, error)
- func NormalizeSchema(s map[string]any) map[string]any
- func Preview(data []byte, parseOpts ParseOptions, synthOpts SynthesizeOptions, ...) (*PreviewResult, *Synthesis, error)
- func ResolveBaseURL(doc *Document, override *string, specURL string) (base string, explicit bool, err error)
- func ResolveServingBaseURL(doc *Document, syn *Synthesis, override *string, specURL string) (base string, explicit bool, err error)
- func ResolveSpec(ctx context.Context, cfg *schemas.MCPOpenAPIConfig, client *http.Client, ...) ([]byte, string, string, error)
- func SelectBodyContent(content map[string]any) (contentType string, schema map[string]any, ok bool, reason string)
- func SpecHash(data []byte) string
- func ToolName(op Operation, maxLen int) string
- type ArgBinding
- type BodyBinding
- type Document
- type Executor
- type FactoryOption
- type HeaderResolver
- type Operation
- type Parameter
- type ParseOptions
- type PreviewResult
- type RequestBody
- type SecurityRequirement
- type SecurityScheme
- type ServerOptions
- type Synthesis
- type SynthesizeOptions
- type Tool
- type ToolSummary
- type UnsupportedOperation
- type Version
Constants ¶
const ( // MaxSpecBytes caps a spec document (inline, file or fetched). MaxSpecBytes = 5 << 20 // DefaultMaxResponseBytes caps an upstream response body returned to the model. DefaultMaxResponseBytes = 64 << 10 // ToolCountWarnThreshold is the tool count above which a warning is emitted. ToolCountWarnThreshold = 128 // MaxToolCount is the hard cap on synthesized tools per spec. MaxToolCount = 1000 // MaxToolNameLen is the provider-side cap on a function name, prefix included. MaxToolNameLen = 64 )
const ( InPath = "path" InQuery = "query" InHeader = "header" InCookie = "cookie" InQuerystring = "querystring" )
Parameter locations. The OpenAPI values plus 3.2's querystring.
const ( ContentTypeJSON = "application/json" ContentTypeForm = "application/x-www-form-urlencoded" ContentTypeText = "text/plain" )
Request body content types the executor can encode.
Variables ¶
var ErrSpecTooLarge = fmt.Errorf("openapi spec exceeds %d bytes", MaxSpecBytes)
ErrSpecTooLarge is returned when a spec exceeds MaxSpecBytes.
var ErrSpecURLInvalid = errors.New("invalid spec_url")
ErrSpecURLInvalid marks a spec_url that cannot be fetched no matter how often it is retried (wrong scheme, malformed URL).
Functions ¶
func ApplyMetadata ¶
func ApplyMetadata(cfg *schemas.MCPOpenAPIConfig, data []byte, doc *Document, syn *Synthesis)
ApplyMetadata fills the server-computed fields of an openapi_config from a parsed spec so stored rows describe themselves without re-parsing.
func BuildDescription ¶
BuildDescription composes the tool description from the operation's summary, description (capped) and its HTTP shape, so the model always sees the verb and path even when the spec carries no prose.
func BuildServer ¶
func BuildServer(opts ServerOptions) (*server.MCPServer, error)
BuildServer creates the in-process MCP server: one tool per synthesized operation, each handled by the shared Executor.
func DedupeToolNames ¶
DedupeToolNames gives colliding names a deterministic "_2", "_3", ... suffix in walk order (paths sorted, methods in fixed order), re-truncating so the suffixed name still fits. It returns one warning per renamed tool.
func Factory ¶
func Factory(opts ...FactoryOption) schemas.InProcessServerFactory
Factory returns the schemas.InProcessServerFactory core calls to synthesize an openapi client's server: resolve the spec (inline, file or URL fetched with the client core supplies), parse, synthesize, pick the base URL and build the server with core's HTTP client and header resolver.
Errors that will not go away by retrying (unparseable spec, no usable operation, bad base URL, missing config) are wrapped with schemas.ErrMCPOpenAPIConfigInvalid so the connect path fails fast; a failed spec_url fetch is returned as-is and keeps core's transient retry behavior.
func FlattenInputSchema ¶
func FlattenInputSchema(op Operation) (json.RawMessage, []ArgBinding, *BodyBinding, []string, error)
FlattenInputSchema builds a tool's single input schema from an operation: path/query/header/cookie parameters and the request body's properties are all top-level properties. The body keeps plain names; a parameter whose name collides with a body property or another parameter is suffixed "__<in>". Non-object bodies (arrays, primitives, oneOf/anyOf, text) travel as one "body" property.
func InjectCredentials ¶
func InjectCredentials(req *http.Request, q url.Values, cookies *[]*http.Cookie, schemes []SecurityScheme, creds map[string]schemas.MCPOpenAPICredential)
InjectCredentials applies the chosen schemes to the outgoing request: apiKey header/query/cookie, http bearer and http basic. Query credentials are added to q (the caller encodes it into the URL afterwards); cookies are appended to cookies as one name and one value each. Spec-mapped credentials win over same-named headers from the resolver, since they are the explicit instruction for this upstream.
func LoadFile ¶
LoadFile reads a spec from disk. A relative path is resolved against baseDir (the config directory) and must not escape it, judged by the file actually read: symlinks are resolved before the check. Only regular files are read, and the read itself is capped, so a special file or one that grows after the size check cannot block or exhaust memory.
func LoadURL ¶
LoadURL fetches a spec over HTTP(S) with the supplied client. The caller picks the client, and with it the network policy (SSRF guard, proxy, TLS). Bodies are read through a hard cap so a hostile server cannot exhaust memory. It also returns the document's final URL after redirects, which is what relative servers resolve against.
func NormalizeSchema ¶
NormalizeSchema returns a copy of s in the 3.0-style dialect tool consumers expect: 3.1 null-type arrays become nullable, multi-type arrays become anyOf, allOf-of-objects is merged, boolean exclusive bounds are dropped, readOnly properties are removed, and only allow-listed keywords survive.
func Preview ¶
func Preview(data []byte, parseOpts ParseOptions, synthOpts SynthesizeOptions, baseURLOverride *string) (*PreviewResult, *Synthesis, error)
Preview parses and synthesizes without building a server, for the UI's parse-first flow and for validating a create/update request.
func ResolveBaseURL ¶
func ResolveBaseURL(doc *Document, override *string, specURL string) (base string, explicit bool, err error)
ResolveBaseURL decides the upstream base: an explicit override wins, else the spec's first absolute server, else a relative server resolved against the spec URL. explicit reports whether the admin override was used (op-level servers are then ignored by the executor).
func ResolveServingBaseURL ¶
func ResolveServingBaseURL(doc *Document, syn *Synthesis, override *string, specURL string) (base string, explicit bool, err error)
ResolveServingBaseURL is ResolveBaseURL for a spec about to be served. When no document-level base resolves but every synthesized tool's operation declares its own absolute server, it returns an empty base and no error: the executor sends each call to its operation's server.
func ResolveSpec ¶
func ResolveSpec(ctx context.Context, cfg *schemas.MCPOpenAPIConfig, client *http.Client, baseDir string) ([]byte, string, string, error)
ResolveSpec picks the spec source from an openapi_config in priority order: inline spec, spec_file (relative to baseDir), spec_url (fetched with client). It returns the bytes, which source supplied them ("inline", "file", "url"), and the URL relative servers resolve against: the fetched document's final URL, or spec_url as given for the other sources.
func SelectBodyContent ¶
func SelectBodyContent(content map[string]any) (contentType string, schema map[string]any, ok bool, reason string)
SelectBodyContent picks the media type of a requestBody.content map that the executor can produce, returning its (dereferenced) schema. ok is false, with a reason, when the body can only be sent in a form this package does not support (multipart, binary, streaming).
func ToolName ¶
ToolName derives the tool name for an operation: the sanitized operationId, or "<method>_<path>" when the spec declares none. Only [A-Za-z0-9_] survive (hyphens would collide with the gateway's "<client>-<tool>" prefix and break code mode's tool-name parsing), a leading digit is prefixed with "op_", and the result is cut to maxLen.
Types ¶
type ArgBinding ¶
type ArgBinding struct {
ArgName string // property name in the tool input schema
Location string // path | query | header | cookie | querystring
Name string // wire parameter name
Required bool
Explode bool
Style string
ContentType string // media type the whole value is serialized in (content-declared parameters)
Group string // optional 3.2 querystring this field was flattened from
RequiredIfGroupSent bool // required whenever any field of Group is sent
}
ArgBinding maps one flattened input property to a wire parameter.
type BodyBinding ¶
type BodyBinding struct {
Mode string // flattened | single
ArgNames []string // flattened: body properties present at the top level
SingleArg string // single: the one property carrying the whole body
ContentType string
RequiredIfSent []string // flattened, optional body: fields a body must carry whenever one is sent
}
BodyBinding describes how input properties are turned into the request body.
type Document ¶
type Document struct {
Title string
Version string // info.version
Description string
OpenAPIVersion string // detected spec version, e.g. "2.0", "3.0.3", "3.1.0"
Self string // 3.2 $self, if any
Servers []string
SecuritySchemes map[string]SecurityScheme
GlobalSecurity []SecurityRequirement
Operations []Operation
Warnings []string
}
Document is the version-independent view of a spec.
type Executor ¶
type Executor struct {
// contains filtered or unexported fields
}
Executor turns tool calls into HTTP requests against one upstream.
func NewExecutor ¶
func NewExecutor(opts ServerOptions) (*Executor, error)
NewExecutor validates ServerOptions and returns the executor the synthesized tool handlers share.
func (*Executor) BuildRequest ¶
func (e *Executor) BuildRequest(ctx context.Context, tool *Tool, args map[string]any) (*http.Request, error)
BuildRequest assembles the HTTP request for one tool call from its arguments.
func (*Executor) Execute ¶
func (e *Executor) Execute(ctx context.Context, tool *Tool, args map[string]any) (*mcp.CallToolResult, error)
Execute sends the tool call upstream and converts the response into a tool result. Transport failures and non-2xx statuses become error results (the model sees them); only argument/binding problems surface as Go errors.
type FactoryOption ¶
type FactoryOption func(*factoryOptions)
FactoryOption configures Factory.
func WithConfigDir ¶
func WithConfigDir(dir string) FactoryOption
WithConfigDir sets the directory openapi_config.spec_file paths resolve against.
type HeaderResolver ¶
HeaderResolver supplies the headers for one upstream call.
type Operation ¶
type Operation struct {
ID string
Method string // upper-case HTTP method
Path string
Summary string
Description string
Deprecated bool
Tags []string
Params []Parameter
Body *RequestBody
Security []SecurityRequirement // op-level requirements; meaningful when HasSecurity
HasSecurity bool // op declared its own security (even if empty)
Servers []string // op/path-level server override
Unsupported string // non-empty: reason the operation cannot become a tool
}
Operation is one HTTP operation of the spec.
type Parameter ¶
type Parameter struct {
Name string
In string
Description string
Required bool
Explode *bool
Style string // form | simple | spaceDelimited | pipeDelimited | tabDelimited | deepObject | label | matrix
Schema map[string]any
ContentType string // set when declared through `content`: the value is serialized whole in this media type
Unsupported string // why no value can be sent (schema false); checked after operation-level replacement
}
Parameter is one path/query/header/cookie/querystring parameter.
type ParseOptions ¶
type ParseOptions struct {
MaxBytes int // 0 = MaxSpecBytes
SpecURL string // used to resolve relative server URLs
}
ParseOptions tunes Parse.
type PreviewResult ¶
type PreviewResult struct {
Title string `json:"title"`
Version string `json:"version"`
OpenAPIVersion string `json:"openapi_version"`
Description string `json:"description,omitempty"`
Servers []string `json:"servers"`
BaseURL string `json:"base_url,omitempty"`
OperationServers bool `json:"operation_servers,omitempty"` // no base URL needed: every tool's operation declares its own server
SecuritySchemes []SecurityScheme `json:"security_schemes"`
Tools []ToolSummary `json:"tools"`
Unsupported []UnsupportedOperation `json:"unsupported"`
Warnings []string `json:"warnings"`
ToolCount int `json:"tool_count"`
SpecSize int `json:"spec_size"`
SpecHash string `json:"spec_hash"`
}
PreviewResult is the API-facing summary of a parsed spec.
type RequestBody ¶
type RequestBody struct {
ContentType string
Required bool
Description string
Schema map[string]any
FormDelimiters map[string]string // Form fields whose arrays join into one value with this delimiter (Swagger 2.0 collectionFormat)
FormObjectStyles map[string]string // Form fields whose objects use a declared style: form (exploded), formPacked or deepObject
}
RequestBody is the selected request body representation of an operation.
type SecurityRequirement ¶
SecurityRequirement maps scheme names to scopes; all listed schemes apply together.
type SecurityScheme ¶
type SecurityScheme struct {
Name string `json:"name"`
Type string `json:"type"` // apiKey | http | oauth2 | openIdConnect | mutualTLS
In string `json:"in,omitempty"` // apiKey: header | query | cookie
ParamName string `json:"param_name,omitempty"` // apiKey: header/query/cookie name
Scheme string `json:"scheme,omitempty"` // http: bearer | basic | ...
Description string `json:"description,omitempty"`
Supported bool `json:"supported"`
Reason string `json:"reason,omitempty"` // why unsupported
}
SecurityScheme is one components.securitySchemes (or securityDefinitions) entry.
func SelectSecurity ¶
func SelectSecurity(op Operation, doc *Document, creds map[string]schemas.MCPOpenAPICredential, supplied http.Header) []SecurityScheme
SelectSecurity picks the schemes to apply to a call: the operation's own requirements when it declares any, else the document's. The first requirement whose schemes are all supported and all have a configured credential wins; an empty requirement means the operation accepts anonymous calls and selects nothing. A header-carried scheme also counts as met when supplied already carries its header (one each caller forwards, with no stored credential); it is then left to that header and not returned. When no requirement is satisfiable the call is sent without spec-mapped credentials (static headers from the admin still apply).
type ServerOptions ¶
type ServerOptions struct {
ClientName string
Synthesis *Synthesis
BaseURL string // resolved upstream base
BaseURLExplicit bool // true when the admin set it; op-level servers are then ignored
Credentials map[string]schemas.MCPOpenAPICredential
HTTPClient *http.Client
Headers HeaderResolver
MaxResponseBytes int
Logger schemas.Logger
}
ServerOptions configures BuildServer.
type Synthesis ¶
type Synthesis struct {
Document *Document
Tools []Tool
Unsupported []UnsupportedOperation
Warnings []string
}
Synthesis is the result of turning a Document into tools.
func Synthesize ¶
func Synthesize(doc *Document, opts SynthesizeOptions) (*Synthesis, error)
Synthesize turns a Document into tools: one per supported operation, named, described, with a flattened input schema and the bindings the executor needs. Operations that cannot become tools are listed in Unsupported with a reason; the result is still usable as long as at least one tool was produced (the factory enforces that, the preview shows both lists).
type SynthesizeOptions ¶
type SynthesizeOptions struct {
ClientName string // budgets the tool-name length against the "<client>-" prefix
IncludeDeprecated bool
}
SynthesizeOptions tunes Synthesize.
type Tool ¶
type Tool struct {
Name string
Operation Operation
Description string
InputSchema json.RawMessage
Args []ArgBinding
Body *BodyBinding
Annotations mcp.ToolAnnotation
Warnings []string
}
Tool is a synthesized MCP tool plus the bindings needed to execute it.
type ToolSummary ¶
type ToolSummary struct {
Name string `json:"name"`
OperationID string `json:"operation_id,omitempty"`
Method string `json:"method"`
Path string `json:"path"`
Summary string `json:"summary,omitempty"`
Description string `json:"description,omitempty"`
Deprecated bool `json:"deprecated,omitempty"`
InputSchema json.RawMessage `json:"input_schema"`
Annotations mcp.ToolAnnotation `json:"annotations"`
Warnings []string `json:"warnings,omitempty"`
}
ToolSummary is one synthesized tool as shown in a preview.
type UnsupportedOperation ¶
type UnsupportedOperation struct {
OperationID string `json:"operation_id,omitempty"`
Method string `json:"method"`
Path string `json:"path"`
Reason string `json:"reason"`
}
UnsupportedOperation is an operation that did not become a tool.
type Version ¶
type Version struct {
Raw string // the declared version string
Swagger2 bool // Swagger 2.0 document
Major int // 2 or 3
Minor int // 0, 1, 2 (clamped to 2 for newer 3.x minors)
}
Version is the detected spec version.
func DetectVersion ¶
DetectVersion reads the swagger/openapi field of a decoded document. It returns the version, any best-effort warnings, or an error for documents that are not OpenAPI or declare a major this package does not understand.
func (Version) HasAdditionalOperations ¶
HasAdditionalOperations reports whether path items may carry additionalOperations (3.2+).
func (Version) HasNullableKeyword ¶
HasNullableKeyword reports whether `nullable: true` is the null idiom (2.0 via x-nullable, 3.0) as opposed to `type: [..., "null"]` (3.1+).
func (Version) HasQueryMethod ¶
HasQueryMethod reports whether `query` is a first-class path item method (3.2+).
func (Version) HasQuerystringParam ¶
HasQuerystringParam reports whether `in: querystring` parameters exist (3.2+).
func (Version) HasRequestBody ¶
HasRequestBody reports whether bodies are declared via requestBody (3.x) rather than body/formData parameters (2.0).