@minicor/mcp-server 3.3.4 → 3.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 +148 -25
- package/dist/__tests__/middleware-service-client.test.d.ts +2 -0
- package/dist/__tests__/middleware-service-client.test.d.ts.map +1 -0
- package/dist/__tests__/middleware-service-client.test.js +293 -0
- package/dist/__tests__/middleware-service-client.test.js.map +1 -0
- package/dist/codemode.js +10 -9
- package/dist/codemode.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/lib.d.ts +1 -1
- package/dist/lib.d.ts.map +1 -1
- package/dist/middleware-service-client.d.ts +258 -0
- package/dist/middleware-service-client.d.ts.map +1 -0
- package/dist/middleware-service-client.js +264 -0
- package/dist/middleware-service-client.js.map +1 -0
- package/dist/prompts/build-rpa.d.ts.map +1 -1
- package/dist/prompts/build-rpa.js +17 -0
- package/dist/prompts/build-rpa.js.map +1 -1
- package/dist/prompts/nestjs-to-job.d.ts +11 -0
- package/dist/prompts/nestjs-to-job.d.ts.map +1 -0
- package/dist/prompts/nestjs-to-job.js +110 -0
- package/dist/prompts/nestjs-to-job.js.map +1 -0
- package/dist/tools/jobs.d.ts +21 -0
- package/dist/tools/jobs.d.ts.map +1 -0
- package/dist/tools/jobs.js +470 -0
- package/dist/tools/jobs.js.map +1 -0
- package/dist/tools/vm-rpa.d.ts.map +1 -1
- package/dist/tools/vm-rpa.js +109 -25
- package/dist/tools/vm-rpa.js.map +1 -1
- package/package.json +1 -1
- package/skills/general/nestjs-middleware-to-job.md +82 -0
package/README.md
CHANGED
|
@@ -19,20 +19,25 @@ Build, deploy, and debug VM-based browser and desktop automations from **Cursor*
|
|
|
19
19
|
}
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
**Claude Code
|
|
22
|
+
**Claude Code** — no global install needed (npx always runs the latest):
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
|
|
26
|
-
claude mcp add minicor -- minicor-mcp
|
|
25
|
+
claude mcp add minicor -- npx -y @minicor/mcp-server
|
|
27
26
|
```
|
|
28
27
|
|
|
29
28
|
### 2. Authenticate
|
|
30
29
|
|
|
31
30
|
```bash
|
|
32
|
-
npx @minicor/mcp-server
|
|
31
|
+
npx -y -p @minicor/mcp-server minicor-mcp-setup
|
|
33
32
|
```
|
|
34
33
|
|
|
35
|
-
Opens a browser to sign in. Tokens stored at `~/.minicor/tokens.json` with auto-refresh. For headless
|
|
34
|
+
Opens a browser to sign in. Tokens are stored at `~/.minicor/tokens.json` with auto-refresh. For headless / SSH:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx -y -p @minicor/mcp-server minicor-mcp-setup --cli
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
> The package ships multiple commands. Always use `npx -p @minicor/mcp-server <command>` to run a specific one (`minicor-mcp-setup`, `minicor-bootstrap`, …). Running `npx @minicor/mcp-server` alone only starts the MCP server and ignores trailing arguments.
|
|
36
41
|
|
|
37
42
|
### 3. Bootstrap Workspace (optional)
|
|
38
43
|
|
|
@@ -40,22 +45,44 @@ After cloning a workspace, generate harness-specific config files:
|
|
|
40
45
|
|
|
41
46
|
```bash
|
|
42
47
|
# For Claude Code — creates CLAUDE.md + .claude/settings.local.json
|
|
43
|
-
npx @minicor/mcp-server minicor-bootstrap claude-code --dir ./my-workspace
|
|
48
|
+
npx -y -p @minicor/mcp-server minicor-bootstrap claude-code --dir ./my-workspace
|
|
44
49
|
|
|
45
50
|
# For Cursor — creates .cursor/rules/workspace.mdc
|
|
46
|
-
npx @minicor/mcp-server minicor-bootstrap cursor --dir ./my-workspace
|
|
51
|
+
npx -y -p @minicor/mcp-server minicor-bootstrap cursor --dir ./my-workspace
|
|
47
52
|
|
|
48
53
|
# All harnesses at once
|
|
49
|
-
npx @minicor/mcp-server minicor-bootstrap all --dir ./my-workspace
|
|
54
|
+
npx -y -p @minicor/mcp-server minicor-bootstrap all --dir ./my-workspace
|
|
50
55
|
```
|
|
51
56
|
|
|
52
57
|
### 4. Update
|
|
53
58
|
|
|
54
59
|
`npx` users: restart your editor (auto-fetches latest). Global: `npm update -g @minicor/mcp-server`.
|
|
55
60
|
|
|
56
|
-
## Code Mode
|
|
61
|
+
## Code Mode (experimental, opt-in)
|
|
62
|
+
|
|
63
|
+
By default the server exposes every Minicor operation as its own MCP tool. As an **experimental** alternative, you can enable a code-mode surface: a single `codemode` tool that runs JavaScript and provides Minicor methods as async functions on the `minicor` global.
|
|
64
|
+
|
|
65
|
+
Enable it by setting the environment variable before launching the server:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
EXPERIMENTAL_USE_CODE_MODE=1 minicor-mcp
|
|
69
|
+
```
|
|
57
70
|
|
|
58
|
-
|
|
71
|
+
In Cursor, add it to the server's `env` block in `~/.cursor/mcp.json`:
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"mcpServers": {
|
|
76
|
+
"minicor": {
|
|
77
|
+
"command": "npx",
|
|
78
|
+
"args": ["-y", "@minicor/mcp-server"],
|
|
79
|
+
"env": { "EXPERIMENTAL_USE_CODE_MODE": "1" }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Once enabled:
|
|
59
86
|
|
|
60
87
|
```javascript
|
|
61
88
|
const workspaces = await minicor.list_workspaces({});
|
|
@@ -73,21 +100,17 @@ Inside `codemode`:
|
|
|
73
100
|
- `await codemode.describe("minicor.tool_name")` returns TypeScript-style parameter docs.
|
|
74
101
|
- `await codemode.list()` lists all available methods.
|
|
75
102
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
MINICOR_MCP_LEGACY_TOOLS=1 minicor-mcp
|
|
80
|
-
# or
|
|
81
|
-
MINICOR_MCP_CODE_MODE=0 minicor-mcp
|
|
82
|
-
```
|
|
103
|
+
Unset `EXPERIMENTAL_USE_CODE_MODE` to return to the default one-tool-per-operation surface.
|
|
83
104
|
|
|
84
105
|
## Session Lifecycle
|
|
85
106
|
|
|
86
|
-
The MCP server sends **instructions** to every client on connect, telling agents to start a session first when in a workspace clone.
|
|
107
|
+
The MCP server sends **instructions** to every client on connect, telling agents to start a session first when in a workspace clone. Call the `session_start` tool with the workspace directory:
|
|
87
108
|
|
|
88
|
-
```javascript
|
|
89
|
-
return await minicor.session_start({ directory: "/path/to/workspace" });
|
|
90
109
|
```
|
|
110
|
+
session_start({ directory: "/path/to/workspace" })
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
(With experimental code mode enabled, run the same call as `await minicor.session_start({ ... })` through the `codemode` tool.)
|
|
91
114
|
|
|
92
115
|
This works across all harnesses, including Claude Code, Cursor, and Bedrock.
|
|
93
116
|
|
|
@@ -195,9 +218,75 @@ After a successful build:
|
|
|
195
218
|
- Skills are stored per-workspace and available to future builds
|
|
196
219
|
- Customer-specific quirks become reusable knowledge
|
|
197
220
|
|
|
221
|
+
## Jobs (RPA orchestration)
|
|
222
|
+
|
|
223
|
+
A **Job** is a middleware route whose behavior is a structured `definition` (a step graph) instead of freeform handler code. Jobs sit **above** workflows: a job step either calls an entire Minicor workflow (`kind: "workflow"`, by `workflowId`) or runs a code block (`kind: "code"`). The job never touches a workflow's internal steps — a workflow is an opaque callable with an input and an output. A job is the **macro zoom** (what ran, status, replays); a workflow → step is the **microscope**. This is the layer that turns one or more workflows into a versioned, testable, observable API endpoint.
|
|
224
|
+
|
|
225
|
+
### The job tools
|
|
226
|
+
|
|
227
|
+
| Tool | What it does |
|
|
228
|
+
| --- | --- |
|
|
229
|
+
| `list_middlewares` | List the routers (middlewares) in a workspace — jobs attach to one of these. |
|
|
230
|
+
| `register_middleware` | Create a router jobs attach to. `autoConfigure` (default true) populates the router's `workspaceApiKey` from the workspace's first key; or pass `workspaceApiKey` explicitly. |
|
|
231
|
+
| `register_job` | Create a job — a route backed by a structured `definition` (step graph). |
|
|
232
|
+
| `update_job` | Update a job's `definition`, path, method, or description (snapshots a new route version). |
|
|
233
|
+
| `get_job` | Get a job (route) incl. its `definition`; omit `routeId` to list all routes on the router. |
|
|
234
|
+
| `run_job` | Run a job once with an input, poll to completion, return the full `JobExecution` (per-step Minicor deep-links). |
|
|
235
|
+
| `add_test_case` | Attach a test case to a job: named `input` + assertions `[{ name, expr }]` (the green-gate). |
|
|
236
|
+
| `run_tests` | Run a job's test suite (or a subset), poll to completion, return the report (`green`, totals, per-case). |
|
|
237
|
+
| `get_test_report` | Fetch a test run report by id (totals + per-case results, each linking to its job execution). |
|
|
238
|
+
| `list_workspace_api_keys` | List a workspace's API keys (id, apiKey, name) — to find a key for the two tools below. |
|
|
239
|
+
| `set_middleware_api_key` | Set a router's `workspaceApiKey` after the fact, then re-read to confirm it stuck. |
|
|
240
|
+
|
|
241
|
+
### The recommended build loop — test-driven → job → workflow
|
|
242
|
+
|
|
243
|
+
You work **backwards from the job's test cases**. The job's test suite is the macro green-gate; individual workflows are the micro RPA underneath.
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
1. CONTRACT FIRST — define the JOB's input/output and its test cases (the macro green-gate)
|
|
247
|
+
via register_job + add_test_case. Cases start RED.
|
|
248
|
+
2. BUILD WORKFLOWS — build/curate the Minicor workflow(s) each step will call (the micro RPA).
|
|
249
|
+
3. WIRE THE STEPS — wire workflows as `kind: "workflow"` steps with `input` mappings,
|
|
250
|
+
plus `when` / `forEach` / `cache` / `onError` as needed.
|
|
251
|
+
4. RUN TESTS — run_tests until green: true. (run_job to inspect a single execution.)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
A job step supports `saveAs`, `when` (conditional skip), `forEach` (fan-out), `onError` (`abort`/`skip`/`continue`/`{goto}` — `goto` currently treated as abort), `retry`, and `cache` (a login/smart-launch gate). Use `resource.lockKey` to serialize execution per desktop/VM. To migrate an existing NestJS customer middleware onto this layer, use the `nestjs-middleware-to-job` prompt or skill.
|
|
255
|
+
|
|
256
|
+
### ctx / job-shape primer
|
|
257
|
+
|
|
258
|
+
The `definition` you pass to `register_job` (see `register_job`'s `definition` param for the full schema):
|
|
259
|
+
|
|
260
|
+
```ts
|
|
261
|
+
{
|
|
262
|
+
region: "us" | "ca", // region awareness — also auto-detected from workspace
|
|
263
|
+
resource?: { configStoreId?, lockKey? }, // configStoreId = which VM/desktop; lockKey serializes runs
|
|
264
|
+
inputSchema: JSONSchema,
|
|
265
|
+
outputSchema: JSONSchema,
|
|
266
|
+
steps: [ { id, name, kind, saveAs?, when?, forEach?, onError?, retry?, cache?,
|
|
267
|
+
// kind:"workflow" → workflowId + input (object with {{ctx.x}} OR a (ctx)=>({...}) code block)
|
|
268
|
+
// kind:"code" → code: "(ctx) => ..."
|
|
269
|
+
} ],
|
|
270
|
+
output?: "(ctx) => response", // builds the job's response from the final ctx
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**`ctx` is the data through-line** between steps — each step's `saveAs` key is what the next step reads:
|
|
275
|
+
|
|
276
|
+
- `ctx` starts as `{ input: <the job input> }`.
|
|
277
|
+
- Each step's result is stored at `ctx.<saveAs>` (when `saveAs` is set), so later steps / `when` / `forEach` / `output` can read it.
|
|
278
|
+
- Inside a `forEach` step, the current item is `ctx.item` (results are collected into an array under `saveAs`).
|
|
279
|
+
- The top-level `output` block `(ctx) => ...` builds the job's response from the accumulated `ctx`.
|
|
280
|
+
|
|
281
|
+
Assertions (`add_test_case`) are TS booleans over `{ input, output, ctx, steps }`, e.g. `output.status === "COMPLETED"`. The full model + runner semantics live in the **middleware-service** repo's `README.md` ("## Jobs") and `middlewares/JOBS-DESIGN.md`.
|
|
282
|
+
|
|
283
|
+
### Region & API key
|
|
284
|
+
|
|
285
|
+
Jobs are **region-aware** (`us` / `ca`) — pass `region` or let it be auto-detected from the workspace. Running a job requires the router's `workspaceApiKey` to be set so a job's workflow steps can call the Laminar API: it's populated by `register_middleware`'s `autoConfigure` (default), or set later with `set_middleware_api_key` (find a key via `list_workspace_api_keys`). `register_middleware` / `set_middleware_api_key` report `workspaceApiKeyConfigured` so you can confirm it stuck.
|
|
286
|
+
|
|
198
287
|
## Skills System
|
|
199
288
|
|
|
200
|
-
Skills are reusable RPA patterns that accumulate as you build automations. The MCP ships with
|
|
289
|
+
Skills are reusable RPA patterns that accumulate as you build automations. The MCP ships with bundled general skills, including:
|
|
201
290
|
|
|
202
291
|
|
|
203
292
|
| Skill | Purpose |
|
|
@@ -208,6 +297,7 @@ Skills are reusable RPA patterns that accumulate as you build automations. The M
|
|
|
208
297
|
| `data-extraction-strategies` | Priority-ordered methods for reading data from screen |
|
|
209
298
|
| `data-passing-between-steps` | JS-layer interpolation, config variables, step outputs |
|
|
210
299
|
| `state-verification` | LLM-based UI verification for production workflows |
|
|
300
|
+
| `nestjs-middleware-to-job` | Map a NestJS middleware controller onto the Jobs layer (step graph + test cases) |
|
|
211
301
|
|
|
212
302
|
|
|
213
303
|
### Using Skills
|
|
@@ -300,6 +390,26 @@ For manual setup, use `get_lds_setup_guide` for step-by-step instructions.
|
|
|
300
390
|
| `preview_flow_changes` / `compare_flow_versions` | Diff code before deploying |
|
|
301
391
|
|
|
302
392
|
|
|
393
|
+
### Jobs & Test Cases
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
| Tool | What it does |
|
|
397
|
+
| --------------------- | -------------------------------------------------------------------------------------------- |
|
|
398
|
+
| `list_middlewares` | List the routers (middlewares) in a workspace — jobs attach to one of these |
|
|
399
|
+
| `register_middleware` | Create a router (middleware) jobs attach to; `autoConfigure` populates its workspace API key |
|
|
400
|
+
| `register_job` | Create a job — a middleware route backed by a structured `definition` (step graph) |
|
|
401
|
+
| `update_job` | Update a job's `definition`, path, method, or description (snapshots a new route version) |
|
|
402
|
+
| `get_job` | Get a job (route) incl. its `definition`; omit `routeId` to list all routes on the router |
|
|
403
|
+
| `run_job` | Run a job once with an input, poll to completion, return the full `JobExecution` |
|
|
404
|
+
| `add_test_case` | Attach a test case to a job: named `input` + assertions `[{ name, expr }]` (the green-gate) |
|
|
405
|
+
| `run_tests` | Run a job's test suite, poll to completion, return the report (`green`, totals, per-case) |
|
|
406
|
+
| `get_test_report` | Fetch a test run report by id (totals + per-case results, each linking to its job execution) |
|
|
407
|
+
| `list_workspace_api_keys` | List a workspace's API keys (id, apiKey, name) — find one for the two tools below |
|
|
408
|
+
| `set_middleware_api_key` | Set a router's `workspaceApiKey` (then re-reads to confirm it stuck) |
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
See the **Jobs (RPA orchestration)** section above for the build loop and job/ctx shape. `batch_test_rpa` can also persist each passing input as a real job test case — set `persistAsTestCases` with `routeId` + `middlewareId` + `workspaceId`.
|
|
412
|
+
|
|
303
413
|
### 2FA / OTP
|
|
304
414
|
|
|
305
415
|
|
|
@@ -410,6 +520,7 @@ Agents are autonomous runners that execute tasks on VMs using MCP tools. After b
|
|
|
410
520
|
| `minicor-workflow-guide` | Full workflow specification: step types, data access, keywords, libraries |
|
|
411
521
|
| `2fa-workflow-guide` | Handling 2FA in workflows: provisioning channels, TOTP secrets, auto-resolving OTP codes |
|
|
412
522
|
| `workspace-session-guide` | Returns AGENTS.md rules and session state for any workspace clone |
|
|
523
|
+
| `nestjs-middleware-to-job` | Convert an existing NestJS customer middleware endpoint into a Job (structured step graph) with test cases as the green-gate |
|
|
413
524
|
|
|
414
525
|
|
|
415
526
|
The `build-rpa-workflow` prompt accepts a `mode` parameter:
|
|
@@ -421,14 +532,26 @@ The `build-rpa-workflow` prompt accepts a `mode` parameter:
|
|
|
421
532
|
## Environment Variables
|
|
422
533
|
|
|
423
534
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
|
535
|
+
Jobs/middleware base resolution (precedence: **proxy > direct > default**):
|
|
536
|
+
|
|
537
|
+
| Variable | Purpose | Default |
|
|
538
|
+
| --------------------------------- | ------------------------------------------------------------------- | ---------------------- |
|
|
539
|
+
| `VM_MANAGER_API_URL` | VM Manager service URL | Cloud Run instance |
|
|
540
|
+
| `MIDDLEWARE_SERVICE_PROXY_BASE` | Frontend origin fronting the middleware proxy, all regions | unset |
|
|
541
|
+
| `MIDDLEWARE_SERVICE_PROXY_BASE_US`| Frontend proxy origin for US (overrides the generic one) | unset |
|
|
542
|
+
| `MIDDLEWARE_SERVICE_PROXY_BASE_CA`| Frontend proxy origin for CA (overrides the generic one) | unset |
|
|
543
|
+
| `MIDDLEWARE_SERVICE_PROXY_PREFIX` | Proxy path prefix | `/api/middleware` |
|
|
544
|
+
| `MIDDLEWARE_SERVICE_URL` | Direct middleware-service base, all regions (dev/local) | unset |
|
|
545
|
+
| `MIDDLEWARE_SERVICE_URL_US` | Direct middleware-service base for US (overrides the generic one) | unset |
|
|
546
|
+
| `MIDDLEWARE_SERVICE_URL_CA` | Direct middleware-service base for CA (overrides the generic one) | unset |
|
|
547
|
+
| `EXPERIMENTAL_USE_CODE_MODE` | Expose the experimental single `codemode` tool surface | unset (standard tools) |
|
|
548
|
+
|
|
549
|
+
When none are set, Jobs calls the region's public middleware-service Cloud Run directly. To route through the frontend (once it deploys an `/api/middleware/[...path]` proxy), set `MIDDLEWARE_SERVICE_PROXY_BASE` (or the per-region variants).
|
|
427
550
|
|
|
428
551
|
|
|
429
552
|
## Auth
|
|
430
553
|
|
|
431
|
-
- **Sign in**: `minicor-mcp-setup` (browser) or
|
|
554
|
+
- **Sign in**: `npx -y -p @minicor/mcp-server minicor-mcp-setup` (browser) or append `--cli` for headless
|
|
432
555
|
- **Token storage**: `~/.minicor/tokens.json`
|
|
433
556
|
- **Auto-refresh**: tokens refresh before expiry
|
|
434
557
|
- **Regions**: US (default) or Canada
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware-service-client.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/middleware-service-client.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { MiddlewareServiceClient } from "../middleware-service-client.js";
|
|
3
|
+
/**
|
|
4
|
+
* The client only parses the body as JSON when the response carries an
|
|
5
|
+
* `application/json` content-type, so tests must set it explicitly (this also
|
|
6
|
+
* documents the contract the frontend proxy must satisfy).
|
|
7
|
+
*/
|
|
8
|
+
function jsonResponse(body, status = 200) {
|
|
9
|
+
return new Response(JSON.stringify(body), {
|
|
10
|
+
status,
|
|
11
|
+
headers: { "Content-Type": "application/json" },
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
describe("MiddlewareServiceClient", () => {
|
|
15
|
+
const originalFetch = globalThis.fetch;
|
|
16
|
+
const apiBase = "https://api.minicor.com";
|
|
17
|
+
const token = "user-bearer-xyz";
|
|
18
|
+
const ws = 257;
|
|
19
|
+
const mw = "mw-1";
|
|
20
|
+
const route = "route-1";
|
|
21
|
+
// Default (deployed): the region's public middleware-service Cloud Run, hit
|
|
22
|
+
// directly at `${base}/api/workspaces/...`. (us by default for api.minicor.com.)
|
|
23
|
+
const US_DIRECT = "https://middleware-service-489511347341.northamerica-northeast1.run.app";
|
|
24
|
+
const middlewaresBase = `${US_DIRECT}/api/workspaces/257/middlewares`;
|
|
25
|
+
const routesBase = `${middlewaresBase}/mw-1/routes`;
|
|
26
|
+
const ENV_KEYS = [
|
|
27
|
+
"MIDDLEWARE_SERVICE_URL",
|
|
28
|
+
"MIDDLEWARE_SERVICE_URL_US",
|
|
29
|
+
"MIDDLEWARE_SERVICE_URL_CA",
|
|
30
|
+
"MIDDLEWARE_SERVICE_PROXY_BASE",
|
|
31
|
+
"MIDDLEWARE_SERVICE_PROXY_BASE_US",
|
|
32
|
+
"MIDDLEWARE_SERVICE_PROXY_BASE_CA",
|
|
33
|
+
"MIDDLEWARE_SERVICE_PROXY_PREFIX",
|
|
34
|
+
];
|
|
35
|
+
const savedEnv = {};
|
|
36
|
+
let client;
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
for (const k of ENV_KEYS) {
|
|
39
|
+
savedEnv[k] = process.env[k];
|
|
40
|
+
delete process.env[k];
|
|
41
|
+
}
|
|
42
|
+
globalThis.fetch = vi.fn();
|
|
43
|
+
client = new MiddlewareServiceClient(apiBase, token);
|
|
44
|
+
});
|
|
45
|
+
afterEach(() => {
|
|
46
|
+
for (const k of ENV_KEYS) {
|
|
47
|
+
if (savedEnv[k] === undefined)
|
|
48
|
+
delete process.env[k];
|
|
49
|
+
else
|
|
50
|
+
process.env[k] = savedEnv[k];
|
|
51
|
+
}
|
|
52
|
+
globalThis.fetch = originalFetch;
|
|
53
|
+
vi.restoreAllMocks();
|
|
54
|
+
});
|
|
55
|
+
function lastCall() {
|
|
56
|
+
const { calls } = vi.mocked(globalThis.fetch).mock;
|
|
57
|
+
return calls[calls.length - 1];
|
|
58
|
+
}
|
|
59
|
+
function lastUrl() {
|
|
60
|
+
return lastCall()[0];
|
|
61
|
+
}
|
|
62
|
+
function lastInit() {
|
|
63
|
+
return lastCall()[1];
|
|
64
|
+
}
|
|
65
|
+
function lastBody() {
|
|
66
|
+
return JSON.parse(lastInit().body);
|
|
67
|
+
}
|
|
68
|
+
it("defaults to the US Cloud Run (direct /api/workspaces) with the bearer token", async () => {
|
|
69
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse({ id: "exec-1" }));
|
|
70
|
+
await client.getExecution(ws, mw, route, "exec-1");
|
|
71
|
+
expect(lastUrl()).toBe(`${routesBase}/route-1/executions/exec-1`);
|
|
72
|
+
expect(lastInit().method).toBe("GET");
|
|
73
|
+
expect(lastInit().headers.Authorization).toBe(`Bearer ${token}`);
|
|
74
|
+
});
|
|
75
|
+
it("defaults to the CA Cloud Run for a CA api base", async () => {
|
|
76
|
+
const ca = new MiddlewareServiceClient("https://ca.api.minicor.com", token);
|
|
77
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse([]));
|
|
78
|
+
await ca.listRoutes(ws, mw);
|
|
79
|
+
expect(lastUrl()).toBe("https://middleware-service-ca-ernfuwetfa-nn.a.run.app/api/workspaces/257/middlewares/mw-1/routes");
|
|
80
|
+
});
|
|
81
|
+
it("routes through the frontend proxy when MIDDLEWARE_SERVICE_PROXY_BASE is set (/api/ stripped)", async () => {
|
|
82
|
+
process.env.MIDDLEWARE_SERVICE_PROXY_BASE = "https://app.minicor.com";
|
|
83
|
+
const proxied = new MiddlewareServiceClient(apiBase, token);
|
|
84
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse([]));
|
|
85
|
+
await proxied.listRoutes(ws, mw);
|
|
86
|
+
expect(lastUrl()).toBe("https://app.minicor.com/api/middleware/workspaces/257/middlewares/mw-1/routes");
|
|
87
|
+
expect(lastInit().headers.Authorization).toBe(`Bearer ${token}`);
|
|
88
|
+
});
|
|
89
|
+
it("uses a region-specific proxy base for CA, overriding the generic one", async () => {
|
|
90
|
+
process.env.MIDDLEWARE_SERVICE_PROXY_BASE = "https://app.laminar.run";
|
|
91
|
+
process.env.MIDDLEWARE_SERVICE_PROXY_BASE_CA = "https://ca.app.laminar.run";
|
|
92
|
+
const ca = new MiddlewareServiceClient("https://ca.api.laminar.run", token);
|
|
93
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse([]));
|
|
94
|
+
await ca.listRoutes(ws, mw);
|
|
95
|
+
expect(lastUrl()).toBe("https://ca.app.laminar.run/api/middleware/workspaces/257/middlewares/mw-1/routes");
|
|
96
|
+
});
|
|
97
|
+
it("falls back to the generic proxy base for US when no US-specific one is set", async () => {
|
|
98
|
+
process.env.MIDDLEWARE_SERVICE_PROXY_BASE = "https://app.laminar.run";
|
|
99
|
+
process.env.MIDDLEWARE_SERVICE_PROXY_BASE_CA = "https://ca.app.laminar.run";
|
|
100
|
+
const us = new MiddlewareServiceClient("https://api.laminar.run", token);
|
|
101
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse([]));
|
|
102
|
+
await us.listRoutes(ws, mw);
|
|
103
|
+
expect(lastUrl()).toBe("https://app.laminar.run/api/middleware/workspaces/257/middlewares/mw-1/routes");
|
|
104
|
+
});
|
|
105
|
+
it("honors a custom MIDDLEWARE_SERVICE_PROXY_PREFIX", async () => {
|
|
106
|
+
process.env.MIDDLEWARE_SERVICE_PROXY_BASE = "https://app.minicor.com";
|
|
107
|
+
process.env.MIDDLEWARE_SERVICE_PROXY_PREFIX = "api/mw-proxy";
|
|
108
|
+
const proxied = new MiddlewareServiceClient(apiBase, token);
|
|
109
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse([]));
|
|
110
|
+
await proxied.listRoutes(ws, mw);
|
|
111
|
+
expect(lastUrl()).toBe("https://app.minicor.com/api/mw-proxy/workspaces/257/middlewares/mw-1/routes");
|
|
112
|
+
});
|
|
113
|
+
it("proxy base takes precedence over a direct override", async () => {
|
|
114
|
+
process.env.MIDDLEWARE_SERVICE_PROXY_BASE = "https://app.minicor.com";
|
|
115
|
+
process.env.MIDDLEWARE_SERVICE_URL = "https://mw.dev.example.com";
|
|
116
|
+
const proxied = new MiddlewareServiceClient(apiBase, token);
|
|
117
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse([]));
|
|
118
|
+
await proxied.listRoutes(ws, mw);
|
|
119
|
+
expect(lastUrl()).toBe("https://app.minicor.com/api/middleware/workspaces/257/middlewares/mw-1/routes");
|
|
120
|
+
});
|
|
121
|
+
it("hits a direct base (no proxy) when MIDDLEWARE_SERVICE_URL is set", async () => {
|
|
122
|
+
process.env.MIDDLEWARE_SERVICE_URL = "https://mw.dev.example.com";
|
|
123
|
+
const direct = new MiddlewareServiceClient(apiBase, token);
|
|
124
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse([]));
|
|
125
|
+
await direct.listRoutes(ws, mw);
|
|
126
|
+
expect(lastUrl()).toBe("https://mw.dev.example.com/api/workspaces/257/middlewares/mw-1/routes");
|
|
127
|
+
});
|
|
128
|
+
it("MIDDLEWARE_SERVICE_URL_CA overrides only the CA region", async () => {
|
|
129
|
+
process.env.MIDDLEWARE_SERVICE_URL_CA = "https://mw-ca.example.com";
|
|
130
|
+
const ca = new MiddlewareServiceClient("https://ca.api.minicor.com", token);
|
|
131
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse([]));
|
|
132
|
+
await ca.listRoutes(ws, mw);
|
|
133
|
+
expect(lastUrl()).toBe("https://mw-ca.example.com/api/workspaces/257/middlewares/mw-1/routes");
|
|
134
|
+
});
|
|
135
|
+
it("listMiddlewares GETs the workspace middlewares", async () => {
|
|
136
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse([{ id: "mw-1" }, { id: "mw-2" }]));
|
|
137
|
+
const mws = await client.listMiddlewares(ws);
|
|
138
|
+
expect(lastUrl()).toBe(middlewaresBase);
|
|
139
|
+
expect(lastInit().method).toBe("GET");
|
|
140
|
+
expect(mws.map((m) => m.id)).toEqual(["mw-1", "mw-2"]);
|
|
141
|
+
});
|
|
142
|
+
it("createMiddleware POSTs name/slug/description", async () => {
|
|
143
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse({ id: "mw-9", name: "AMS", slug: "ams" }));
|
|
144
|
+
await client.createMiddleware(ws, { name: "AMS", slug: "ams", description: "d" });
|
|
145
|
+
expect(lastUrl()).toBe(middlewaresBase);
|
|
146
|
+
expect(lastInit().method).toBe("POST");
|
|
147
|
+
expect(lastBody()).toEqual({ name: "AMS", slug: "ams", description: "d" });
|
|
148
|
+
});
|
|
149
|
+
it("autoConfigureMiddleware POSTs to the auto-configure endpoint", async () => {
|
|
150
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse({}));
|
|
151
|
+
await client.autoConfigureMiddleware(ws, "mw-9");
|
|
152
|
+
expect(lastUrl()).toBe(`${middlewaresBase}/mw-9/auto-configure`);
|
|
153
|
+
expect(lastInit().method).toBe("POST");
|
|
154
|
+
});
|
|
155
|
+
it("createJob defaults method to POST and sends the definition", async () => {
|
|
156
|
+
const definition = {
|
|
157
|
+
region: "us",
|
|
158
|
+
inputSchema: {},
|
|
159
|
+
outputSchema: {},
|
|
160
|
+
steps: [],
|
|
161
|
+
};
|
|
162
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse({ id: route, path: "/coi" }));
|
|
163
|
+
await client.createJob(ws, mw, { path: "/coi", definition });
|
|
164
|
+
expect(lastUrl()).toBe(routesBase);
|
|
165
|
+
expect(lastInit().method).toBe("POST");
|
|
166
|
+
expect(lastBody()).toEqual({ method: "POST", path: "/coi", definition });
|
|
167
|
+
});
|
|
168
|
+
it("createJob keeps an explicit method", async () => {
|
|
169
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse({ id: route }));
|
|
170
|
+
await client.createJob(ws, mw, {
|
|
171
|
+
path: "/x",
|
|
172
|
+
method: "GET",
|
|
173
|
+
definition: {},
|
|
174
|
+
});
|
|
175
|
+
expect(lastBody().method).toBe("GET");
|
|
176
|
+
});
|
|
177
|
+
it("updateJob PUTs only the provided fields to the route id", async () => {
|
|
178
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse({ id: route }));
|
|
179
|
+
await client.updateJob(ws, mw, route, { description: "new desc" });
|
|
180
|
+
expect(lastUrl()).toBe(`${routesBase}/route-1`);
|
|
181
|
+
expect(lastInit().method).toBe("PUT");
|
|
182
|
+
expect(lastBody()).toEqual({ description: "new desc" });
|
|
183
|
+
});
|
|
184
|
+
it("listRoutes handles a bare array", async () => {
|
|
185
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse([{ id: "a" }, { id: "b" }]));
|
|
186
|
+
const routes = await client.listRoutes(ws, mw);
|
|
187
|
+
expect(routes.map((r) => r.id)).toEqual(["a", "b"]);
|
|
188
|
+
});
|
|
189
|
+
it("listRoutes handles a { routes } envelope", async () => {
|
|
190
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse({ routes: [{ id: "a" }] }));
|
|
191
|
+
const routes = await client.listRoutes(ws, mw);
|
|
192
|
+
expect(routes.map((r) => r.id)).toEqual(["a"]);
|
|
193
|
+
});
|
|
194
|
+
it("getJob filters the route list by id, returning undefined when absent", async () => {
|
|
195
|
+
vi.mocked(globalThis.fetch).mockResolvedValueOnce(jsonResponse([{ id: "a" }, { id: "route-1" }]));
|
|
196
|
+
expect((await client.getJob(ws, mw, "route-1"))?.id).toBe("route-1");
|
|
197
|
+
vi.mocked(globalThis.fetch).mockResolvedValueOnce(jsonResponse([{ id: "a" }]));
|
|
198
|
+
expect(await client.getJob(ws, mw, "route-1")).toBeUndefined();
|
|
199
|
+
});
|
|
200
|
+
it("runJob posts input + trigger to /run and returns the execution id", async () => {
|
|
201
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse({ jobExecutionId: "je-1" }));
|
|
202
|
+
const res = await client.runJob(ws, mw, route, { a: 1 }, "test");
|
|
203
|
+
expect(res.jobExecutionId).toBe("je-1");
|
|
204
|
+
expect(lastUrl()).toBe(`${routesBase}/route-1/run`);
|
|
205
|
+
expect(lastBody()).toEqual({ input: { a: 1 }, trigger: "test" });
|
|
206
|
+
});
|
|
207
|
+
it("runJob defaults the trigger to mcp", async () => {
|
|
208
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse({ jobExecutionId: "je-2" }));
|
|
209
|
+
await client.runJob(ws, mw, route, {});
|
|
210
|
+
expect(lastBody().trigger).toBe("mcp");
|
|
211
|
+
});
|
|
212
|
+
it("addTestCase posts the case to /test-cases", async () => {
|
|
213
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse({ id: "tc-1" }));
|
|
214
|
+
await client.addTestCase(ws, mw, route, {
|
|
215
|
+
name: "happy path",
|
|
216
|
+
input: { x: 1 },
|
|
217
|
+
assertions: [{ name: "ok", expr: "output != null" }],
|
|
218
|
+
});
|
|
219
|
+
expect(lastUrl()).toBe(`${routesBase}/route-1/test-cases`);
|
|
220
|
+
expect(lastInit().method).toBe("POST");
|
|
221
|
+
expect(lastBody().name).toBe("happy path");
|
|
222
|
+
});
|
|
223
|
+
it("startTestRun sends testCaseIds when provided and {} otherwise", async () => {
|
|
224
|
+
vi.mocked(globalThis.fetch).mockImplementation(async () => jsonResponse({ testRunId: "tr-1" }));
|
|
225
|
+
await client.startTestRun(ws, mw, route, ["tc1", "tc2"]);
|
|
226
|
+
expect(lastUrl()).toBe(`${routesBase}/route-1/test-runs`);
|
|
227
|
+
expect(lastBody()).toEqual({ testCaseIds: ["tc1", "tc2"] });
|
|
228
|
+
await client.startTestRun(ws, mw, route);
|
|
229
|
+
expect(lastBody()).toEqual({});
|
|
230
|
+
});
|
|
231
|
+
it("throws on a non-ok response with the method, path and body", async () => {
|
|
232
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(new Response("nope", { status: 403 }));
|
|
233
|
+
await expect(client.getExecution(ws, mw, route, "e")).rejects.toThrow(/Middleware service GET .*failed \(403\): nope/);
|
|
234
|
+
});
|
|
235
|
+
describe("pollExecution", () => {
|
|
236
|
+
it("polls until a terminal state and returns it", async () => {
|
|
237
|
+
vi.mocked(globalThis.fetch)
|
|
238
|
+
.mockResolvedValueOnce(jsonResponse({ id: "e", status: "queued" }))
|
|
239
|
+
.mockResolvedValueOnce(jsonResponse({ id: "e", status: "running" }))
|
|
240
|
+
.mockResolvedValueOnce(jsonResponse({ id: "e", status: "succeeded" }));
|
|
241
|
+
const exec = await client.pollExecution(ws, mw, route, "e", {
|
|
242
|
+
intervalMs: 1,
|
|
243
|
+
timeoutMs: 1000,
|
|
244
|
+
});
|
|
245
|
+
expect(exec.status).toBe("succeeded");
|
|
246
|
+
expect(globalThis.fetch).toHaveBeenCalledTimes(3);
|
|
247
|
+
});
|
|
248
|
+
it("treats partial/failed as terminal", async () => {
|
|
249
|
+
vi.mocked(globalThis.fetch).mockResolvedValue(jsonResponse({ id: "e", status: "partial" }));
|
|
250
|
+
const exec = await client.pollExecution(ws, mw, route, "e", {
|
|
251
|
+
intervalMs: 1,
|
|
252
|
+
timeoutMs: 1000,
|
|
253
|
+
});
|
|
254
|
+
expect(exec.status).toBe("partial");
|
|
255
|
+
expect(globalThis.fetch).toHaveBeenCalledTimes(1);
|
|
256
|
+
});
|
|
257
|
+
it("throws on timeout while still running", async () => {
|
|
258
|
+
vi.mocked(globalThis.fetch).mockImplementation(async () => jsonResponse({ id: "e", status: "running" }));
|
|
259
|
+
await expect(client.pollExecution(ws, mw, route, "e", {
|
|
260
|
+
intervalMs: 1,
|
|
261
|
+
timeoutMs: 15,
|
|
262
|
+
})).rejects.toThrow(/Timed out polling job execution e .*last status: running/);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
describe("pollTestRun", () => {
|
|
266
|
+
it("polls until the run leaves running and returns the report", async () => {
|
|
267
|
+
vi.mocked(globalThis.fetch)
|
|
268
|
+
.mockResolvedValueOnce(jsonResponse({ testRun: { id: "tr", status: "running" }, results: [] }))
|
|
269
|
+
.mockResolvedValueOnce(jsonResponse({
|
|
270
|
+
testRun: {
|
|
271
|
+
id: "tr",
|
|
272
|
+
status: "passed",
|
|
273
|
+
totals: { total: 1, passed: 1, failed: 0, errored: 0 },
|
|
274
|
+
},
|
|
275
|
+
results: [{ id: "r1", testRunId: "tr", testCaseId: "tc", passed: true }],
|
|
276
|
+
}));
|
|
277
|
+
const report = await client.pollTestRun(ws, mw, route, "tr", {
|
|
278
|
+
intervalMs: 1,
|
|
279
|
+
timeoutMs: 1000,
|
|
280
|
+
});
|
|
281
|
+
expect(report.testRun.status).toBe("passed");
|
|
282
|
+
expect(report.results).toHaveLength(1);
|
|
283
|
+
});
|
|
284
|
+
it("throws on timeout while still running", async () => {
|
|
285
|
+
vi.mocked(globalThis.fetch).mockImplementation(async () => jsonResponse({ testRun: { id: "tr", status: "running" }, results: [] }));
|
|
286
|
+
await expect(client.pollTestRun(ws, mw, route, "tr", {
|
|
287
|
+
intervalMs: 1,
|
|
288
|
+
timeoutMs: 15,
|
|
289
|
+
})).rejects.toThrow(/Timed out polling test run tr/);
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
//# sourceMappingURL=middleware-service-client.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware-service-client.test.js","sourceRoot":"","sources":["../../src/__tests__/middleware-service-client.test.ts"],"names":[],"mappings":"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"}
|
package/dist/codemode.js
CHANGED
|
@@ -694,19 +694,20 @@ export class CodeModeToolRegistry {
|
|
|
694
694
|
}
|
|
695
695
|
}
|
|
696
696
|
export function shouldUseCodeMode() {
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
697
|
+
// Code mode is experimental and OFF by default. Opt in with
|
|
698
|
+
// EXPERIMENTAL_USE_CODE_MODE=1 (legacy aliases kept for back-compat).
|
|
699
|
+
const flag = process.env.EXPERIMENTAL_USE_CODE_MODE ??
|
|
700
|
+
process.env.MINICOR_MCP_CODE_MODE ??
|
|
701
|
+
"";
|
|
702
|
+
return ["1", "true", "on", "yes"].includes(flag.toLowerCase());
|
|
702
703
|
}
|
|
703
704
|
export function buildCodeModeInstructions(baseInstructions) {
|
|
704
705
|
return `${baseInstructions}
|
|
705
706
|
|
|
706
|
-
CODE MODE:
|
|
707
|
-
This MCP
|
|
707
|
+
CODE MODE (EXPERIMENTAL):
|
|
708
|
+
This MCP is running with the experimental code-mode surface enabled. Use the \`codemode\` tool to run JavaScript that calls Minicor methods as \`await minicor.tool_name({ ... })\`.
|
|
708
709
|
|
|
709
|
-
If the server starts unauthenticated and only exposes \`setup_account\`, call \`setup_account\` directly, authenticate, and restart the MCP server.
|
|
710
|
+
If the server starts unauthenticated and only exposes \`setup_account\`, call \`setup_account\` directly, authenticate, and restart the MCP server.
|
|
710
711
|
|
|
711
712
|
Discovery inside code mode:
|
|
712
713
|
- \`await codemode.search("workflow")\` finds relevant Minicor methods.
|
|
@@ -715,7 +716,7 @@ Discovery inside code mode:
|
|
|
715
716
|
|
|
716
717
|
If the current directory contains a minicor.json file, your first MCP action should be a \`codemode\` call whose code invokes \`await minicor.session_start({ directory: "<current directory>" })\`. Before ending work, invoke \`await minicor.session_end(...)\` through code mode.
|
|
717
718
|
|
|
718
|
-
|
|
719
|
+
Code mode is opt-in: unset EXPERIMENTAL_USE_CODE_MODE to return to the default one-tool-per-operation MCP surface.`;
|
|
719
720
|
}
|
|
720
721
|
export function asMcpServer(registry) {
|
|
721
722
|
return registry;
|