openapimcp

package
v1.11.1 Latest Latest
Warning

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

Go to latest
Published: Oct 29, 2026 License: Apache-2.0 Imports: 22 Imported by: 0

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

View Source
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
)
View Source
const (
	InPath        = "path"
	InQuery       = "query"
	InHeader      = "header"
	InCookie      = "cookie"
	InQuerystring = "querystring"
)

Parameter locations. The OpenAPI values plus 3.2's querystring.

View Source
const (
	ContentTypeJSON = "application/json"
	ContentTypeForm = "application/x-www-form-urlencoded"
	ContentTypeText = "text/plain"
)

Request body content types the executor can encode.

Variables

View Source
var ErrSpecTooLarge = fmt.Errorf("openapi spec exceeds %d bytes", MaxSpecBytes)

ErrSpecTooLarge is returned when a spec exceeds MaxSpecBytes.

View Source
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

func BuildDescription(op Operation) string

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

func DedupeToolNames(tools []Tool, maxLen int) []string

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

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 LoadBytes

func LoadBytes(data []byte) ([]byte, error)

LoadBytes accepts a spec supplied as text (paste, upload, inline config).

func LoadFile

func LoadFile(path, baseDir string) ([]byte, error)

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

func LoadURL(ctx context.Context, client *http.Client, specURL string) ([]byte, string, error)

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

func NormalizeSchema(s map[string]any) map[string]any

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 SpecHash

func SpecHash(data []byte) string

SpecHash is the stable identity of a spec's bytes.

func ToolName

func ToolName(op Operation, maxLen int) string

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.

func Parse

func Parse(data []byte, opts ParseOptions) (*Document, error)

Parse decodes a JSON or YAML spec into the version-independent Document.

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

type HeaderResolver func(ctx context.Context) (http.Header, error)

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

type SecurityRequirement map[string][]string

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).

func (*Synthesis) ToolNames

func (s *Synthesis) ToolNames() []string

ToolNames lists the synthesized tool names, for validating tools_to_execute.

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

func DetectVersion(tree map[string]any) (Version, []string, error)

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

func (v Version) HasAdditionalOperations() bool

HasAdditionalOperations reports whether path items may carry additionalOperations (3.2+).

func (Version) HasNullableKeyword

func (v Version) HasNullableKeyword() bool

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

func (v Version) HasQueryMethod() bool

HasQueryMethod reports whether `query` is a first-class path item method (3.2+).

func (Version) HasQuerystringParam

func (v Version) HasQuerystringParam() bool

HasQuerystringParam reports whether `in: querystring` parameters exist (3.2+).

func (Version) HasRequestBody

func (v Version) HasRequestBody() bool

HasRequestBody reports whether bodies are declared via requestBody (3.x) rather than body/formData parameters (2.0).

func (Version) HasSelf

func (v Version) HasSelf() bool

HasSelf reports whether the document may declare $self (3.2+).

func (Version) String

func (v Version) String() string

String returns the declared version string.

Jump to

Keyboard shortcuts

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