@ttoss/http-server-mcp-openapi 0.2.15 → 0.4.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
@@ -31,12 +31,15 @@ registerOpenApiTools({
31
31
  server,
32
32
  spec: openApiDocument,
33
33
  // You own how the request is executed — base URL, auth, fetch impl.
34
- callApi: async ({ method, url, body }) => {
34
+ // `headers` is what createMcpRouter's `getApiHeaders` produced for this
35
+ // MCP request — typically the caller's credentials.
36
+ callApi: async ({ method, url, body, headers }) => {
35
37
  const res = await fetch(`https://api.example.com${url}`, {
36
38
  method,
37
- headers: { 'Content-Type': 'application/json' },
39
+ headers: { ...headers, 'Content-Type': 'application/json' },
38
40
  body: body ? JSON.stringify(body) : undefined,
39
41
  });
42
+ if (res.status === 204) return undefined;
40
43
  return res.json();
41
44
  },
42
45
  });
@@ -55,9 +58,9 @@ Each OpenAPI operation with an `operationId` and a supported HTTP method
55
58
  | OpenAPI | MCP tool |
56
59
  | ------------------------- | -------------------------------------------- |
57
60
  | `operationId: listAgents` | tool name `list-agents` (kebab-case) |
58
- | path/query/body params | a single camelCase `inputSchema` object |
61
+ | path/query/body params | a single `inputSchema` object |
59
62
  | `$ref`, `oneOf`, `anyOf` | dereferenced and merged into a flat schema |
60
- | snake_case body fields | camelCase tool inputs, mapped back on call |
63
+ | snake_case names | camelCase tool inputs, mapped back on call |
61
64
  | operation `description` | tool description (quotes/newlines sanitised) |
62
65
 
63
66
  Path params are always required strings. Query and body params carry their
@@ -66,12 +69,18 @@ body property declared as a single-entry `allOf` (usually `allOf: [{ $ref }]`
66
69
  beside its own `description`) takes `type`, `nullable` and `items` from the
67
70
  referenced schema; a multi-entry `allOf` is forwarded verbatim, and a property
68
71
  with no declared type is advertised untyped so it accepts any value.
72
+ OpenAPI's `nullable` becomes JSON Schema at every depth — inside `items`,
73
+ `properties`, `additionalProperties` and `oneOf` / `anyOf` / `allOf`
74
+ alternatives: `nullable: true` adds `'null'` to the `type`, and the keyword is
75
+ dropped.
69
76
  Parameters declared at the **path-item level** (shared by every operation on a
70
77
  path) are merged into each operation; an operation-level parameter overrides a
71
78
  path-item one with the same `name`+`in`.
72
79
 
73
- Tool arguments are **camelCase** (`agentId`, `projectId`); the generated
74
- request path, query string, and body use the original **snake_case** names.
80
+ By default tool arguments are **camelCase** (`agentId`, `projectId`) and the
81
+ generated request path, query string, and body use the spec's original names.
82
+ Set `argumentNames: 'verbatim'` to use the spec's names as the arguments too,
83
+ so the MCP contract matches the REST contract exactly.
75
84
 
76
85
  Query params honour their declared `style` and `explode`. `form` (the default)
77
86
  repeats array values, `spaceDelimited`/`pipeDelimited` join them, and
@@ -80,13 +89,18 @@ repeats array values, `spaceDelimited`/`pipeDelimited` join them, and
80
89
 
81
90
  ## `registerOpenApiTools`
82
91
 
83
- | Field | Description |
84
- | ---------- | ------------------------------------------------------------------------------------------------------------- |
85
- | `server` | The `McpServer` to register tools on. |
86
- | `spec` | One OpenAPI document, or an array of them (tools are flattened). |
87
- | `callApi` | Runs the resolved `{ method, url, body, tool }` request and returns the raw data. |
88
- | `toText?` | Serialises the raw data into the tool's text payload. Defaults to pretty JSON; strings pass through verbatim. |
89
- | `options?` | See [Options](#options). |
92
+ | Field | Description |
93
+ | ------------------- | ------------------------------------------------------------------------------------------------------------- |
94
+ | `server` | The `McpServer` to register tools on. |
95
+ | `spec` | One OpenAPI document, or an array of them (tools are flattened). |
96
+ | `callApi` | Runs the resolved `{ method, url, body, tool, headers }` request and returns the raw data. |
97
+ | `toText?` | Serialises the raw data into the tool's text payload. Defaults to pretty JSON; strings pass through verbatim. |
98
+ | `serverParameters?` | Supplies server-managed path/query parameter values. See [Server-managed values](#server-managed-values). |
99
+ | `options?` | See [Options](#options). |
100
+
101
+ The default `toText` answers `NO_CONTENT_TEXT` (`Succeeded. The operation
102
+ returned no content.`) when `callApi` resolves `undefined` or `''`, so a `204`
103
+ reaches the client as a success.
90
104
 
91
105
  Returns the list of `ToolDefinition`s that were registered.
92
106
 
@@ -109,7 +123,7 @@ for (const tool of tools) {
109
123
 
110
124
  Each `ToolDefinition` exposes `name`, `description`, `inputSchema`, `method`,
111
125
  `pathTemplate`, `operationId`, the `path`/`query`/`body` builders,
112
- `acceptedBodyFields`, and `extensions`.
126
+ `acceptedBodyFields`, `extensions`, and `serverManagedParameters`.
113
127
 
114
128
  ## Options
115
129
 
@@ -120,17 +134,54 @@ registerOpenApiTools({
120
134
  callApi,
121
135
  options: {
122
136
  excludeExtension: 'x-mcp-exclude', // operations flagged truthy are skipped
123
- serverManagedExtension: 'x-mcp-server-managed', // body fields hidden from the input schema
137
+ serverManagedExtension: 'x-mcp-server-managed', // or several: ['x-a', 'x-b']
138
+ argumentNames: 'camelCase', // or 'verbatim'
139
+ documents: { './tags.yaml': tagsDocument }, // targets of cross-file $refs
124
140
  },
125
141
  });
126
142
  ```
127
143
 
128
144
  - **`excludeExtension`** (default `x-mcp-exclude`) — an operation with this
129
145
  extension set truthy is omitted from the tool surface.
130
- - **`serverManagedExtension`** (default `x-mcp-server-managed`) — a request-body
131
- property with this extension set truthy is hidden from the tool's
132
- `inputSchema` (the caller can't set it) but still appears in
133
- `acceptedBodyFields`.
146
+ - **`serverManagedExtension`** (default `x-mcp-server-managed`) — one name or
147
+ an array of names; see [Server-managed values](#server-managed-values).
148
+ - **`argumentNames`** (default `camelCase`) — `verbatim` keeps the spec's
149
+ parameter and property names as tool argument names.
150
+ - **`documents`** — sibling documents for `$ref`s with a file part, keyed by
151
+ that part as the spec writes it (a leading `./` is optional). In
152
+ `./tags.yaml#/components/schemas/Tag` the key is `./tags.yaml`; refs inside
153
+ a sibling resolve against that sibling. A ref to a file missing from the map
154
+ resolves to an empty schema, which accepts any value.
155
+
156
+ ### Server-managed values
157
+
158
+ A value flagged with `serverManagedExtension` is never offered to the model:
159
+
160
+ - A **request-body property** is hidden from `inputSchema` and never sent (the
161
+ API sets it itself). It still appears in `acceptedBodyFields`.
162
+ - A **path or query parameter** is hidden from `inputSchema` and listed in
163
+ `tool.serverManagedParameters`. `registerOpenApiTools` discards anything the
164
+ model sent for it and fills it from `serverParameters`, keyed by spec name:
165
+
166
+ ```typescript
167
+ registerOpenApiTools({
168
+ server,
169
+ spec,
170
+ callApi,
171
+ serverParameters: ({ tool, headers }) => ({
172
+ project_id: projectIdFromToken(headers.Authorization),
173
+ }),
174
+ });
175
+ ```
176
+
177
+ With `openApiToToolDefinitions`, set each entry's `argName` in the args before
178
+ calling `tool.path` / `tool.query`.
179
+
180
+ A **string** extension value pins the parameter: `wait` declared with
181
+ `x-mcp-server-managed: 'true'` is always sent as `wait=true`. `tool.path` and
182
+ `tool.query` apply pinned values themselves, over anything in the args or
183
+ `serverParameters`, and the entry in `serverManagedParameters` carries it as
184
+ `value`.
134
185
 
135
186
  ### Reading custom extensions
136
187