@apicircle/mcp-server 1.0.7 → 1.0.9
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/dist/bin/mcp-server.cjs +290 -120
- package/dist/bin/mcp-server.cjs.map +1 -1
- package/dist/index.cjs +280 -117
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -6
- package/dist/index.d.ts +20 -6
- package/dist/index.js +279 -116
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { z } from "zod";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@apicircle/mcp-server",
|
|
9
|
-
version: "1.0.
|
|
9
|
+
version: "1.0.9",
|
|
10
10
|
private: false,
|
|
11
11
|
type: "module",
|
|
12
12
|
description: "Model Context Protocol server exposing API Circle Studio's workspace as a tool catalog. Used by Claude Desktop, ChatGPT, Cursor, GitHub Copilot, and any other MCP-compatible AI client.",
|
|
@@ -2118,15 +2118,143 @@ var promptSetEndpointMultipliersTool = {
|
|
|
2118
2118
|
}
|
|
2119
2119
|
};
|
|
2120
2120
|
|
|
2121
|
-
// src/tools/
|
|
2121
|
+
// src/tools/globalAssets.ts
|
|
2122
2122
|
import { z as z10 } from "zod";
|
|
2123
|
-
import { generateId as generateId4
|
|
2123
|
+
import { generateId as generateId4 } from "@apicircle/shared";
|
|
2124
|
+
function deriveState(asset, hasPendingUpload) {
|
|
2125
|
+
const w = asset.workingBranchRef ?? null;
|
|
2126
|
+
const b = asset.baseBranchRef ?? null;
|
|
2127
|
+
if (w && b) {
|
|
2128
|
+
if (w.blobSha && b.blobSha && w.blobSha !== b.blobSha) return "diverged";
|
|
2129
|
+
return "merged";
|
|
2130
|
+
}
|
|
2131
|
+
if (w && !b) return "workingOnly";
|
|
2132
|
+
if (!w && b) return "baseOnly";
|
|
2133
|
+
if (hasPendingUpload) return "uploading";
|
|
2134
|
+
return "missing";
|
|
2135
|
+
}
|
|
2136
|
+
var globalAssetsFilesListTool = {
|
|
2137
|
+
name: "assets.list_files",
|
|
2138
|
+
description: "List every Global File Asset with its provenance state and reference count. Each entry includes id, name, filename, size, mimeType, sha256, state (uploading | workingOnly | merged | baseOnly | missing | diverged), workingBranchRef, baseBranchRef, and usage { requests, mockEndpoints, total }.",
|
|
2139
|
+
inputSchema: z10.object({}).strict(),
|
|
2140
|
+
async handler(_input, ctx) {
|
|
2141
|
+
const state = await ctx.workspace.read();
|
|
2142
|
+
const files = state.synced.globalAssets.files ?? {};
|
|
2143
|
+
const pending = state.local.pendingFileUploads ?? {};
|
|
2144
|
+
const usage = state.local.assetUsageIndex ?? {};
|
|
2145
|
+
const items = Object.values(files).map((asset) => {
|
|
2146
|
+
const hasPending = Boolean(pending[asset.id]);
|
|
2147
|
+
const u = usage[asset.id] ?? { requests: [], mockEndpoints: [], total: 0 };
|
|
2148
|
+
return {
|
|
2149
|
+
id: asset.id,
|
|
2150
|
+
name: asset.name,
|
|
2151
|
+
description: asset.description ?? null,
|
|
2152
|
+
filename: asset.filename,
|
|
2153
|
+
size: asset.size,
|
|
2154
|
+
mimeType: asset.mimeType,
|
|
2155
|
+
sha256: asset.sha256 ?? null,
|
|
2156
|
+
state: deriveState(asset, hasPending),
|
|
2157
|
+
workingBranchRef: asset.workingBranchRef ?? null,
|
|
2158
|
+
baseBranchRef: asset.baseBranchRef ?? null,
|
|
2159
|
+
usage: { ...u }
|
|
2160
|
+
};
|
|
2161
|
+
});
|
|
2162
|
+
return { count: items.length, files: items };
|
|
2163
|
+
}
|
|
2164
|
+
};
|
|
2165
|
+
var globalAssetsFilesCreateTool = {
|
|
2166
|
+
name: "assets.create_file",
|
|
2167
|
+
description: 'Register a Global File Asset entry. Bytes are NOT carried \u2014 MCP returns the new asset id and the asset enters the "missing" state. The user fills the bytes from the Global Assets panel (a "Fill bytes" button surfaces on missing-state assets) which preserves the slot id and queues the bytes for the next push. Use this when an AI client wants to claim an asset slot for a file the user will provide later.',
|
|
2168
|
+
inputSchema: z10.object({
|
|
2169
|
+
name: z10.string().min(1, "name is required"),
|
|
2170
|
+
description: z10.string().optional(),
|
|
2171
|
+
filename: z10.string().min(1, "filename is required"),
|
|
2172
|
+
size: z10.number().int().nonnegative(),
|
|
2173
|
+
mimeType: z10.string().default("application/octet-stream"),
|
|
2174
|
+
sha256: z10.string().optional()
|
|
2175
|
+
}).strict(),
|
|
2176
|
+
async handler(input, ctx) {
|
|
2177
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2178
|
+
const file = {
|
|
2179
|
+
id: generateId4(),
|
|
2180
|
+
name: input.name,
|
|
2181
|
+
description: input.description,
|
|
2182
|
+
slotId: generateId4(),
|
|
2183
|
+
filename: input.filename,
|
|
2184
|
+
size: input.size,
|
|
2185
|
+
mimeType: input.mimeType,
|
|
2186
|
+
sha256: input.sha256,
|
|
2187
|
+
createdAt: now,
|
|
2188
|
+
updatedAt: now
|
|
2189
|
+
};
|
|
2190
|
+
const out = await ctx.workspace.apply({ kind: "globalAsset.upsertFile", file });
|
|
2191
|
+
return { id: file.id, slotId: file.slotId, changedIds: out.changedIds };
|
|
2192
|
+
}
|
|
2193
|
+
};
|
|
2194
|
+
var globalAssetsFilesUpdateTool = {
|
|
2195
|
+
name: "assets.update_file",
|
|
2196
|
+
description: "Rename or re-describe a Global File Asset. Provenance refs (workingBranchRef, baseBranchRef) and binary metadata (slotId, sha256, size, mimeType) are preserved verbatim.",
|
|
2197
|
+
inputSchema: z10.object({
|
|
2198
|
+
id: z10.string().min(1),
|
|
2199
|
+
patch: z10.object({
|
|
2200
|
+
name: z10.string().optional(),
|
|
2201
|
+
description: z10.string().nullable().optional()
|
|
2202
|
+
}).strict()
|
|
2203
|
+
}).strict(),
|
|
2204
|
+
async handler(input, ctx) {
|
|
2205
|
+
const state = await ctx.workspace.read();
|
|
2206
|
+
const existing = state.synced.globalAssets.files?.[input.id];
|
|
2207
|
+
if (!existing) {
|
|
2208
|
+
return { found: false, changedIds: [] };
|
|
2209
|
+
}
|
|
2210
|
+
const next = {
|
|
2211
|
+
...existing,
|
|
2212
|
+
name: input.patch.name ?? existing.name,
|
|
2213
|
+
description: input.patch.description === null ? void 0 : input.patch.description ?? existing.description
|
|
2214
|
+
};
|
|
2215
|
+
const out = await ctx.workspace.apply({ kind: "globalAsset.upsertFile", file: next });
|
|
2216
|
+
return { found: true, id: next.id, changedIds: out.changedIds };
|
|
2217
|
+
}
|
|
2218
|
+
};
|
|
2219
|
+
var globalAssetsFilesDeleteTool = {
|
|
2220
|
+
name: "assets.delete_file",
|
|
2221
|
+
description: "Delete a Global File Asset. Cascades \u2014 every request body and mock-response body that referenced the asset is unbound in the same mutation. The result envelope includes the consumer list that was cleared so the caller can report what changed.",
|
|
2222
|
+
inputSchema: z10.object({ id: z10.string().min(1) }).strict(),
|
|
2223
|
+
async handler(input, ctx) {
|
|
2224
|
+
const before = await ctx.workspace.read();
|
|
2225
|
+
const usage = before.local.assetUsageIndex?.[input.id] ?? {
|
|
2226
|
+
requests: [],
|
|
2227
|
+
mockEndpoints: [],
|
|
2228
|
+
total: 0
|
|
2229
|
+
};
|
|
2230
|
+
const existing = before.synced.globalAssets.files?.[input.id];
|
|
2231
|
+
if (!existing) {
|
|
2232
|
+
return { found: false, changedIds: [] };
|
|
2233
|
+
}
|
|
2234
|
+
const out = await ctx.workspace.apply({ kind: "globalAsset.removeFile", id: input.id });
|
|
2235
|
+
return {
|
|
2236
|
+
found: true,
|
|
2237
|
+
id: input.id,
|
|
2238
|
+
filename: existing.filename,
|
|
2239
|
+
unbound: {
|
|
2240
|
+
requests: usage.requests,
|
|
2241
|
+
mockEndpoints: usage.mockEndpoints,
|
|
2242
|
+
total: usage.total
|
|
2243
|
+
},
|
|
2244
|
+
changedIds: out.changedIds
|
|
2245
|
+
};
|
|
2246
|
+
}
|
|
2247
|
+
};
|
|
2248
|
+
|
|
2249
|
+
// src/tools/mocks.ts
|
|
2250
|
+
import { z as z11 } from "zod";
|
|
2251
|
+
import { generateId as generateId5, makeDefaultMockResponse as makeDefaultMockResponse2, makeDefaultRequestSchema as makeDefaultRequestSchema2 } from "@apicircle/shared";
|
|
2124
2252
|
import { parseSourceToEndpoints } from "@apicircle/mock-server-core";
|
|
2125
2253
|
async function ingestSource(source, name) {
|
|
2126
2254
|
const { endpoints, warnings } = await parseSourceToEndpoints(source);
|
|
2127
2255
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2128
2256
|
const mock = {
|
|
2129
|
-
id:
|
|
2257
|
+
id: generateId5(),
|
|
2130
2258
|
name,
|
|
2131
2259
|
source,
|
|
2132
2260
|
endpoints,
|
|
@@ -2142,10 +2270,10 @@ async function ingestSource(source, name) {
|
|
|
2142
2270
|
var mockCreateFromOpenApiTool = {
|
|
2143
2271
|
name: "mock.create_from_openapi",
|
|
2144
2272
|
description: "Create a mock server from an OpenAPI / Swagger spec (YAML or JSON).",
|
|
2145
|
-
inputSchema:
|
|
2146
|
-
name:
|
|
2147
|
-
spec:
|
|
2148
|
-
format:
|
|
2273
|
+
inputSchema: z11.object({
|
|
2274
|
+
name: z11.string(),
|
|
2275
|
+
spec: z11.string().min(1),
|
|
2276
|
+
format: z11.enum(["json", "yaml"]).default("json")
|
|
2149
2277
|
}),
|
|
2150
2278
|
async handler(input, ctx) {
|
|
2151
2279
|
const { mock, warnings } = await ingestSource(
|
|
@@ -2164,7 +2292,7 @@ var mockCreateFromOpenApiTool = {
|
|
|
2164
2292
|
var mockCreateFromPostmanTool = {
|
|
2165
2293
|
name: "mock.create_from_postman",
|
|
2166
2294
|
description: "Create a mock server from a Postman v2/v2.1 collection.",
|
|
2167
|
-
inputSchema:
|
|
2295
|
+
inputSchema: z11.object({ name: z11.string(), collection: z11.string().min(1) }),
|
|
2168
2296
|
async handler(input, ctx) {
|
|
2169
2297
|
const { mock, warnings } = await ingestSource(
|
|
2170
2298
|
{ kind: "postman", collection: input.collection },
|
|
@@ -2182,7 +2310,7 @@ var mockCreateFromPostmanTool = {
|
|
|
2182
2310
|
var mockCreateFromInsomniaTool = {
|
|
2183
2311
|
name: "mock.create_from_insomnia",
|
|
2184
2312
|
description: "Create a mock server from an Insomnia v4 export.",
|
|
2185
|
-
inputSchema:
|
|
2313
|
+
inputSchema: z11.object({ name: z11.string(), export: z11.string().min(1) }),
|
|
2186
2314
|
async handler(input, ctx) {
|
|
2187
2315
|
const { mock, warnings } = await ingestSource(
|
|
2188
2316
|
{ kind: "insomnia", export: input.export },
|
|
@@ -2200,7 +2328,7 @@ var mockCreateFromInsomniaTool = {
|
|
|
2200
2328
|
var mockImportPostmanMockCollectionTool = {
|
|
2201
2329
|
name: "mock.import_postman_mock_collection",
|
|
2202
2330
|
description: "Import a Postman Mock Collection (collections previously hosted on Postman's mock service). Same parser as a regular Postman collection but marked as a mock import.",
|
|
2203
|
-
inputSchema:
|
|
2331
|
+
inputSchema: z11.object({ name: z11.string(), collection: z11.string().min(1) }),
|
|
2204
2332
|
async handler(input, ctx) {
|
|
2205
2333
|
const { mock, warnings } = await ingestSource(
|
|
2206
2334
|
{ kind: "postman", collection: input.collection },
|
|
@@ -2218,7 +2346,7 @@ var mockImportPostmanMockCollectionTool = {
|
|
|
2218
2346
|
var mockListTool = {
|
|
2219
2347
|
name: "mock.list",
|
|
2220
2348
|
description: "List all mock servers in the workspace plus their runtime status (running / stopped, port).",
|
|
2221
|
-
inputSchema:
|
|
2349
|
+
inputSchema: z11.object({}),
|
|
2222
2350
|
async handler(_input, ctx) {
|
|
2223
2351
|
const state = await ctx.workspace.read();
|
|
2224
2352
|
const running = await ctx.mock.list();
|
|
@@ -2240,9 +2368,9 @@ var mockListTool = {
|
|
|
2240
2368
|
var mockStartTool = {
|
|
2241
2369
|
name: "mock.start",
|
|
2242
2370
|
description: "Start a mock server by id. Returns the bound port. Errors if the mock is already running or the requested port is in use.",
|
|
2243
|
-
inputSchema:
|
|
2244
|
-
id:
|
|
2245
|
-
port:
|
|
2371
|
+
inputSchema: z11.object({
|
|
2372
|
+
id: z11.string(),
|
|
2373
|
+
port: z11.number().int().positive().optional()
|
|
2246
2374
|
}),
|
|
2247
2375
|
async handler(input, ctx) {
|
|
2248
2376
|
const state = await ctx.workspace.read();
|
|
@@ -2259,7 +2387,7 @@ var mockStartTool = {
|
|
|
2259
2387
|
var mockStopTool = {
|
|
2260
2388
|
name: "mock.stop",
|
|
2261
2389
|
description: "Stop a running mock server by id (no-op if not running).",
|
|
2262
|
-
inputSchema:
|
|
2390
|
+
inputSchema: z11.object({ id: z11.string() }),
|
|
2263
2391
|
async handler(input, ctx) {
|
|
2264
2392
|
try {
|
|
2265
2393
|
await ctx.mock.stop(input.id);
|
|
@@ -2272,7 +2400,7 @@ var mockStopTool = {
|
|
|
2272
2400
|
var mockDeleteTool = {
|
|
2273
2401
|
name: "mock.delete",
|
|
2274
2402
|
description: "Delete a mock server definition. Stops it first if it's running.",
|
|
2275
|
-
inputSchema:
|
|
2403
|
+
inputSchema: z11.object({ id: z11.string() }),
|
|
2276
2404
|
async handler(input, ctx) {
|
|
2277
2405
|
try {
|
|
2278
2406
|
await ctx.mock.stop(input.id);
|
|
@@ -2282,18 +2410,18 @@ var mockDeleteTool = {
|
|
|
2282
2410
|
return { ok: true, changedIds: out.changedIds };
|
|
2283
2411
|
}
|
|
2284
2412
|
};
|
|
2285
|
-
var HTTP_METHOD3 =
|
|
2413
|
+
var HTTP_METHOD3 = z11.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
|
|
2286
2414
|
var mockCreateManualTool = {
|
|
2287
2415
|
name: "mock.create_manual",
|
|
2288
2416
|
description: "Create an empty manual-mode mock server. Use `mock.add_endpoint` afterward to populate it. CORS defaults to off (same-origin only); enable + list explicit origins via `mock.update_cors` if cross-origin access is needed.",
|
|
2289
|
-
inputSchema:
|
|
2290
|
-
name:
|
|
2291
|
-
defaultPort:
|
|
2417
|
+
inputSchema: z11.object({
|
|
2418
|
+
name: z11.string().min(1),
|
|
2419
|
+
defaultPort: z11.number().int().positive().nullable().optional()
|
|
2292
2420
|
}),
|
|
2293
2421
|
async handler(input, ctx) {
|
|
2294
2422
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2295
2423
|
const mock = {
|
|
2296
|
-
id:
|
|
2424
|
+
id: generateId5(),
|
|
2297
2425
|
name: input.name,
|
|
2298
2426
|
source: { kind: "manual", endpoints: [] },
|
|
2299
2427
|
endpoints: [],
|
|
@@ -2311,7 +2439,7 @@ var mockCreateManualTool = {
|
|
|
2311
2439
|
var mockListEndpointsTool = {
|
|
2312
2440
|
name: "mock.list_endpoints",
|
|
2313
2441
|
description: "List endpoints for a mock server (id, method, path, name).",
|
|
2314
|
-
inputSchema:
|
|
2442
|
+
inputSchema: z11.object({ mockId: z11.string() }),
|
|
2315
2443
|
async handler(input, ctx) {
|
|
2316
2444
|
const state = await ctx.workspace.read();
|
|
2317
2445
|
const mock = state.synced.mockServers[input.mockId];
|
|
@@ -2330,10 +2458,10 @@ var mockListEndpointsTool = {
|
|
|
2330
2458
|
};
|
|
2331
2459
|
}
|
|
2332
2460
|
};
|
|
2333
|
-
var ENDPOINT_RESPONSE2 =
|
|
2334
|
-
status:
|
|
2335
|
-
jsonBody:
|
|
2336
|
-
contentType:
|
|
2461
|
+
var ENDPOINT_RESPONSE2 = z11.object({
|
|
2462
|
+
status: z11.number().int().min(100).max(599).default(200),
|
|
2463
|
+
jsonBody: z11.string().default("{}"),
|
|
2464
|
+
contentType: z11.string().default("application/json")
|
|
2337
2465
|
});
|
|
2338
2466
|
function buildDefaultEndpoint(args) {
|
|
2339
2467
|
const response = args.response ?? {
|
|
@@ -2343,7 +2471,7 @@ function buildDefaultEndpoint(args) {
|
|
|
2343
2471
|
};
|
|
2344
2472
|
const headers = [{ key: "Content-Type", value: response.contentType, enabled: true }];
|
|
2345
2473
|
return {
|
|
2346
|
-
id:
|
|
2474
|
+
id: generateId5(),
|
|
2347
2475
|
name: args.name ?? `${args.method} ${args.pathPattern}`,
|
|
2348
2476
|
method: args.method,
|
|
2349
2477
|
pathPattern: args.pathPattern,
|
|
@@ -2362,12 +2490,12 @@ function buildDefaultEndpoint(args) {
|
|
|
2362
2490
|
var mockAddEndpointTool = {
|
|
2363
2491
|
name: "mock.add_endpoint",
|
|
2364
2492
|
description: "Append a new endpoint to a mock server. Defaults to a 200 JSON response of `{}`. Returns the new endpoint id.",
|
|
2365
|
-
inputSchema:
|
|
2366
|
-
mockId:
|
|
2493
|
+
inputSchema: z11.object({
|
|
2494
|
+
mockId: z11.string(),
|
|
2367
2495
|
method: HTTP_METHOD3,
|
|
2368
|
-
pathPattern:
|
|
2369
|
-
name:
|
|
2370
|
-
description:
|
|
2496
|
+
pathPattern: z11.string().min(1),
|
|
2497
|
+
name: z11.string().optional(),
|
|
2498
|
+
description: z11.string().optional(),
|
|
2371
2499
|
response: ENDPOINT_RESPONSE2.optional()
|
|
2372
2500
|
}),
|
|
2373
2501
|
async handler(input, ctx) {
|
|
@@ -2390,13 +2518,13 @@ var mockAddEndpointTool = {
|
|
|
2390
2518
|
var mockUpdateEndpointTool = {
|
|
2391
2519
|
name: "mock.update_endpoint",
|
|
2392
2520
|
description: "Patch fields on a single mock endpoint (method, pathPattern, name, description, defaultResponse status / contentType / json body). Pass only the fields you want to change.",
|
|
2393
|
-
inputSchema:
|
|
2394
|
-
mockId:
|
|
2395
|
-
endpointId:
|
|
2521
|
+
inputSchema: z11.object({
|
|
2522
|
+
mockId: z11.string(),
|
|
2523
|
+
endpointId: z11.string(),
|
|
2396
2524
|
method: HTTP_METHOD3.optional(),
|
|
2397
|
-
pathPattern:
|
|
2398
|
-
name:
|
|
2399
|
-
description:
|
|
2525
|
+
pathPattern: z11.string().optional(),
|
|
2526
|
+
name: z11.string().optional(),
|
|
2527
|
+
description: z11.string().optional(),
|
|
2400
2528
|
response: ENDPOINT_RESPONSE2.partial().optional()
|
|
2401
2529
|
}),
|
|
2402
2530
|
async handler(input, ctx) {
|
|
@@ -2437,7 +2565,7 @@ var mockUpdateEndpointTool = {
|
|
|
2437
2565
|
var mockDeleteEndpointTool = {
|
|
2438
2566
|
name: "mock.delete_endpoint",
|
|
2439
2567
|
description: "Remove an endpoint from a mock server.",
|
|
2440
|
-
inputSchema:
|
|
2568
|
+
inputSchema: z11.object({ mockId: z11.string(), endpointId: z11.string() }),
|
|
2441
2569
|
async handler(input, ctx) {
|
|
2442
2570
|
const state = await ctx.workspace.read();
|
|
2443
2571
|
const mock = state.synced.mockServers[input.mockId];
|
|
@@ -2457,9 +2585,9 @@ var mockDeleteEndpointTool = {
|
|
|
2457
2585
|
return { ok: true, changedIds: out.changedIds };
|
|
2458
2586
|
}
|
|
2459
2587
|
};
|
|
2460
|
-
var VALIDATION_RULE =
|
|
2461
|
-
id:
|
|
2462
|
-
kind:
|
|
2588
|
+
var VALIDATION_RULE = z11.object({
|
|
2589
|
+
id: z11.string().optional(),
|
|
2590
|
+
kind: z11.enum([
|
|
2463
2591
|
"header-required",
|
|
2464
2592
|
"header-equals",
|
|
2465
2593
|
"header-matches",
|
|
@@ -2470,43 +2598,43 @@ var VALIDATION_RULE = z10.object({
|
|
|
2470
2598
|
"body-required",
|
|
2471
2599
|
"content-type-equals"
|
|
2472
2600
|
]),
|
|
2473
|
-
target:
|
|
2474
|
-
expected:
|
|
2475
|
-
message:
|
|
2476
|
-
enabled:
|
|
2477
|
-
failResponse:
|
|
2478
|
-
status:
|
|
2479
|
-
jsonBody:
|
|
2601
|
+
target: z11.string().default(""),
|
|
2602
|
+
expected: z11.string().optional(),
|
|
2603
|
+
message: z11.string().optional(),
|
|
2604
|
+
enabled: z11.boolean().default(true),
|
|
2605
|
+
failResponse: z11.object({
|
|
2606
|
+
status: z11.number().int().min(100).max(599).default(400),
|
|
2607
|
+
jsonBody: z11.string().default('{"error":"validation failed"}')
|
|
2480
2608
|
}).default({})
|
|
2481
2609
|
});
|
|
2482
|
-
var CONDITION_CLAUSE =
|
|
2483
|
-
id:
|
|
2484
|
-
scope:
|
|
2485
|
-
target:
|
|
2486
|
-
op:
|
|
2487
|
-
value:
|
|
2610
|
+
var CONDITION_CLAUSE = z11.object({
|
|
2611
|
+
id: z11.string().optional(),
|
|
2612
|
+
scope: z11.enum(["query", "pathParam", "header", "cookie", "body-json-path"]),
|
|
2613
|
+
target: z11.string(),
|
|
2614
|
+
op: z11.enum(["equals", "not-equals", "matches", "gt", "lt", "gte", "lte", "present", "absent"]),
|
|
2615
|
+
value: z11.string().optional()
|
|
2488
2616
|
});
|
|
2489
|
-
var RESPONSE_RULE =
|
|
2490
|
-
id:
|
|
2491
|
-
name:
|
|
2492
|
-
enabled:
|
|
2493
|
-
when:
|
|
2494
|
-
response:
|
|
2495
|
-
status:
|
|
2496
|
-
jsonBody:
|
|
2617
|
+
var RESPONSE_RULE = z11.object({
|
|
2618
|
+
id: z11.string().optional(),
|
|
2619
|
+
name: z11.string(),
|
|
2620
|
+
enabled: z11.boolean().default(true),
|
|
2621
|
+
when: z11.array(CONDITION_CLAUSE).default([]),
|
|
2622
|
+
response: z11.object({
|
|
2623
|
+
status: z11.number().int().min(100).max(599).default(200),
|
|
2624
|
+
jsonBody: z11.string().default("{}")
|
|
2497
2625
|
}).default({})
|
|
2498
2626
|
});
|
|
2499
|
-
var MULTIPLIER =
|
|
2500
|
-
id:
|
|
2501
|
-
name:
|
|
2502
|
-
source:
|
|
2503
|
-
kind:
|
|
2504
|
-
key:
|
|
2627
|
+
var MULTIPLIER = z11.object({
|
|
2628
|
+
id: z11.string().optional(),
|
|
2629
|
+
name: z11.string().optional(),
|
|
2630
|
+
source: z11.object({
|
|
2631
|
+
kind: z11.enum(["query", "pathParam", "header", "body-json-path"]),
|
|
2632
|
+
key: z11.string()
|
|
2505
2633
|
}),
|
|
2506
|
-
targetJsonPath:
|
|
2507
|
-
defaultCount:
|
|
2508
|
-
min:
|
|
2509
|
-
max:
|
|
2634
|
+
targetJsonPath: z11.string(),
|
|
2635
|
+
defaultCount: z11.number().int().nonnegative().default(0),
|
|
2636
|
+
min: z11.number().int().nonnegative().optional(),
|
|
2637
|
+
max: z11.number().int().nonnegative().optional()
|
|
2510
2638
|
});
|
|
2511
2639
|
function defaultJsonResponseConfig(args) {
|
|
2512
2640
|
return {
|
|
@@ -2531,10 +2659,10 @@ function patchEndpoint2(mock, endpointId, patcher) {
|
|
|
2531
2659
|
var mockSetValidationRulesTool = {
|
|
2532
2660
|
name: "mock.set_validation_rules",
|
|
2533
2661
|
description: "Replace an endpoint's validation rules. Rules without an `id` get a fresh one; existing rules can keep theirs to preserve client-side selection state. Empty array clears all rules.",
|
|
2534
|
-
inputSchema:
|
|
2535
|
-
mockId:
|
|
2536
|
-
endpointId:
|
|
2537
|
-
rules:
|
|
2662
|
+
inputSchema: z11.object({
|
|
2663
|
+
mockId: z11.string(),
|
|
2664
|
+
endpointId: z11.string(),
|
|
2665
|
+
rules: z11.array(VALIDATION_RULE)
|
|
2538
2666
|
}),
|
|
2539
2667
|
async handler(input, ctx) {
|
|
2540
2668
|
const state = await ctx.workspace.read();
|
|
@@ -2544,7 +2672,7 @@ var mockSetValidationRulesTool = {
|
|
|
2544
2672
|
const next = patchEndpoint2(mock, input.endpointId, (e) => ({
|
|
2545
2673
|
...e,
|
|
2546
2674
|
requestValidation: rules.map((r) => ({
|
|
2547
|
-
id: r.id ??
|
|
2675
|
+
id: r.id ?? generateId5(),
|
|
2548
2676
|
kind: r.kind,
|
|
2549
2677
|
target: r.target,
|
|
2550
2678
|
expected: r.expected,
|
|
@@ -2561,10 +2689,10 @@ var mockSetValidationRulesTool = {
|
|
|
2561
2689
|
var mockSetResponseRulesTool = {
|
|
2562
2690
|
name: "mock.set_response_rules",
|
|
2563
2691
|
description: "Replace an endpoint's conditional response rules. Rules fire in order; the first whose every clause matches wins. Disabled rules are skipped. Empty array falls back to defaultResponse.",
|
|
2564
|
-
inputSchema:
|
|
2565
|
-
mockId:
|
|
2566
|
-
endpointId:
|
|
2567
|
-
rules:
|
|
2692
|
+
inputSchema: z11.object({
|
|
2693
|
+
mockId: z11.string(),
|
|
2694
|
+
endpointId: z11.string(),
|
|
2695
|
+
rules: z11.array(RESPONSE_RULE)
|
|
2568
2696
|
}),
|
|
2569
2697
|
async handler(input, ctx) {
|
|
2570
2698
|
const state = await ctx.workspace.read();
|
|
@@ -2574,11 +2702,11 @@ var mockSetResponseRulesTool = {
|
|
|
2574
2702
|
const next = patchEndpoint2(mock, input.endpointId, (e) => ({
|
|
2575
2703
|
...e,
|
|
2576
2704
|
responseRules: rules.map((r) => ({
|
|
2577
|
-
id: r.id ??
|
|
2705
|
+
id: r.id ?? generateId5(),
|
|
2578
2706
|
name: r.name,
|
|
2579
2707
|
enabled: r.enabled,
|
|
2580
2708
|
when: r.when.map((c) => ({
|
|
2581
|
-
id: c.id ??
|
|
2709
|
+
id: c.id ?? generateId5(),
|
|
2582
2710
|
scope: c.scope,
|
|
2583
2711
|
target: c.target,
|
|
2584
2712
|
op: c.op,
|
|
@@ -2595,10 +2723,10 @@ var mockSetResponseRulesTool = {
|
|
|
2595
2723
|
var mockSetMultipliersTool = {
|
|
2596
2724
|
name: "mock.set_multipliers",
|
|
2597
2725
|
description: "Replace the response multipliers on an endpoint's defaultResponse. Multipliers expand an array at `targetJsonPath` to a count derived from a request value. Empty array clears all multipliers.",
|
|
2598
|
-
inputSchema:
|
|
2599
|
-
mockId:
|
|
2600
|
-
endpointId:
|
|
2601
|
-
multipliers:
|
|
2726
|
+
inputSchema: z11.object({
|
|
2727
|
+
mockId: z11.string(),
|
|
2728
|
+
endpointId: z11.string(),
|
|
2729
|
+
multipliers: z11.array(MULTIPLIER)
|
|
2602
2730
|
}),
|
|
2603
2731
|
async handler(input, ctx) {
|
|
2604
2732
|
const state = await ctx.workspace.read();
|
|
@@ -2610,7 +2738,7 @@ var mockSetMultipliersTool = {
|
|
|
2610
2738
|
defaultResponse: {
|
|
2611
2739
|
...e.defaultResponse,
|
|
2612
2740
|
multipliers: multipliers.length === 0 ? void 0 : multipliers.map((m) => ({
|
|
2613
|
-
id: m.id ??
|
|
2741
|
+
id: m.id ?? generateId5(),
|
|
2614
2742
|
name: m.name,
|
|
2615
2743
|
source: { kind: m.source.kind, key: m.source.key },
|
|
2616
2744
|
targetJsonPath: m.targetJsonPath,
|
|
@@ -2686,6 +2814,10 @@ var TOOL_REGISTRY = [
|
|
|
2686
2814
|
promptSetEndpointValidationRulesTool,
|
|
2687
2815
|
promptSetEndpointResponseRulesTool,
|
|
2688
2816
|
promptSetEndpointMultipliersTool,
|
|
2817
|
+
globalAssetsFilesListTool,
|
|
2818
|
+
globalAssetsFilesCreateTool,
|
|
2819
|
+
globalAssetsFilesUpdateTool,
|
|
2820
|
+
globalAssetsFilesDeleteTool,
|
|
2689
2821
|
mockCreateFromOpenApiTool,
|
|
2690
2822
|
mockCreateFromPostmanTool,
|
|
2691
2823
|
mockCreateFromInsomniaTool,
|
|
@@ -2832,17 +2964,58 @@ import {
|
|
|
2832
2964
|
setActiveWorkspace as setActiveWorkspaceOnDisk,
|
|
2833
2965
|
workspaceDirFor
|
|
2834
2966
|
} from "@apicircle/core/workspace/registry";
|
|
2967
|
+
var LazyActiveWorkspaceProvider = class {
|
|
2968
|
+
constructor(registryRoot, onActiveResolved) {
|
|
2969
|
+
this.registryRoot = registryRoot;
|
|
2970
|
+
this.onActiveResolved = onActiveResolved;
|
|
2971
|
+
}
|
|
2972
|
+
registryRoot;
|
|
2973
|
+
onActiveResolved;
|
|
2974
|
+
async resolveActive() {
|
|
2975
|
+
const registry = await loadRegistry(this.registryRoot);
|
|
2976
|
+
const activeId = registry?.activeWorkspaceId ?? null;
|
|
2977
|
+
if (!activeId) {
|
|
2978
|
+
throw new Error(
|
|
2979
|
+
"No active workspace. Open the desktop app at least once, or run `apicircle workspaces create <name>`."
|
|
2980
|
+
);
|
|
2981
|
+
}
|
|
2982
|
+
this.onActiveResolved(activeId);
|
|
2983
|
+
return new FileBackedWorkspaceProvider(workspaceDirFor(this.registryRoot, activeId));
|
|
2984
|
+
}
|
|
2985
|
+
async read() {
|
|
2986
|
+
const provider = await this.resolveActive();
|
|
2987
|
+
return provider.read();
|
|
2988
|
+
}
|
|
2989
|
+
async apply(patch) {
|
|
2990
|
+
const provider = await this.resolveActive();
|
|
2991
|
+
return provider.apply(patch);
|
|
2992
|
+
}
|
|
2993
|
+
async write(next) {
|
|
2994
|
+
const provider = await this.resolveActive();
|
|
2995
|
+
return provider.write(next);
|
|
2996
|
+
}
|
|
2997
|
+
};
|
|
2835
2998
|
var MultiWorkspaceProvider = class {
|
|
2836
2999
|
constructor(registryRoot) {
|
|
2837
3000
|
this.registryRoot = registryRoot;
|
|
3001
|
+
this.lazyProvider = new LazyActiveWorkspaceProvider(this.registryRoot, (id) => {
|
|
3002
|
+
this.activeWorkspaceId = id;
|
|
3003
|
+
});
|
|
2838
3004
|
}
|
|
2839
3005
|
registryRoot;
|
|
2840
|
-
active
|
|
3006
|
+
/** Last-known active workspace id. Refreshed every time the lazy
|
|
3007
|
+
* provider resolves; reflects what the most recent operation saw on
|
|
3008
|
+
* disk, not a stale boot-time snapshot. */
|
|
2841
3009
|
activeWorkspaceId = null;
|
|
3010
|
+
/** The lazy provider tool handlers consume as `ctx.workspace`. Holds a
|
|
3011
|
+
* reference back to this instance so each call updates
|
|
3012
|
+
* `activeWorkspaceId` for `activeId()` callers + diagnostic logs. */
|
|
3013
|
+
lazyProvider;
|
|
2842
3014
|
/**
|
|
2843
|
-
*
|
|
2844
|
-
*
|
|
2845
|
-
* registry
|
|
3015
|
+
* Read the registry from disk so the host can log a boot banner. Does
|
|
3016
|
+
* NOT cache a per-id provider — each `activeProvider()` call re-reads
|
|
3017
|
+
* the registry, so a workspace switch in the desktop is picked up by
|
|
3018
|
+
* the next tool call without restarting the MCP server.
|
|
2846
3019
|
*/
|
|
2847
3020
|
async init() {
|
|
2848
3021
|
const registry = await loadRegistry(this.registryRoot) ?? {
|
|
@@ -2850,22 +3023,18 @@ var MultiWorkspaceProvider = class {
|
|
|
2850
3023
|
activeWorkspaceId: null,
|
|
2851
3024
|
workspaces: []
|
|
2852
3025
|
};
|
|
2853
|
-
|
|
2854
|
-
this.activeWorkspaceId = registry.activeWorkspaceId;
|
|
2855
|
-
this.active = new FileBackedWorkspaceProvider(
|
|
2856
|
-
workspaceDirFor(this.registryRoot, registry.activeWorkspaceId)
|
|
2857
|
-
);
|
|
2858
|
-
}
|
|
3026
|
+
this.activeWorkspaceId = registry.activeWorkspaceId;
|
|
2859
3027
|
return registry;
|
|
2860
3028
|
}
|
|
2861
|
-
/**
|
|
3029
|
+
/**
|
|
3030
|
+
* The provider tool handlers see as `ctx.workspace`. Returns a lazy
|
|
3031
|
+
* provider whose `read` / `apply` / `write` calls re-read
|
|
3032
|
+
* `registry.json` so the right active workspace is always targeted
|
|
3033
|
+
* even if the desktop switched workspaces since this MCP process
|
|
3034
|
+
* started.
|
|
3035
|
+
*/
|
|
2862
3036
|
activeProvider() {
|
|
2863
|
-
|
|
2864
|
-
throw new Error(
|
|
2865
|
-
"No active workspace. Open the desktop app at least once, or run `apicircle workspaces create <name>`."
|
|
2866
|
-
);
|
|
2867
|
-
}
|
|
2868
|
-
return this.active;
|
|
3037
|
+
return this.lazyProvider;
|
|
2869
3038
|
}
|
|
2870
3039
|
// ─── Workspaces interface ──────────────────────────────────────────────────
|
|
2871
3040
|
async list() {
|
|
@@ -2913,23 +3082,17 @@ var MultiWorkspaceProvider = class {
|
|
|
2913
3082
|
if (!registry || !registry.workspaces.some((w) => w.id === workspaceId)) {
|
|
2914
3083
|
throw new WorkspaceNotFoundError(workspaceId);
|
|
2915
3084
|
}
|
|
2916
|
-
|
|
2917
|
-
void next;
|
|
3085
|
+
await setActiveWorkspaceOnDisk(this.registryRoot, workspaceId);
|
|
2918
3086
|
this.activeWorkspaceId = workspaceId;
|
|
2919
|
-
this.active = new FileBackedWorkspaceProvider(workspaceDirFor(this.registryRoot, workspaceId));
|
|
2920
3087
|
}
|
|
2921
3088
|
/**
|
|
2922
3089
|
* Idempotent registry write — used by tests / tools that need to
|
|
2923
|
-
* persist registry updates that didn't go through `setActive`.
|
|
3090
|
+
* persist registry updates that didn't go through `setActive`. The
|
|
3091
|
+
* lazy active provider picks the new id up on its next operation.
|
|
2924
3092
|
*/
|
|
2925
3093
|
async writeRegistry(registry) {
|
|
2926
3094
|
await saveRegistry(this.registryRoot, registry);
|
|
2927
3095
|
this.activeWorkspaceId = registry.activeWorkspaceId;
|
|
2928
|
-
if (registry.activeWorkspaceId) {
|
|
2929
|
-
this.active = new FileBackedWorkspaceProvider(
|
|
2930
|
-
workspaceDirFor(this.registryRoot, registry.activeWorkspaceId)
|
|
2931
|
-
);
|
|
2932
|
-
}
|
|
2933
3096
|
}
|
|
2934
3097
|
};
|
|
2935
3098
|
|