mcp-from-openapi 2.5.1 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,14 +18,14 @@ paths:
18
18
  /users/{id}:
19
19
  post:
20
20
  parameters:
21
- - name: id # path
21
+ - name: id # path
22
22
  in: path
23
23
  requestBody:
24
24
  content:
25
25
  application/json:
26
26
  schema:
27
27
  properties:
28
- id: # body -- CONFLICT!
28
+ id: # body -- CONFLICT!
29
29
  type: string
30
30
  ```
31
31
 
@@ -50,14 +50,18 @@ Now you know exactly how to build the HTTP request.
50
50
 
51
51
  ## Features
52
52
 
53
- - **Smart Parameter Handling** -- Automatic conflict detection and resolution across path, query, header, cookie, and body
54
- - **Complete Schemas** -- Input schema combines all parameters; output schema from responses (with oneOf unions)
55
- - **Security Resolution** -- Framework-agnostic auth for Bearer, Basic, Digest, API Key, OAuth2, OpenID, mTLS, HMAC, AWS Sig V4
56
- - **SSRF Prevention** -- Blocks internal IPs, localhost, and cloud metadata endpoints by default during `$ref` resolution
53
+ - **Built-in Request Builder** -- `buildHttpRequest()` applies the full OpenAPI serialization table (form/deepObject/pipeDelimited queries, label/matrix paths, multipart, binary, `wholeBody`) so you never hand-write request assembly
54
+ - **Client Compatibility Targets** -- `target: 'claude' | 'openai' | 'gemini' | 'strict'` emits schemas each client actually accepts (inlined refs, closed objects, collapsed unions, demoted formats)
55
+ - **Curation-Grade Filtering** -- Filter by tag, method, path glob (`/admin/**`), operationId, a `readOnlyOnly` safety switch, and `x-mcp` extension flags with root < path < operation precedence
56
+ - **Smart Parameter Handling** -- Automatic conflict detection and resolution across path, query, header, cookie, and body; `allOf` bodies flatten, union and binary bodies map cleanly (`wholeBody`, `binary` markers)
57
+ - **Complete Schemas** -- Input schema combines all parameters; output schema from responses (with oneOf unions); clean JSON Schema 2020-12 output (`nullable` unions, normalized `examples`)
58
+ - **MCP-Native Tools** -- `title` and tool `annotations` (readOnly/destructive/idempotent hints) inferred from HTTP semantics, overridable via the `x-mcp` extension family; spec-compliant tool names (64-char cap, stable hash truncation, collision dedup); deterministic tool ordering for prompt-cache friendliness; `toSdkTool()` for one-line SDK registration
59
+ - **Security Resolution** -- Framework-agnostic auth for Bearer, Basic, Digest, API Key, OAuth2, OpenID, mTLS, HMAC, AWS Sig V4; per-scheme `includeSecurityInInput`
60
+ - **SSRF Prevention** -- Blocks internal IPs, localhost, and cloud metadata endpoints by default during `$ref` resolution; one-flag `secureDefaults` posture for untrusted specs
57
61
  - **Multiple Input Sources** -- Load from URL, file, YAML string, or JSON object
58
62
  - **Rich Metadata** -- Authentication, servers, tags, deprecation, external docs, `x-frontmcp` extension
59
- - **Production Ready** -- Full TypeScript support, validation, structured errors, 80%+ test coverage
60
- - **MCP Native** -- Designed specifically for Model Context Protocol integration
63
+ - **Production Ready** -- Full TypeScript support, validation, structured errors, 100% test coverage (enforced)
64
+ - **Runtime Agnostic** -- Works on Node and V8 isolates (Cloudflare Workers) alike
61
65
 
62
66
  ## Installation
63
67
 
@@ -72,86 +76,88 @@ pnpm add mcp-from-openapi
72
76
  ## Quick Start
73
77
 
74
78
  ```typescript
75
- import { OpenAPIToolGenerator } from 'mcp-from-openapi';
79
+ import { OpenAPIToolGenerator } from "mcp-from-openapi";
76
80
 
77
81
  // Load an OpenAPI spec
78
- const generator = await OpenAPIToolGenerator.fromURL('https://api.example.com/openapi.json');
82
+ const generator = await OpenAPIToolGenerator.fromURL(
83
+ "https://api.example.com/openapi.json",
84
+ );
79
85
 
80
86
  // Generate MCP tools
81
87
  const tools = await generator.generateTools();
82
88
 
83
89
  // Each tool has everything you need
84
90
  tools.forEach((tool) => {
85
- console.log(tool.name); // "createUser"
86
- console.log(tool.inputSchema); // Combined schema for all params
87
- console.log(tool.outputSchema); // Response schema
88
- console.log(tool.mapper); // How to build the HTTP request
89
- console.log(tool.metadata); // Auth, servers, tags, etc.
91
+ console.log(tool.name); // "createUser"
92
+ console.log(tool.title); // "Create a user" (from summary/extensions)
93
+ console.log(tool.annotations); // { readOnlyHint: false, destructiveHint: true, ... }
94
+ console.log(tool.inputSchema); // Combined schema for all params
95
+ console.log(tool.outputSchema); // Response schema
96
+ console.log(tool.mapper); // How to build the HTTP request
97
+ console.log(tool.metadata); // Auth, servers, tags, etc.
90
98
  });
91
99
  ```
92
100
 
93
- ## Using the Mapper
101
+ ## Building Requests
94
102
 
95
- The mapper tells you how to convert tool inputs into an HTTP request:
103
+ `buildHttpRequest()` turns a tool plus input values into a ready-to-send request — style/explode serialization, deepObject queries, multipart, binary, and `wholeBody` handled correctly:
96
104
 
97
105
  ```typescript
98
- function buildRequest(tool: McpOpenAPITool, input: Record<string, any>) {
99
- let path = tool.metadata.path;
100
- const query = new URLSearchParams();
101
- const headers: Record<string, string> = {};
102
- let body: Record<string, any> | undefined;
103
-
104
- for (const m of tool.mapper) {
105
- const value = input[m.inputKey];
106
- if (value === undefined) continue;
107
-
108
- switch (m.type) {
109
- case 'path':
110
- path = path.replace(`{${m.key}}`, encodeURIComponent(value));
111
- break;
112
- case 'query':
113
- query.set(m.key, String(value));
114
- break;
115
- case 'header':
116
- headers[m.key] = String(value);
117
- break;
118
- case 'body':
119
- if (!body) body = {};
120
- body[m.key] = value;
121
- break;
122
- }
123
- }
106
+ import { buildHttpRequest } from "mcp-from-openapi";
107
+
108
+ const request = buildHttpRequest(tool, { id: "42", filter: { tag: "news" } });
109
+ // { url: 'https://api.example.com/users/42?filter[tag]=news',
110
+ // method: 'GET', headers: {...}, body: undefined, ... }
124
111
 
125
- const baseUrl = tool.metadata.servers?.[0]?.url ?? '';
126
- const qs = query.toString();
112
+ await fetch(request.url, {
113
+ method: request.method,
114
+ headers: request.headers,
115
+ body: request.body as BodyInit,
116
+ });
117
+ ```
127
118
 
128
- return {
129
- url: `${baseUrl}${path}${qs ? '?' + qs : ''}`,
130
- method: tool.metadata.method.toUpperCase(),
131
- headers,
132
- body: body ? JSON.stringify(body) : undefined,
133
- };
119
+ The `mapper` array stays public for anyone who needs custom request assembly — see [Request Builder](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/request-builder.md) and [Parameter Conflicts](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/parameter-conflicts.md) for its contract.
120
+
121
+ ## Serving with the Official MCP SDK
122
+
123
+ ```typescript
124
+ import { toSdkTool, buildHttpRequest } from "mcp-from-openapi";
125
+ import { fromJsonSchema } from "@modelcontextprotocol/server"; // SDK v2
126
+
127
+ for (const tool of await generator.generateTools({ target: "claude" })) {
128
+ server.registerTool(...toSdkTool(tool, { fromJsonSchema }), async (input) => {
129
+ const request = buildHttpRequest(tool, input);
130
+ const response = await fetch(request.url, {
131
+ method: request.method,
132
+ headers: request.headers,
133
+ body: request.body as BodyInit,
134
+ });
135
+ return { content: [{ type: "text", text: await response.text() }] };
136
+ });
134
137
  }
135
138
  ```
136
139
 
137
140
  ## Documentation
138
141
 
139
- | Document | Description |
140
- |----------|-------------|
141
- | [Getting Started](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/getting-started.md) | Loading specs, generating tools, building requests |
142
- | [Configuration](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/configuration.md) | LoadOptions, GenerateOptions, RefResolutionOptions |
143
- | [Parameter Conflicts](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/parameter-conflicts.md) | How conflict detection and resolution works |
144
- | [Response Schemas](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/response-schemas.md) | Output schemas, status codes, oneOf unions |
145
- | [Security](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/security.md) | SecurityResolver, all auth types, custom resolvers |
146
- | [SSRF Prevention](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/ssrf-prevention.md) | Ref resolution security, blocked IPs and hosts |
147
- | [Format Resolution](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/FORMAT_RESOLUTION.md) | Format-to-schema enrichment (uuid, date-time, email, int32, etc.) |
148
- | [Naming Strategies](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/naming-strategies.md) | Custom tool naming and conflict resolvers |
149
- | [SchemaBuilder](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/schema-builder.md) | JSON Schema utility methods |
150
- | [Error Handling](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/error-handling.md) | Error classes, context, and patterns |
151
- | [x-frontmcp Extension](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/x-frontmcp.md) | Custom OpenAPI extension for MCP annotations |
152
- | [API Reference](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/api-reference.md) | Complete types, interfaces, and exports |
153
- | [Examples](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/examples.md) | MCP server, Zod, filtering, security, and more |
154
- | [Architecture](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/architecture.md) | System overview, data flow, design patterns |
142
+ | Document | Description |
143
+ | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
144
+ | [Getting Started](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/getting-started.md) | Loading specs, generating tools, building requests |
145
+ | [Configuration](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/configuration.md) | LoadOptions, GenerateOptions, RefResolutionOptions |
146
+ | [Parameter Conflicts](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/parameter-conflicts.md) | How conflict detection and resolution works |
147
+ | [Request Builder](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/request-builder.md) | `buildHttpRequest` — full OpenAPI parameter serialization |
148
+ | [Client Targets](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/client-targets.md) | Per-client schema dialects (Claude, OpenAI, Gemini) |
149
+ | [Response Schemas](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/response-schemas.md) | Output schemas, status codes, oneOf unions |
150
+ | [Annotations & Extensions](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/annotations.md) | Tool title, annotation inference, `x-mcp` extension family |
151
+ | [Security](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/security.md) | SecurityResolver, all auth types, custom resolvers |
152
+ | [SSRF Prevention](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/ssrf-prevention.md) | Ref resolution security, blocked IPs and hosts |
153
+ | [Format Resolution](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/FORMAT_RESOLUTION.md) | Format-to-schema enrichment (uuid, date-time, email, int32, etc.) |
154
+ | [Naming Strategies](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/naming-strategies.md) | Custom tool naming and conflict resolvers |
155
+ | [SchemaBuilder](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/schema-builder.md) | JSON Schema utility methods |
156
+ | [Error Handling](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/error-handling.md) | Error classes, context, and patterns |
157
+ | [x-frontmcp Extension](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/x-frontmcp.md) | Custom OpenAPI extension for MCP annotations |
158
+ | [API Reference](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/api-reference.md) | Complete types, interfaces, and exports |
159
+ | [Examples](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/examples.md) | MCP server, Zod, filtering, security, and more |
160
+ | [Architecture](https://github.com/agentfront/mcp-from-openapi/blob/main/docs/architecture.md) | System overview, data flow, design patterns |
155
161
 
156
162
  ## Requirements
157
163
 
@@ -0,0 +1,57 @@
1
+ import type { HTTPMethod, OperationObject, ToolAnnotations } from './types';
2
+ /**
3
+ * Tool-level overrides read from the `x-mcp` extension family on an operation.
4
+ */
5
+ export interface ExtensionToolOverrides {
6
+ /**
7
+ * Operation explicitly excluded from tool generation
8
+ * (`x-mcp: false`, `x-mcp: { enabled: false }`, or
9
+ * `x-speakeasy-mcp: { disabled: true }`).
10
+ */
11
+ disabled?: boolean;
12
+ /**
13
+ * Tool name override (still normalized to MCP name rules).
14
+ */
15
+ name?: string;
16
+ /**
17
+ * Display title override.
18
+ */
19
+ title?: string;
20
+ /**
21
+ * LLM-facing description override.
22
+ */
23
+ description?: string;
24
+ /**
25
+ * Annotation overrides, merged field-by-field over inferred values.
26
+ */
27
+ annotations?: ToolAnnotations;
28
+ }
29
+ /**
30
+ * Default MCP tool annotations per HTTP method, derived from HTTP semantics
31
+ * (RFC 9110 safety/idempotency): safe methods are read-only and idempotent;
32
+ * PUT/DELETE are idempotent but may destroy state; POST/PATCH are neither
33
+ * safe nor idempotent. `openWorldHint` is false throughout — tools generated
34
+ * from a spec target one known API backend, a closed world.
35
+ */
36
+ export declare function inferAnnotationsFromMethod(method: HTTPMethod): ToolAnnotations;
37
+ /**
38
+ * Resolve whether an operation is enabled for tool generation, honoring the
39
+ * `x-mcp` extension at every level with harsha-compatible precedence:
40
+ * root (document) < path item < operation. A root-level `x-mcp: false` flips
41
+ * the whole spec to opt-in; a path or operation level `x-mcp: true` (or
42
+ * `{ enabled: true }`) re-enables its subtree. At the operation level the
43
+ * whole extension family participates (`x-speakeasy-mcp: { disabled }` too),
44
+ * with the family's own precedence.
45
+ */
46
+ export declare function resolveExtensionEnabled(document: object, pathItem: object, operation: OperationObject): boolean;
47
+ /**
48
+ * Extract tool overrides from the `x-mcp` extension family on an operation.
49
+ *
50
+ * Precedence (ascending — later overrides earlier, field-by-field):
51
+ * 1. `x-speakeasy-mcp` — `{ disabled, name, title, description, readOnlyHint, ... }`
52
+ * (Speakeasy places annotation hints at the top level of the object)
53
+ * 2. `x-mcp` — `false` to exclude, or `{ enabled, name, title, description, annotations }`
54
+ * 3. `x-frontmcp` — the canonical extension for this stack; its `annotations`
55
+ * (including `annotations.title`) win over the generic variants
56
+ */
57
+ export declare function extractExtensionOverrides(operation: OperationObject): ExtensionToolOverrides;
@@ -0,0 +1,55 @@
1
+ import type { JsonSchema } from './types';
2
+ /**
3
+ * Client dialect targets. Every MCP client accepts a different JSON Schema
4
+ * subset; a target applies the transforms that make schemas valid for it:
5
+ *
6
+ * - `strict` — the safe baseline for all clients: local `$ref`/`$defs`
7
+ * inlined, arrays always carry `items`, root-level compositions collapsed
8
+ * - `claude` — `strict` (Claude additionally rejects top-level unions on
9
+ * `input_schema` and caps tool names at 64 chars — both already covered by
10
+ * the generator's defaults)
11
+ * - `openai` — `strict` + every object closed (`additionalProperties: false`,
12
+ * required by OpenAI strict function calling)
13
+ * - `gemini` — `strict` + unions collapsed at every level and unsupported
14
+ * `format` values demoted to descriptions (Gemini rejects `$ref`/`$defs`,
15
+ * union-heavy schemas, and most string formats)
16
+ */
17
+ export type ClientTarget = 'claude' | 'openai' | 'gemini' | 'strict';
18
+ /**
19
+ * Resolve local `$ref` pointers against the schema's own `$defs`/`definitions`
20
+ * and strip the definition blocks. Cycles and unresolvable pointers become
21
+ * permissive `{}` nodes with an explanatory description. (Generated schemas
22
+ * are usually fully dereferenced already — this covers `dereference: false`
23
+ * flows and hand-fed schemas.)
24
+ */
25
+ export declare function inlineLocalRefs(schema: JsonSchema): JsonSchema;
26
+ /** Ensure every array schema carries an `items` schema (permissive when absent). */
27
+ export declare function ensureArrayItems(schema: JsonSchema): JsonSchema;
28
+ /**
29
+ * Collapse ROOT-level compositions only: `allOf` merges; `oneOf`/`anyOf`
30
+ * become a permissive node that documents the variants and preserves them
31
+ * under `x-variants`. The nullable wrapper unwraps to its non-null member.
32
+ */
33
+ export declare function collapseRootCompositions(schema: JsonSchema): JsonSchema;
34
+ /**
35
+ * Collapse unions at EVERY level (Gemini): nullable wrappers unwrap with a
36
+ * note; other `oneOf`/`anyOf` keep their first variant and document the
37
+ * omitted alternatives; `allOf` merges.
38
+ */
39
+ export declare function collapseNestedUnions(schema: JsonSchema): JsonSchema;
40
+ /** Demote unsupported `format` values into descriptions (Gemini). */
41
+ export declare function demoteFormats(schema: JsonSchema, supported?: Set<string>): JsonSchema;
42
+ /** Close every object node (`additionalProperties: false`) — OpenAI strict mode. */
43
+ export declare function enforceClosedObjects(schema: JsonSchema): JsonSchema;
44
+ /**
45
+ * OpenAI strict function calling requires EVERY property to be listed in
46
+ * `required`; optional fields are expressed as required-but-nullable. This
47
+ * rewrites each object node accordingly (typed optionals gain a `null` type;
48
+ * untyped optionals are already permissive).
49
+ */
50
+ export declare function requireAllProperties(schema: JsonSchema): JsonSchema;
51
+ /**
52
+ * Apply a client target's transform pipeline to a schema. Also exported for
53
+ * standalone use on schemas that did not come from the generator.
54
+ */
55
+ export declare function applyClientTarget(schema: JsonSchema, target: ClientTarget): JsonSchema;
package/errors.d.ts CHANGED
@@ -43,6 +43,13 @@ export declare class ValidationError extends OpenAPIToolError {
43
43
  export declare class GenerationError extends OpenAPIToolError {
44
44
  constructor(message: string, context?: Record<string, any>);
45
45
  }
46
+ /**
47
+ * Error thrown when an HTTP request cannot be built from a tool's mapper
48
+ * (missing required parameters, unserializable values, injection attempts)
49
+ */
50
+ export declare class RequestBuildError extends OpenAPIToolError {
51
+ constructor(message: string, context?: Record<string, any>);
52
+ }
46
53
  /**
47
54
  * Error thrown when a schema is invalid
48
55
  */