@powerduck/dev-mcp-server 0.1.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.
@@ -0,0 +1,384 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { Tool, Resource, ResourceTemplate } from '@modelcontextprotocol/sdk/types.js';
3
+ import { AuthConfig, CliConfig, DeclarativeAssertion, TestResult, TestReport, ScenarioReport } from '@powerduck/openapi-cli';
4
+ import { RequestValues } from '@powerduck/openapi-request';
5
+
6
+ interface ServerFact {
7
+ url: string;
8
+ description?: string;
9
+ /** Names of variables the server URL still requires. */
10
+ variables: string[];
11
+ }
12
+ interface SecuritySchemeFact {
13
+ name: string;
14
+ type: string;
15
+ scheme?: string;
16
+ bearerFormat?: string;
17
+ in?: string;
18
+ parameterName?: string;
19
+ openIdConnectUrl?: string;
20
+ /** OAuth2 flow names and the scopes each declares. */
21
+ flows: Record<string, string[]>;
22
+ }
23
+ interface CompactOperation {
24
+ /** Stable reference, e.g. "POST /orders". */
25
+ ref: string;
26
+ operationId?: string;
27
+ method: string;
28
+ path: string;
29
+ summary?: string;
30
+ tags: string[];
31
+ /** A non-empty security requirement applies to this operation. */
32
+ secured: boolean;
33
+ statusCodes: string[];
34
+ hasParameters: boolean;
35
+ hasRequestBody: boolean;
36
+ protocol: string;
37
+ }
38
+ interface ParameterFact {
39
+ name: string;
40
+ in: "path" | "query" | "header" | "cookie";
41
+ required: boolean;
42
+ description?: string;
43
+ deprecated: boolean;
44
+ /** Compact JSON Schema describing the parameter value. */
45
+ schema: Record<string, unknown>;
46
+ example?: unknown;
47
+ }
48
+ interface MediaFact {
49
+ /** Dereferenced JSON Schema for the payload. */
50
+ schema: unknown;
51
+ example?: unknown;
52
+ /** Named examples declared by the specification. */
53
+ examples: string[];
54
+ }
55
+ interface HeaderFact {
56
+ name: string;
57
+ required: boolean;
58
+ description?: string;
59
+ schema: Record<string, unknown>;
60
+ }
61
+ interface ResponseFact {
62
+ status: string;
63
+ description?: string;
64
+ headers: HeaderFact[];
65
+ content: Record<string, MediaFact>;
66
+ }
67
+ interface RequestBodyFact {
68
+ required: boolean;
69
+ description?: string;
70
+ content: Record<string, MediaFact>;
71
+ }
72
+ interface OperationFact {
73
+ ref: string;
74
+ operationId?: string;
75
+ method: string;
76
+ path: string;
77
+ summary?: string;
78
+ description?: string;
79
+ tags: string[];
80
+ deprecated: boolean;
81
+ idempotent: boolean;
82
+ protocol: string;
83
+ parameters: ParameterFact[];
84
+ requestBody?: RequestBodyFact;
85
+ responses: ResponseFact[];
86
+ /** Effective security requirements after operation-level overrides. */
87
+ security: SecuritySchemeFact[];
88
+ servers: ServerFact[];
89
+ }
90
+ type IssueSeverity = "error" | "warning" | "info";
91
+ interface ValidationIssue {
92
+ message: string;
93
+ severity: IssueSeverity;
94
+ path?: string;
95
+ }
96
+ interface ValidationFact {
97
+ valid: boolean;
98
+ openapiVersion?: string;
99
+ issues: ValidationIssue[];
100
+ }
101
+ interface OverviewFact {
102
+ title: string;
103
+ version: string;
104
+ description?: string;
105
+ openapiVersion?: string;
106
+ servers: ServerFact[];
107
+ endpointCount: number;
108
+ tags: string[];
109
+ protocols: string[];
110
+ schemaCount: number;
111
+ securitySchemes: SecuritySchemeFact[];
112
+ }
113
+ interface PagedOperations {
114
+ items: CompactOperation[];
115
+ total: number;
116
+ nextCursor?: string;
117
+ }
118
+ interface OperationLocator {
119
+ /** "METHOD /path" reference. */
120
+ ref?: string;
121
+ operationId?: string;
122
+ method?: string;
123
+ path?: string;
124
+ }
125
+ /** A failed tool call whose cause is the caller's input, not the server. */
126
+ declare class FactError extends Error {
127
+ readonly code: string;
128
+ readonly details?: unknown;
129
+ constructor(code: string, message: string, details?: unknown);
130
+ }
131
+
132
+ /**
133
+ * Loads an OpenAPI document from disk and keeps it fresh.
134
+ *
135
+ * A developer edits the spec while the coding agent is connected, so the
136
+ * store re-reads the file whenever its mtime changes. Parsing and validation
137
+ * are best effort: an in-progress document with validation issues must still
138
+ * answer fact queries, and the validate_spec tool surfaces those problems.
139
+ */
140
+
141
+ interface SpecSnapshot {
142
+ /** Absolute source path. */
143
+ path: string;
144
+ /** Document normalized to 3.x when conversion is safe; otherwise as authored. */
145
+ document: Record<string, any>;
146
+ /** Document with internal $ref values inlined where possible. */
147
+ dereferenced: Record<string, any>;
148
+ /** Raw source text, served by the `source` resource. */
149
+ sourceText: string;
150
+ mediaType: "application/yaml" | "application/json";
151
+ validation: ValidationFact;
152
+ mtimeMs: number;
153
+ }
154
+ declare class SpecStore {
155
+ readonly specPath: string;
156
+ private cache;
157
+ constructor(specPath: string);
158
+ /** Read and normalize the document, reusing the cache while mtime is stable. */
159
+ load(force?: boolean): Promise<SpecSnapshot>;
160
+ }
161
+
162
+ /**
163
+ * Builds the developer MCP server: read-only contract facts plus verification
164
+ * against a running backend.
165
+ *
166
+ * The server is transport agnostic. A SpecStore supplies a live document, and
167
+ * every request re-reads the file when it changed on disk. Long-running tools
168
+ * stream MCP $/progress and honor notifications/cancelled.
169
+ */
170
+
171
+ interface CreateServerOptions {
172
+ name?: string;
173
+ version?: string;
174
+ /** Free-form usage instructions surfaced during MCP initialization. */
175
+ instructions?: string;
176
+ }
177
+ declare function createDevMcpServer(store: SpecStore, options?: CreateServerOptions): Server;
178
+
179
+ /**
180
+ * stdio transport for AI coding clients (Claude Code, Codex CLI, Cursor,
181
+ * Claude Desktop). stdout is the JSON-RPC channel, so all accidental logging
182
+ * is diverted to stderr for the lifetime of the server.
183
+ */
184
+ interface StdioHandle {
185
+ closed: Promise<void>;
186
+ close(): Promise<void>;
187
+ }
188
+ interface StartStdioOptions {
189
+ specPath: string;
190
+ name?: string;
191
+ version?: string;
192
+ handleSignals?: boolean;
193
+ }
194
+ declare function startStdioServer(options: StartStdioOptions): Promise<StdioHandle>;
195
+
196
+ /**
197
+ * Builds the @powerduck/openapi-cli runner configuration for a verification
198
+ * request. Fact tools read an in-memory document; the runner also needs a real
199
+ * spec path (used as an identifier and fallback loader), which the SpecStore
200
+ * always has.
201
+ */
202
+
203
+ interface RunOverrides {
204
+ baseUrl?: string;
205
+ timeoutMs?: number;
206
+ concurrency?: number;
207
+ headers?: Record<string, string>;
208
+ variables?: Record<string, string>;
209
+ proxy?: string;
210
+ auth?: AuthConfig;
211
+ }
212
+ interface ProgressSink {
213
+ /** Emit MCP $/progress when a progress token was supplied by the client. */
214
+ report?: (progress: number, total: number, message?: string) => void;
215
+ /** Cancellation propagated from notifications/cancelled. */
216
+ signal?: AbortSignal;
217
+ }
218
+ declare function buildRunnerConfig(snapshot: SpecSnapshot, overrides: RunOverrides): CliConfig;
219
+ /** Resolves the target base URL from an explicit override or the spec servers. */
220
+ declare function resolveBaseUrl(snapshot: SpecSnapshot, explicit: string | undefined): string | undefined;
221
+ /** Maps raw tool arguments into typed runner overrides with validation. */
222
+ declare function runOverridesFromArgs(snapshot: SpecSnapshot, args: Record<string, unknown>): RunOverrides;
223
+
224
+ /**
225
+ * Developer MCP tools.
226
+ *
227
+ * Fact tools (M0) expose read-only contract truth. Verification tools (M1)
228
+ * send requests and run contract/scenario tests against a running backend via
229
+ * @powerduck/openapi-cli.
230
+ */
231
+
232
+ declare const devTools: Tool[];
233
+ /** Executes one tool call and returns a JSON-serializable result. */
234
+ declare function executeDevTool(name: string, args: Record<string, unknown>, store: SpecStore, sink?: ProgressSink): Promise<unknown>;
235
+
236
+ /**
237
+ * MCP resources expose the same facts as stable URIs, so agents can attach
238
+ * contract context directly to a conversation.
239
+ */
240
+
241
+ declare function listSpecResources(store: SpecStore): Promise<Resource[]>;
242
+ declare const specResourceTemplates: ResourceTemplate[];
243
+ interface ResourceContents {
244
+ uri: string;
245
+ mimeType: string;
246
+ text: string;
247
+ }
248
+ declare function readSpecResource(uri: string, store: SpecStore): Promise<ResourceContents>;
249
+
250
+ /**
251
+ * Pure fact extraction over an OpenAPI document.
252
+ *
253
+ * Every function here is deterministic and side-effect free. The MCP layer is
254
+ * a thin transport over these functions so the same logic is unit-testable
255
+ * without a JSON-RPC client.
256
+ */
257
+
258
+ declare const HTTP_METHODS: readonly ["get", "post", "put", "patch", "delete", "head", "options", "trace"];
259
+ /** Resolves a local JSON pointer ("#/components/schemas/Foo"). */
260
+ declare function resolvePointer(doc: unknown, ref: string): unknown;
261
+ declare function overview(doc: Record<string, any>): OverviewFact;
262
+ interface ListOptions {
263
+ tag?: string;
264
+ method?: string;
265
+ protocol?: string;
266
+ secured?: boolean;
267
+ search?: string;
268
+ pageSize?: number;
269
+ cursor?: string;
270
+ }
271
+ declare function listOperations(doc: Record<string, any>, options?: ListOptions): PagedOperations;
272
+ declare function getOperation(doc: Record<string, any>, rawDoc: Record<string, any>, locator: OperationLocator): OperationFact;
273
+ interface SchemaLocator {
274
+ name?: string;
275
+ ref?: string;
276
+ }
277
+ declare function getSchema(doc: Record<string, any>, rawDoc: Record<string, any>, locator: SchemaLocator): {
278
+ name?: string;
279
+ schema: unknown;
280
+ };
281
+ declare function authRequirements(doc: Record<string, any>, locator?: OperationLocator): {
282
+ global: SecuritySchemeFact[];
283
+ operation?: SecuritySchemeFact[];
284
+ };
285
+
286
+ /**
287
+ * Project-level developer configuration (powerduck.dev.json).
288
+ *
289
+ * This file is safe to commit: it points at the specification and the local
290
+ * development server. Secrets must come from environment variables, never from
291
+ * this file.
292
+ */
293
+ interface DevConfig {
294
+ /** Path to the OpenAPI document, relative to the config file. */
295
+ spec?: string;
296
+ /** Default base URL of the local backend under development. */
297
+ baseUrl?: string;
298
+ /** Backend project root, used by future code-aware tools. */
299
+ project?: string;
300
+ }
301
+ /**
302
+ * Loads optional configuration. Explicit `--config` wins; otherwise a
303
+ * powerduck.dev.json in the current working directory is used when present.
304
+ */
305
+ declare function loadDevConfig(configPath?: string): {
306
+ config: DevConfig;
307
+ configDir: string;
308
+ };
309
+ /** Resolves a possibly config-relative path to an absolute one. */
310
+ declare function resolveFrom(configDir: string, target?: string): string | undefined;
311
+
312
+ /**
313
+ * Lightweight reachability probe. Unlike send_request this does not require a
314
+ * matching operation; it confirms the local backend is up before testing.
315
+ */
316
+ interface PingResult {
317
+ url: string;
318
+ reachable: boolean;
319
+ status?: number;
320
+ statusText?: string;
321
+ contentType?: string;
322
+ latencyMs: number;
323
+ error?: string;
324
+ }
325
+ declare function pingTarget(rawUrl: string, timeoutMs: number): Promise<PingResult>;
326
+
327
+ /**
328
+ * Single-operation verification: send one request to the local backend and
329
+ * report the normalized response plus spec-derived assertion results.
330
+ */
331
+
332
+ interface SendInput {
333
+ values?: RequestValues;
334
+ variables?: Record<string, string>;
335
+ assertions?: DeclarativeAssertion[];
336
+ /** Per-call target override; wins over the configured base URL. */
337
+ baseUrl?: string;
338
+ }
339
+ declare function sendOne(snapshot: SpecSnapshot, overrides: RunOverrides, locator: OperationLocator, input: SendInput): Promise<TestResult>;
340
+
341
+ /**
342
+ * Batch contract verification against a running backend.
343
+ *
344
+ * Mirrors openapi-cli's runTests but executes against the in-memory live
345
+ * document so the MCP never depends on a stale file on disk.
346
+ */
347
+
348
+ interface ContractFilter {
349
+ methods?: string[];
350
+ paths?: string[];
351
+ tags?: string[];
352
+ operationIds?: string[];
353
+ }
354
+ interface ContractSuiteResult {
355
+ summary: TestReport["summary"];
356
+ results: TestResult[];
357
+ generatedAt: string;
358
+ version: string;
359
+ }
360
+ declare function contractFilterFromArgs(args: Record<string, unknown>): ContractFilter;
361
+ declare function runContractSuite(snapshot: SpecSnapshot, overrides: RunOverrides, filter: ContractFilter, sink?: ProgressSink): Promise<ContractSuiteResult>;
362
+
363
+ /**
364
+ * Scenario validation and execution.
365
+ *
366
+ * Validation resolves every step reference up front and never touches the
367
+ * network, so an agent can confirm a plan before running it. Execution reuses
368
+ * openapi-cli's ordered, stateful engine with live progress and cancellation.
369
+ */
370
+
371
+ interface ScenarioValidation {
372
+ valid: boolean;
373
+ errors?: string[];
374
+ steps?: Array<{
375
+ index: number;
376
+ ref: string;
377
+ name?: string;
378
+ }>;
379
+ count?: number;
380
+ }
381
+ declare function validateScenario(snapshot: SpecSnapshot, rawScenario: unknown): ScenarioValidation;
382
+ declare function runScenarioTool(snapshot: SpecSnapshot, rawScenario: unknown, overrides: RunOverrides, sink?: ProgressSink): Promise<Omit<ScenarioReport, "config">>;
383
+
384
+ export { type CompactOperation, type ContractFilter, type ContractSuiteResult, type CreateServerOptions, type DevConfig, FactError, HTTP_METHODS, type HeaderFact, type ListOptions, type MediaFact, type OperationFact, type OperationLocator, type OverviewFact, type PagedOperations, type ParameterFact, type PingResult, type ProgressSink, type RequestBodyFact, type ResponseFact, type RunOverrides, type ScenarioValidation, type SecuritySchemeFact, type SendInput, type ServerFact, type SpecSnapshot, SpecStore, type StartStdioOptions, type StdioHandle, type ValidationFact, type ValidationIssue, authRequirements, buildRunnerConfig, contractFilterFromArgs, createDevMcpServer, devTools, executeDevTool, getOperation, getSchema, listOperations, listSpecResources, loadDevConfig, overview, pingTarget, readSpecResource, resolveBaseUrl, resolveFrom, resolvePointer, runContractSuite, runOverridesFromArgs, runScenarioTool, sendOne, specResourceTemplates, startStdioServer, validateScenario };