@powerduck/openapi-mcp-server 1.0.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 ADDED
@@ -0,0 +1,116 @@
1
+ A production-oriented TypeScript library and runtime that converts OpenAPI documents into MCP services.
2
+
3
+ ## Features
4
+
5
+ - Tools generated from OpenAPI operations
6
+ - Prompts generated from API metadata and operations
7
+ - Resources generated from the OpenAPI catalog
8
+ - HTTP and STDIO MCP transports
9
+ - Admin Web UI
10
+ - Persistent runtime state
11
+ - Stronger validation and safer request execution
12
+ - Production-oriented structure for future SaaS evolution
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ npm install
18
+ ```
19
+
20
+ ## Build
21
+
22
+ ```bash
23
+ npm run build
24
+ ```
25
+
26
+ ## Start web runtime
27
+
28
+ ```bash
29
+ npm run start -- serve --port 3000 --host 127.0.0.1
30
+ ```
31
+
32
+ ## Start STDIO runtime
33
+
34
+ ```bash
35
+ node dist/cli.js serve --transport stdio --spec ./openapi.yaml
36
+ ```
37
+
38
+ ## Demo
39
+
40
+ ```bash
41
+ npm run demo
42
+ ```
43
+
44
+ ## Library usage
45
+
46
+ ```ts
47
+ import {
48
+ parseSpecContent,
49
+ generateTools,
50
+ generatePrompts,
51
+ generateResources,
52
+ buildMcpServer,
53
+ } from "openapi-mcp-production";
54
+ ```
55
+
56
+ ## CLI usage
57
+
58
+ ### Web mode
59
+
60
+ ```bash
61
+ openapi-mcp serve \
62
+ --transport web \
63
+ --port 3000 \
64
+ --host 127.0.0.1 \
65
+ --api-key your-admin-key
66
+ ```
67
+
68
+ ### STDIO mode
69
+
70
+ ```bash
71
+ openapi-mcp serve \
72
+ --transport stdio \
73
+ --spec ./openapi.yaml \
74
+ --base-url https://api.example.com
75
+ ```
76
+
77
+ ## Production notes
78
+
79
+ This version is production-oriented, but you should still add the following for SaaS deployment:
80
+
81
+ - tenant isolation
82
+ - database-backed project registry
83
+ - rate limits
84
+ - audit logs
85
+ - RBAC
86
+ - secure secret storage
87
+ - upstream host allowlists
88
+ - comprehensive test suites
89
+ - full OpenAPI parameter serialization coverage
90
+ - full requestBody content negotiation support
91
+
92
+ ## Project structure
93
+
94
+ - `src/core`: spec parsing, operation traversal, tool generation, request building, execution
95
+ - `src/mcp`: MCP server creation and transports
96
+ - `src/registry`: prompts and resources registries
97
+ - `src/runtime`: runtime service registry
98
+ - `src/server`: admin HTTP server
99
+ - `src/webui`: browser UI
100
+ - `src/config`: persistence helpers
101
+
102
+ ## Web UI
103
+
104
+ The UI supports:
105
+
106
+ - upload and paste OpenAPI documents
107
+ - view tools, prompts, and resources
108
+ - inspect runtime services
109
+ - stop services
110
+ - basic MCP debugging hints
111
+ - SSE endpoint discovery
112
+ - STDIO tooltip guidance
113
+
114
+ ## Important implementation policy
115
+
116
+ All source comments are written in American English.
@@ -0,0 +1,313 @@
1
+ import { Express } from 'express';
2
+ import { Server } from 'node:http';
3
+ import { Document } from '@scalar/openapi-types/3.2';
4
+ import { Prompt, PromptArgument, Resource, Tool } from '@modelcontextprotocol/sdk/types.js';
5
+
6
+ /** How the MCP server is exposed to a client. */
7
+ declare const TRANSPORT_MODES: readonly ["stdio", "web"];
8
+ type TransportMode = (typeof TRANSPORT_MODES)[number];
9
+ /** Where the currently loaded specification came from. */
10
+ declare const SPEC_SOURCES: readonly ["startup-file", "upload", "paste", "runtime"];
11
+ type SpecSource = (typeof SPEC_SOURCES)[number];
12
+ /** Lifecycle state of a managed MCP service. */
13
+ declare const SERVICE_STATUSES: readonly ["running", "stopped", "error"];
14
+ type ServiceStatus = (typeof SERVICE_STATUSES)[number];
15
+ /**
16
+ * Wire protocol a log entry or session belongs to.
17
+ *
18
+ * `streamable-http` is the transport introduced by the 2025 protocol revision
19
+ * and is preferred for new clients; `sse` remains for backward compatibility
20
+ * with clients pinned to the legacy HTTP+SSE transport.
21
+ */
22
+ declare const PROTOCOL_HINTS: readonly ["streamable-http", "sse", "stdio"];
23
+ type ProtocolHint = (typeof PROTOCOL_HINTS)[number];
24
+ /** Direction of a recorded log entry relative to this process. */
25
+ declare const LOG_DIRECTIONS: readonly ["request", "response", "internal"];
26
+ type LogDirection = (typeof LOG_DIRECTIONS)[number];
27
+ /** Severity used for filtering and colour-coding in the web console. */
28
+ declare const LOG_LEVELS: readonly ["debug", "info", "warn", "error"];
29
+ type LogLevel = (typeof LOG_LEVELS)[number];
30
+ /** OpenAPI parameter locations supported by the request builder. */
31
+ declare const PARAMETER_LOCATIONS: readonly ["path", "query", "header", "cookie"];
32
+ type ParameterLocation = (typeof PARAMETER_LOCATIONS)[number];
33
+ /** Request body serialization strategies supported by the HTTP executor. */
34
+ declare const BODY_ENCODINGS: readonly ["json", "form-urlencoded", "multipart", "text", "binary"];
35
+ type BodyEncoding = (typeof BODY_ENCODINGS)[number];
36
+ /** Narrow an untrusted value to a {@link SpecSource}. */
37
+ declare function isSpecSource(value: unknown): value is SpecSource;
38
+ /** Narrow an untrusted value to a {@link ServiceStatus}. */
39
+ declare function isServiceStatus(value: unknown): value is ServiceStatus;
40
+ /** Narrow an untrusted value to a {@link ProtocolHint}. */
41
+ declare function isProtocolHint(value: unknown): value is ProtocolHint;
42
+ /** Narrow an untrusted value to a {@link ParameterLocation}. */
43
+ declare function isParameterLocation(value: unknown): value is ParameterLocation;
44
+ /** Narrow an untrusted value to a {@link BodyEncoding}. */
45
+ declare function isBodyEncoding(value: unknown): value is BodyEncoding;
46
+ /** True for plain, non-array objects usable as an argument bag. */
47
+ declare function isPlainRecord(value: unknown): value is Record<string, unknown>;
48
+ /**
49
+ * Options accepted when starting the bundled web console and HTTP transport.
50
+ *
51
+ * Every field is optional except {@link ServerConfig.port} so that embedders can
52
+ * start a usable server with a single value.
53
+ */
54
+ interface ServerConfig {
55
+ /** TCP port to bind. Must be an integer in the range 0-65535. */
56
+ port: number;
57
+ /** Interface to bind. Defaults to `127.0.0.1` to avoid accidental exposure. */
58
+ host?: string | undefined;
59
+ /** Shared secret required by the admin API and HTTP transport when set. */
60
+ apiKey?: string | undefined;
61
+ /** Path to a specification loaded once at startup. */
62
+ specPath?: string | undefined;
63
+ /** Overrides the upstream base URL derived from `servers[0].url`. */
64
+ baseUrlOverride?: string | undefined;
65
+ /** Headers merged into every upstream request. */
66
+ upstreamHeaders?: Record<string, string> | undefined;
67
+ /** Per-request upstream timeout in milliseconds. */
68
+ requestTimeoutMs?: number | undefined;
69
+ /** Persist the loaded specification across restarts. Defaults to true. */
70
+ persistState?: boolean | undefined;
71
+ /** Location of the persisted state file. */
72
+ stateFilePath?: string | undefined;
73
+ /** Allowed CORS origins. Empty or omitted disables cross-origin access. */
74
+ allowedOrigins?: string[] | undefined;
75
+ allowedHosts?: string[];
76
+ /** Maximum number of in-memory log entries retained. */
77
+ maxLogEntries?: number | undefined;
78
+ /** Redact sensitive header values before logging. Defaults to true. */
79
+ redactSensitiveHeaders?: boolean | undefined;
80
+ /** Additional header names to redact, in addition to the built-in list. */
81
+ redactHeaderNames?: string[] | undefined;
82
+ /** Credentials forwarded to the upstream API for every tool call. */
83
+ security?: SecurityContext | undefined;
84
+ }
85
+ /** Credentials applied to upstream requests. */
86
+ interface SecurityContext {
87
+ /** Sent as `Authorization: Bearer <token>`. */
88
+ bearerToken?: string | undefined;
89
+ /** Sent as `Authorization: Basic <base64>`. */
90
+ basicAuth?: {
91
+ username: string;
92
+ password: string;
93
+ } | undefined;
94
+ /** Header-name to value pairs for API-key schemes. */
95
+ apiKeys?: Record<string, string> | undefined;
96
+ }
97
+ /** Mutable runtime state of the loaded specification. */
98
+ interface AppState {
99
+ spec: Document | null;
100
+ baseUrlOverride?: string | undefined;
101
+ specSource?: SpecSource | undefined;
102
+ }
103
+ /** Shape of the on-disk state file. All fields are untrusted when read back. */
104
+ interface PersistedState {
105
+ specRaw?: string | undefined;
106
+ specIsYaml?: boolean | undefined;
107
+ baseUrlOverride?: string | undefined;
108
+ /** Schema version, allowing forward-compatible migrations. */
109
+ stateVersion?: number | undefined;
110
+ savedAt?: string | undefined;
111
+ }
112
+ /** Per-call context handed to the request builder and HTTP executor. */
113
+ interface ExecutionContext {
114
+ baseUrlOverride?: string | undefined;
115
+ upstreamHeaders?: Record<string, string> | undefined;
116
+ requestTimeoutMs?: number | undefined;
117
+ security?: SecurityContext | undefined;
118
+ /** Correlates every log entry produced by one logical tool invocation. */
119
+ correlationId?: string | undefined;
120
+ /** Transport that initiated the call, recorded on emitted log entries. */
121
+ protocol?: ProtocolHint | undefined;
122
+ /** Receives request and response log entries. Must never throw. */
123
+ onLog?: ((entry: RequestLogEntry) => void) | undefined;
124
+ signal?: AbortSignal | undefined;
125
+ }
126
+ /** Endpoints a client can use to reach a managed service. */
127
+ interface ServiceEndpointInfo {
128
+ /** Legacy HTTP+SSE stream path. */
129
+ sse: string;
130
+ /** Legacy HTTP+SSE message-post path. */
131
+ messages: string;
132
+ /** Whether the service can also be launched over stdio by the CLI. */
133
+ stdioSupported: boolean;
134
+ /** Streamable HTTP endpoint path, when the transport is mounted. */
135
+ streamableHttp?: string | undefined;
136
+ }
137
+ /** A specification that has been compiled into a runnable MCP service. */
138
+ interface ManagedServiceRecord {
139
+ id: string;
140
+ createdAt: string;
141
+ updatedAt: string;
142
+ status: ServiceStatus;
143
+ title: string;
144
+ version: string;
145
+ source: SpecSource;
146
+ toolCount: number;
147
+ promptCount: number;
148
+ resourceCount: number;
149
+ endpoint: ServiceEndpointInfo;
150
+ /** Number of tools that could not be fully mapped from the specification. */
151
+ degradedToolCount?: number | undefined;
152
+ /** Upstream base URL in effect for this service. */
153
+ baseUrl?: string | undefined;
154
+ /** Timestamp of the most recent tool invocation. */
155
+ lastCallAt?: string | undefined;
156
+ lastError?: {
157
+ message: string;
158
+ at: string;
159
+ } | undefined;
160
+ }
161
+ /** A problem detected while compiling the specification. */
162
+ interface SpecIssue {
163
+ path: string;
164
+ method?: string | undefined;
165
+ message: string;
166
+ severity?: Exclude<LogLevel, "debug"> | undefined;
167
+ }
168
+ /** Snapshot returned by the admin status endpoint. */
169
+ interface AdminStatus {
170
+ specLoaded: boolean;
171
+ specSource: SpecSource | null;
172
+ serviceId: string | null;
173
+ active: boolean;
174
+ title: string | null;
175
+ version: string | null;
176
+ baseUrl: string | null;
177
+ toolCount: number;
178
+ degradedToolCount: number;
179
+ promptCount: number;
180
+ resourceCount: number;
181
+ activeSessions: number;
182
+ services: ManagedServiceRecord[];
183
+ /** Human-readable issue strings, ready for display. */
184
+ issues: string[];
185
+ lastUpdatedAt: string;
186
+ /** Whether an API key is required to reach the admin API. */
187
+ /** Process uptime in seconds. */
188
+ uptimeSeconds?: number | undefined;
189
+ pendingSessions: number;
190
+ persistEnabled: boolean;
191
+ authRequired: boolean;
192
+ /** Library version reported to clients. */
193
+ serverVersion?: string | undefined;
194
+ }
195
+ /**
196
+ * A prompt generated from the specification.
197
+ *
198
+ * `Prompt` already requires `name`; the alias exists so generated values are
199
+ * distinguishable from prompts supplied by an embedder.
200
+ */
201
+ interface GeneratedPrompt extends Prompt {
202
+ name: string;
203
+ arguments?: PromptArgument[] | undefined;
204
+ }
205
+ /** A resource generated from the specification. */
206
+ interface GeneratedResource extends Resource {
207
+ uri: string;
208
+ name: string;
209
+ }
210
+ /** A tool generated from the specification. */
211
+ interface GeneratedTool extends Tool {
212
+ name: string;
213
+ }
214
+ /** One textual resource body returned by a `resources/read` call. */
215
+ interface ResourceContentItem {
216
+ uri: string;
217
+ mimeType: string;
218
+ text: string;
219
+ }
220
+ /**
221
+ * Message shape used when assembling prompt results.
222
+ *
223
+ * Only `user` and `assistant` are valid MCP prompt roles; a `system` role would
224
+ * make this type unassignable to the SDK's `GetPromptResult`.
225
+ */
226
+ interface PromptMessageShape {
227
+ role: "user" | "assistant";
228
+ content: {
229
+ type: "text";
230
+ text: string;
231
+ };
232
+ }
233
+ /**
234
+ * Internal representation of a rendered prompt.
235
+ *
236
+ * @deprecated Build `GetPromptResult` from the SDK directly. Retained so that
237
+ * existing imports keep compiling.
238
+ */
239
+ interface ResolvedPrompt {
240
+ name: string;
241
+ description?: string | undefined;
242
+ messages: PromptMessageShape[];
243
+ }
244
+ /** Query parameters as recorded, preserving repeated keys. */
245
+ type LoggedQuery = Record<string, string | string[]>;
246
+ /** A single entry in the in-memory request log surfaced by the web console. */
247
+ interface RequestLogEntry {
248
+ id: string;
249
+ at: string;
250
+ direction: LogDirection;
251
+ /** Defaults to `info` for successes and `error` for failures when omitted. */
252
+ level?: LogLevel | undefined;
253
+ protocol?: ProtocolHint | undefined;
254
+ /** Ties a request entry to its matching response entry. */
255
+ correlationId?: string | undefined;
256
+ serviceId?: string | undefined;
257
+ sessionId?: string | undefined;
258
+ toolName?: string | undefined;
259
+ operationId?: string | undefined;
260
+ method?: string | undefined;
261
+ path?: string | undefined;
262
+ url?: string | undefined;
263
+ status?: number | undefined;
264
+ durationMs?: number | undefined;
265
+ success?: boolean | undefined;
266
+ requestHeaders?: Record<string, string> | undefined;
267
+ requestQuery?: LoggedQuery | undefined;
268
+ requestBody?: unknown;
269
+ responseHeaders?: Record<string, unknown> | undefined;
270
+ responseBody?: unknown;
271
+ /** Set when a body was shortened to respect the size limit. */
272
+ truncated?: boolean | undefined;
273
+ /** Set when one or more header values were replaced with a placeholder. */
274
+ redacted?: boolean | undefined;
275
+ /** Byte length of the upstream response, before truncation. */
276
+ responseBytes?: number | undefined;
277
+ error?: string | undefined;
278
+ /** Structured detail for internal events such as `spec-applied`. */
279
+ meta?: Record<string, unknown> | undefined;
280
+ requestQueryString?: string | undefined;
281
+ }
282
+ /** Filter accepted by the log query endpoint. */
283
+ interface LogQueryOptions {
284
+ limit?: number | undefined;
285
+ offset?: number | undefined;
286
+ direction?: LogDirection | undefined;
287
+ level?: LogLevel | undefined;
288
+ toolName?: string | undefined;
289
+ /** Case-insensitive substring match across tool name, URL and error text. */
290
+ search?: string | undefined;
291
+ /** Restrict to failures only. */
292
+ onlyErrors?: boolean | undefined;
293
+ }
294
+ /** Paged log response. */
295
+ interface LogQueryResult {
296
+ logs: RequestLogEntry[];
297
+ total: number;
298
+ limit: number;
299
+ offset: number;
300
+ }
301
+
302
+ /** Handle returned to the embedding process so it can shut everything down. */
303
+ interface AdminServerHandle {
304
+ app: Express;
305
+ server: Server;
306
+ /** Actual bound port, which differs from the request when port 0 is used. */
307
+ port: number;
308
+ /** Closes MCP sessions and the HTTP listener. Safe to call more than once. */
309
+ close: () => Promise<void>;
310
+ }
311
+ declare function startAdminServer(config: ServerConfig): Promise<AdminServerHandle>;
312
+
313
+ export { type AdminStatus as A, type BodyEncoding as B, isParameterLocation as C, isPlainRecord as D, type ExecutionContext as E, isProtocolHint as F, type GeneratedPrompt as G, isServiceStatus as H, isSpecSource as I, startAdminServer as J, type AdminServerHandle as K, LOG_DIRECTIONS as L, type ManagedServiceRecord as M, type ParameterLocation as P, type ResourceContentItem as R, SERVICE_STATUSES as S, TRANSPORT_MODES as T, type GeneratedResource as a, type ProtocolHint as b, type AppState as c, BODY_ENCODINGS as d, type GeneratedTool as e, LOG_LEVELS as f, type LogDirection as g, type LogLevel as h, type LogQueryOptions as i, type LogQueryResult as j, type LoggedQuery as k, PARAMETER_LOCATIONS as l, PROTOCOL_HINTS as m, type PersistedState as n, type PromptMessageShape as o, type RequestLogEntry as p, type ResolvedPrompt as q, SPEC_SOURCES as r, type SecurityContext as s, type ServerConfig as t, type ServiceEndpointInfo as u, type ServiceStatus as v, type SpecIssue as w, type SpecSource as x, type TransportMode as y, isBodyEncoding as z };