@minicor/mcp-server 3.9.0 → 4.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 +145 -100
- package/dist/__tests__/discovery.test.d.ts +2 -0
- package/dist/__tests__/discovery.test.d.ts.map +1 -0
- package/dist/__tests__/{builder-discovery.test.js → discovery.test.js} +3 -3
- package/dist/__tests__/discovery.test.js.map +1 -0
- package/dist/__tests__/http-seal.test.d.ts +2 -0
- package/dist/__tests__/http-seal.test.d.ts.map +1 -0
- package/dist/__tests__/http-seal.test.js +40 -0
- package/dist/__tests__/http-seal.test.js.map +1 -0
- package/dist/__tests__/http-server.test.d.ts +7 -0
- package/dist/__tests__/http-server.test.d.ts.map +1 -0
- package/dist/__tests__/http-server.test.js +317 -0
- package/dist/__tests__/http-server.test.js.map +1 -0
- package/dist/__tests__/jobs-tools.test.d.ts +2 -0
- package/dist/__tests__/jobs-tools.test.d.ts.map +1 -0
- package/dist/__tests__/jobs-tools.test.js +336 -0
- package/dist/__tests__/jobs-tools.test.js.map +1 -0
- package/dist/__tests__/middleware-service-client.test.js +70 -0
- package/dist/__tests__/middleware-service-client.test.js.map +1 -1
- package/dist/__tests__/server-surface.test.d.ts +2 -0
- package/dist/__tests__/server-surface.test.d.ts.map +1 -0
- package/dist/__tests__/{server-profile.test.js → server-surface.test.js} +109 -91
- package/dist/__tests__/server-surface.test.js.map +1 -0
- package/dist/bootstrap.js +29 -0
- package/dist/bootstrap.js.map +1 -1
- package/dist/http-oauth.d.ts +60 -0
- package/dist/http-oauth.d.ts.map +1 -0
- package/dist/http-oauth.js +380 -0
- package/dist/http-oauth.js.map +1 -0
- package/dist/http-seal.d.ts +17 -0
- package/dist/http-seal.d.ts.map +1 -0
- package/dist/http-seal.js +63 -0
- package/dist/http-seal.js.map +1 -0
- package/dist/http-server.d.ts +29 -0
- package/dist/http-server.d.ts.map +1 -0
- package/dist/http-server.js +229 -0
- package/dist/http-server.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -15
- package/dist/index.js.map +1 -1
- package/dist/lib.d.ts +6 -4
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +10 -10
- package/dist/lib.js.map +1 -1
- package/dist/middleware-service-client.d.ts +37 -2
- package/dist/middleware-service-client.d.ts.map +1 -1
- package/dist/middleware-service-client.js +43 -2
- package/dist/middleware-service-client.js.map +1 -1
- package/dist/prompts/build-rpa.d.ts.map +1 -1
- package/dist/prompts/build-rpa.js +2 -0
- package/dist/prompts/build-rpa.js.map +1 -1
- package/dist/server-surface.d.ts +23 -0
- package/dist/server-surface.d.ts.map +1 -0
- package/dist/server-surface.js +110 -0
- package/dist/server-surface.js.map +1 -0
- package/dist/setup.d.ts +1 -1
- package/dist/setup.js +3 -9
- package/dist/setup.js.map +1 -1
- package/dist/sync.js +33 -6
- package/dist/sync.js.map +1 -1
- package/dist/tools/core.js +1 -1
- package/dist/tools/core.js.map +1 -1
- package/dist/tools/{builder-discovery.d.ts → discovery.d.ts} +3 -3
- package/dist/tools/discovery.d.ts.map +1 -0
- package/dist/tools/{builder-discovery.js → discovery.js} +3 -3
- package/dist/tools/discovery.js.map +1 -0
- package/dist/tools/jobs.d.ts.map +1 -1
- package/dist/tools/jobs.js +296 -3
- package/dist/tools/jobs.js.map +1 -1
- package/dist/tools/vm.js +1 -1
- package/dist/tools/vm.js.map +1 -1
- package/package.json +3 -2
- package/skills/general/job-build-loop.md +163 -0
- package/dist/__tests__/builder-discovery.test.d.ts +0 -2
- package/dist/__tests__/builder-discovery.test.d.ts.map +0 -1
- package/dist/__tests__/builder-discovery.test.js.map +0 -1
- package/dist/__tests__/server-profile.test.d.ts +0 -2
- package/dist/__tests__/server-profile.test.d.ts.map +0 -1
- package/dist/__tests__/server-profile.test.js.map +0 -1
- package/dist/builder-index.d.ts +0 -3
- package/dist/builder-index.d.ts.map +0 -1
- package/dist/builder-index.js +0 -5
- package/dist/builder-index.js.map +0 -1
- package/dist/server-profile.d.ts +0 -11
- package/dist/server-profile.d.ts.map +0 -1
- package/dist/server-profile.js +0 -104
- package/dist/server-profile.js.map +0 -1
- package/dist/tools/builder-discovery.d.ts.map +0 -1
- package/dist/tools/builder-discovery.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Minicor MCP Server
|
|
2
2
|
|
|
3
|
-
Build, deploy, and debug VM-based browser and desktop automations from **Cursor** or **Claude Code**. The AI agent connects to your Windows VM, writes Python automation scripts, tests them through the Minicor executor, and deploys production-ready workflows
|
|
3
|
+
Build, deploy, and debug VM-based browser and desktop automations from **Cursor** or **Claude Code**. The AI agent connects to your Windows VM, writes Python automation scripts, tests them through the Minicor executor, and deploys production-ready workflows - with skills accumulated from every build.
|
|
4
4
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
7
7
|
### 1. Install
|
|
8
8
|
|
|
9
|
-
**Cursor**
|
|
9
|
+
**Cursor** - add to `~/.cursor/mcp.json`:
|
|
10
10
|
|
|
11
11
|
```json
|
|
12
12
|
{
|
|
@@ -19,7 +19,7 @@ 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
25
|
claude mcp add minicor -- npx -y @minicor/mcp-server
|
|
@@ -39,46 +39,49 @@ npx -y -p @minicor/mcp-server minicor-mcp-setup --cli
|
|
|
39
39
|
|
|
40
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.
|
|
41
41
|
|
|
42
|
-
###
|
|
42
|
+
### Hosted MCP (cloud, no install)
|
|
43
43
|
|
|
44
|
-
The
|
|
44
|
+
The same server also runs hosted at `https://mcp.minicor.com/mcp` (Streamable HTTP). Sign-in is your Minicor email/password through an OAuth flow - no API keys, no local tokens. The tool set matches the local server except for the tools that read and write your local disk (workspace clones, sessions, sync); those require the local install above.
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
**Claude (web / Desktop / mobile):**
|
|
47
|
+
|
|
48
|
+
1. Go to **Settings > Connectors > Add custom connector**.
|
|
49
|
+
2. Paste `https://mcp.minicor.com/mcp` and save. OAuth is auto-discovered - no client ID or secret.
|
|
50
|
+
3. Claude opens the Minicor sign-in page. Enter your email, password, and region (US/Canada). Done - the connector acts as your account.
|
|
51
|
+
|
|
52
|
+
**Claude Code:**
|
|
47
53
|
|
|
48
54
|
```bash
|
|
49
|
-
|
|
55
|
+
claude mcp add --transport http minicor-cloud https://mcp.minicor.com/mcp
|
|
50
56
|
```
|
|
51
57
|
|
|
52
|
-
|
|
58
|
+
Then run `/mcp` inside Claude Code to trigger the OAuth sign-in.
|
|
59
|
+
|
|
60
|
+
**Cursor** - add a URL-based entry to `~/.cursor/mcp.json` (Cursor prompts for the OAuth sign-in on first use):
|
|
53
61
|
|
|
54
62
|
```json
|
|
55
63
|
{
|
|
56
64
|
"mcpServers": {
|
|
57
|
-
"minicor-
|
|
58
|
-
"
|
|
59
|
-
"args": ["-y", "-p", "@minicor/mcp-server", "minicor-builder-mcp"]
|
|
65
|
+
"minicor-cloud": {
|
|
66
|
+
"url": "https://mcp.minicor.com/mcp"
|
|
60
67
|
}
|
|
61
68
|
}
|
|
62
69
|
}
|
|
63
70
|
```
|
|
64
71
|
|
|
65
|
-
|
|
72
|
+
Access tokens expire within an hour and refresh automatically for up to 30 days; after that you sign in again. To self-host the same endpoint (or restrict sign-in to your email domain), see [docs/HOSTED.md](docs/HOSTED.md).
|
|
66
73
|
|
|
67
|
-
|
|
68
|
-
claude mcp add minicor-builder -- npx -y -p @minicor/mcp-server minicor-builder-mcp
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
Use the builder server while editing, testing, and publishing drafts, then switch back to `minicor` for the full tool surface. The builder uses standard MCP tools by default; code mode remains opt-in through `EXPERIMENTAL_USE_CODE_MODE=1`.
|
|
74
|
+
> Older installs may have a separate `minicor-builder` entry in their MCP config. That server was folded into `minicor`; re-running `minicor-mcp-setup` removes the stale entry.
|
|
72
75
|
|
|
73
76
|
### 3. Bootstrap Workspace (optional)
|
|
74
77
|
|
|
75
78
|
After cloning a workspace, generate harness-specific config files:
|
|
76
79
|
|
|
77
80
|
```bash
|
|
78
|
-
# For Claude Code
|
|
81
|
+
# For Claude Code - creates CLAUDE.md + .claude/settings.local.json
|
|
79
82
|
npx -y -p @minicor/mcp-server minicor-bootstrap claude-code --dir ./my-workspace
|
|
80
83
|
|
|
81
|
-
# For Cursor
|
|
84
|
+
# For Cursor - creates .cursor/rules/workspace.mdc
|
|
82
85
|
npx -y -p @minicor/mcp-server minicor-bootstrap cursor --dir ./my-workspace
|
|
83
86
|
|
|
84
87
|
# All harnesses at once
|
|
@@ -93,13 +96,12 @@ npx -y -p @minicor/mcp-server minicor-bootstrap all --dir ./my-workspace
|
|
|
93
96
|
|
|
94
97
|
Remove the MCP server entry from your editor first:
|
|
95
98
|
|
|
96
|
-
**Cursor**
|
|
99
|
+
**Cursor** - edit `~/.cursor/mcp.json` and remove `mcpServers.minicor`, plus any legacy `mcpServers.laminar` or `mcpServers.minicor-builder` entry.
|
|
97
100
|
|
|
98
|
-
**Claude Code**
|
|
101
|
+
**Claude Code** - remove the registered server:
|
|
99
102
|
|
|
100
103
|
```bash
|
|
101
104
|
claude mcp remove minicor
|
|
102
|
-
claude mcp remove minicor-builder
|
|
103
105
|
```
|
|
104
106
|
|
|
105
107
|
If the command is unavailable, remove the `minicor` MCP server entry from your Claude Code MCP config.
|
|
@@ -199,7 +201,7 @@ Sessions are synced to `[Workspace:Session]` issues on the platform, so agents o
|
|
|
199
201
|
|
|
200
202
|
## The RPA Lifecycle
|
|
201
203
|
|
|
202
|
-
This is the complete flow from zero to production automation. The agent handles each phase using MCP tools and skills.
|
|
204
|
+
This is the complete flow from zero to production automation. The agent handles each phase using MCP tools and skills. Workflows are built and tested inside a **Job** (see [Jobs](#jobs-rpa-orchestration) below): the job's test cases are written before the workflow exists and are the definition of done for the whole build.
|
|
203
205
|
|
|
204
206
|
### Phase 1: Workspace Setup
|
|
205
207
|
|
|
@@ -207,11 +209,11 @@ This is the complete flow from zero to production automation. The agent handles
|
|
|
207
209
|
"Create a workspace for Acme Corp and set up a VM"
|
|
208
210
|
```
|
|
209
211
|
|
|
210
|
-
- `create_workspace` / `list_workspaces` / `get_workspace`
|
|
211
|
-
- `vm_list`
|
|
212
|
-
- `deploy_vm`
|
|
213
|
-
- `install_mds_on_vm` / `start_mds_on_vm`
|
|
214
|
-
- `vm_connect`
|
|
212
|
+
- `create_workspace` / `list_workspaces` / `get_workspace` - create or find a workspace
|
|
213
|
+
- `vm_list` - discover available VMs
|
|
214
|
+
- `deploy_vm` - provision a new Windows VM if needed
|
|
215
|
+
- `install_mds_on_vm` / `start_mds_on_vm` - install the Minicor Desktop Service
|
|
216
|
+
- `vm_connect` - connect to the VM via Cloudflare tunnel
|
|
215
217
|
|
|
216
218
|
### Phase 2: Clone Workspace
|
|
217
219
|
|
|
@@ -219,18 +221,18 @@ This is the complete flow from zero to production automation. The agent handles
|
|
|
219
221
|
"Clone workspace 257 to my local folder"
|
|
220
222
|
```
|
|
221
223
|
|
|
222
|
-
- `clone_workspace`
|
|
223
|
-
- `minicor.json`
|
|
224
|
-
- `workflows/`
|
|
225
|
-
- `.minicor/skills/general/`
|
|
226
|
-
- `.minicor/skills/workspace/`
|
|
227
|
-
- `.minicor/issues/`
|
|
228
|
-
- `.minicor/skill-index.json`
|
|
229
|
-
- `.minicor/workspace.json`
|
|
224
|
+
- `clone_workspace` - pulls everything to a local folder:
|
|
225
|
+
- `minicor.json` - workspace manifest
|
|
226
|
+
- `workflows/` - all workflow steps as files (git-native)
|
|
227
|
+
- `.minicor/skills/general/` - bundled RPA skills
|
|
228
|
+
- `.minicor/skills/workspace/` - workspace-specific learned skills
|
|
229
|
+
- `.minicor/issues/` - agent-persisted context from previous runs
|
|
230
|
+
- `.minicor/skill-index.json` - compact skill summary
|
|
231
|
+
- `.minicor/workspace.json` - workspace metadata
|
|
230
232
|
|
|
231
233
|
The folder is git-ready. Push to GitHub for version control, diffs, and CI/CD.
|
|
232
234
|
|
|
233
|
-
### Phase 3: Scope &
|
|
235
|
+
### Phase 3: Scope & Define the Green-Gate
|
|
234
236
|
|
|
235
237
|
```
|
|
236
238
|
"I want to automate patient lookup in Centricity"
|
|
@@ -239,37 +241,45 @@ The folder is git-ready. Push to GitHub for version control, diffs, and CI/CD.
|
|
|
239
241
|
The agent:
|
|
240
242
|
|
|
241
243
|
- Takes screenshots, inspects UI elements, identifies the app framework
|
|
242
|
-
- Loads relevant skills: `get_skill("cdp-browser-automation")` or `get_skill("desktop-uiautomation")`
|
|
244
|
+
- Loads relevant skills: `get_skill("job-build-loop")`, then `get_skill("cdp-browser-automation")` or `get_skill("desktop-uiautomation")`
|
|
243
245
|
- Checks `list_skills()` for customer-specific skills (e.g., "centricity-quirks")
|
|
244
246
|
- Studies existing workflows via `get_workflow_overview`
|
|
245
247
|
- Determines strategy: CDP browser, desktop uiautomation, or hybrid
|
|
248
|
+
- **Writes the test cases before building.** `register_job` (a thin job whose workflow step will call the new workflow) plus `teach_job` or `add_test_case` with a real input and assertions. These cases start red; the rest of the lifecycle exists to turn them green.
|
|
246
249
|
|
|
247
250
|
### Phase 4: Build (Iterative)
|
|
248
251
|
|
|
249
252
|
For each automation step:
|
|
250
253
|
|
|
251
|
-
1. **Observe**
|
|
252
|
-
2. **Write**
|
|
253
|
-
3. **Prototype**
|
|
254
|
-
4. **Save**
|
|
255
|
-
5. **Test through Minicor**
|
|
254
|
+
1. **Observe** - `vm_screenshot` + `vm_inspect_ui` to map the UI
|
|
255
|
+
2. **Write** - Python script using patterns from loaded skills
|
|
256
|
+
3. **Prototype** - `vm_execute_script` to test on the VM directly
|
|
257
|
+
4. **Save** - `create_rpa_flow` to persist as a Minicor workflow step
|
|
258
|
+
5. **Test through Minicor** - `execute_workflow_async` with `start_from_step`/`end_at_step` to verify the step works through the real executor (config variables, data passing, JS wrapper)
|
|
256
259
|
|
|
257
|
-
The testing step is critical
|
|
260
|
+
The testing step is critical - `vm_execute_script` doesn't resolve `{{config.*}}` variables or `data.input` interpolation. The agent loads `get_skill("rpa-testing-workflow")` for exact tool call sequences.
|
|
258
261
|
|
|
259
|
-
### Phase 5: Test End-to-End
|
|
262
|
+
### Phase 5: Test End-to-End, Then Green-Gate the Job
|
|
260
263
|
|
|
261
264
|
After all steps pass individually:
|
|
262
265
|
|
|
263
|
-
- `execute_workflow_async` with real inputs + `configurationId`
|
|
266
|
+
- `execute_workflow_async` with real inputs + `configurationId` - full workflow, no step isolation
|
|
264
267
|
- Poll `get_execution_status` + `vm_screenshot` to monitor
|
|
265
268
|
- `diagnose_execution` on failure, fix with `update_flow`, re-run
|
|
266
|
-
-
|
|
269
|
+
- The workflow is not done until it passes end-to-end through the Minicor executor
|
|
270
|
+
|
|
271
|
+
Then close the loop at the job level:
|
|
272
|
+
|
|
273
|
+
- Point the job's workflow step at the finished `workflowId` (`update_job variant=draft`). If the workflow was edited in Dev Mode, `dev_mode_publish` first - jobs execute published workflows, not drafts.
|
|
274
|
+
- `run_tests variant=draft` - the test cases from Phase 3 are the pass/fail verdict for the build
|
|
275
|
+
- On a red case, `resolve_job_state` / `inspect_job_execution` locate the failing step; replay just that slice with `run_job` (`fromStepId` + `seedExecutionId`) or drop back into the workflow, fix, re-run
|
|
276
|
+
- When the suite is green, `publish_job` puts it live at `/m/:slug/:path`
|
|
267
277
|
|
|
268
278
|
### Phase 6: Production Hardening
|
|
269
279
|
|
|
270
280
|
For production workflows (not POCs):
|
|
271
281
|
|
|
272
|
-
- Load `get_skill("state-verification")`
|
|
282
|
+
- Load `get_skill("state-verification")` - add `expectedPreState`/`expectedPostState` to each step
|
|
273
283
|
- Run the workflow twice to verify idempotency
|
|
274
284
|
- `create_agent` with `mode: "monitor"` + `watchWorkflowId` for failure monitoring
|
|
275
285
|
- `create_agent` with `mode: "scheduled"` + cron for recurring runs
|
|
@@ -279,50 +289,77 @@ For production workflows (not POCs):
|
|
|
279
289
|
|
|
280
290
|
After a successful build:
|
|
281
291
|
|
|
282
|
-
- `generate_skill`
|
|
292
|
+
- `generate_skill` - gathers workflow code, execution history, and agent issues
|
|
283
293
|
- The agent analyzes patterns and calls `save_skill` to persist them
|
|
284
294
|
- Skills are stored per-workspace and available to future builds
|
|
285
295
|
- Customer-specific quirks become reusable knowledge
|
|
286
296
|
|
|
287
297
|
## Jobs (RPA orchestration)
|
|
288
298
|
|
|
289
|
-
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
|
|
299
|
+
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.
|
|
290
300
|
|
|
291
301
|
### The job tools
|
|
292
302
|
|
|
293
303
|
| Tool | What it does |
|
|
294
304
|
| --- | --- |
|
|
295
|
-
| `
|
|
305
|
+
| `resolve_job_state` | The first call to make when picking up a job. Returns the job's state (draft vs published, test cases, latest test run, recent executions, active build) plus `nextActions` - the concrete next tool calls. A red test run is drilled automatically: failing case → failing step → its `workflowId` (a `dev_mode_load` hint) → a seeded `fromStepId` replay command. |
|
|
306
|
+
| `list_middlewares` | List the routers (middlewares) in a workspace - jobs attach to one of these. |
|
|
296
307
|
| `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. |
|
|
297
|
-
| `register_job` | Create a job
|
|
308
|
+
| `register_job` | Create a job - a route backed by a structured `definition` (step graph). |
|
|
298
309
|
| `update_job` | Update a job's `definition`, path, method, or description. `variant: "draft"` writes the draft definition (the teach/build working copy); `variant: "published"` (default) edits the live definition and snapshots a new route version. |
|
|
299
310
|
| `get_job` | Get a job (route) incl. its `definition` (and `draftDefinition` when one exists); omit `routeId` to list all routes on the router. |
|
|
300
|
-
| `run_job` | Run a job once with an input, poll to completion, return the full `JobExecution` (per-step Minicor deep-links). `variant: "draft"` runs the draft definition
|
|
311
|
+
| `run_job` | Run a job once with an input, poll to completion, return the full `JobExecution` (per-step Minicor deep-links). `variant: "draft"` runs the draft definition. For step-range debugging, `fromStepId` / `toStepId` (both inclusive) run a slice of the step graph, and `seedExecutionId` hydrates `ctx` from a prior execution so mid-graph starts still resolve earlier steps' values. `waitForResult: false` returns the `jobExecutionId` immediately for runs you want to stop or monitor. |
|
|
312
|
+
| `stop_job_execution` | Stop a queued/running execution. This is a cooperative cancel (there is no pause): a queued run cancels immediately; a running one stops at the runner's next checkpoint (between steps, between `forEach` items, or while waiting on a workflow). A workflow already running on the VM is not killed - it finishes on its own; the job just stops waiting. Idempotent for already-cancelled runs. |
|
|
313
|
+
| `list_job_executions` | List a job's executions (paginated summaries, newest first). Filter by `status` (`queued`/`running`/`succeeded`/`partial`/`failed`/`cancelled`) and/or `trigger` (`test` = suite runs; `api,mcp,cron` = live traffic). Use to find a prior run to seed a range run from, a running execution to stop, or the latest failure to inspect. |
|
|
314
|
+
| `get_job_execution` | Fetch one past execution's full trace: status, input, output, `ctx`, per-step results. |
|
|
315
|
+
| `inspect_job_execution` | Deep-drill an execution end to end: job → steps → (workflow steps) the underlying workflow execution's internal flow-runs, with RPA failure analysis on failures. |
|
|
301
316
|
| `teach_job` | Teach a job by example: an `input` + a natural-language `prompt` (+ optional `expect` output or refusal, and `artifacts` like Loom links or SOP text). Upserts an idempotent judge-graded test case, folds it into the job's active build run, and, when a draft definition exists, runs the draft now and returns its output + judge verdict. |
|
|
302
317
|
| `publish_job` | Promote a job's draft definition to published: snapshots a route version, copies draft to published, stamps `publishedAt`. Live traffic serves the new definition immediately. Run `run_tests` with `variant: "draft"` first. |
|
|
303
318
|
| `get_build_status` | Get a job's build runs (the teach loop's cycles): status `queued`/`running`/`green`/`failed`/`needs_input`, folded-in test cases, builder summary, open questions. Pass `buildId` for one run; omit to list recent runs. |
|
|
304
319
|
| `answer_build_question` | Answer a `needs_input` question on a build run (credentials choice, ambiguity, 2FA policy). When no open questions remain the build re-queues automatically. |
|
|
320
|
+
| `cancel_build` | Stop a queued/running autonomous build run. The build flips to `failed` with a "stopped by user" summary; the job's draft definition is left as-is. Use it to take the loop over manually. |
|
|
305
321
|
| `update_build_run` | Builder-facing: update a build run's lifecycle (`running` when you start, `green`/`failed` with a summary when done, `needs_input` with questions when blocked). |
|
|
306
322
|
| `add_test_case` | Attach a test case to a job: named `input` + assertions (the green-gate). Assertions are `expr` (sandbox boolean) or `judge` (an LLM grades the execution against a natural-language `intent`). |
|
|
323
|
+
| `update_test_case` | Update an existing test case: tighten assertions as behavior firms up, or set `enabled: false` to disable a case without deleting its history. |
|
|
307
324
|
| `run_tests` | Run a job's test suite (or a subset), poll to completion, return the report (`green`, totals, per-case). `variant: "draft"` runs against the draft definition. |
|
|
308
325
|
| `get_test_report` | Fetch a test run report by id (totals + per-case results, each linking to its job execution). |
|
|
309
|
-
| `list_workspace_api_keys` | List a workspace's API keys (id, apiKey, name)
|
|
326
|
+
| `list_workspace_api_keys` | List a workspace's API keys (id, apiKey, name) - to find a key for the two tools below. |
|
|
310
327
|
| `set_middleware_api_key` | Set a router's `workspaceApiKey` after the fact, then re-read to confirm it stuck. |
|
|
311
328
|
|
|
312
|
-
### The
|
|
329
|
+
### The build loop - test-case-first, macroscope → microscope
|
|
313
330
|
|
|
314
|
-
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.
|
|
331
|
+
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. This mirrors the platform's Jobs UI (Overview = macroscope, Build = microscope). The full playbook ships as the `job-build-loop` skill - load it with `get_skill("job-build-loop")`.
|
|
315
332
|
|
|
316
333
|
```
|
|
317
|
-
1.
|
|
318
|
-
|
|
319
|
-
2. BUILD
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
4.
|
|
334
|
+
1. GREEN-GATE FIRST - teach_job (input + intent) or add_test_case (input + assertions).
|
|
335
|
+
A job with no test cases has no definition of correct. Cases start RED.
|
|
336
|
+
2. BUILD THE DRAFT - register_job / update_job variant=draft. Job steps call PUBLISHED
|
|
337
|
+
Minicor workflows (by workflowId) or run code blocks.
|
|
338
|
+
3. RUN THE GATE - run_tests variant=draft. Green -> publish_job. Red -> step 4.
|
|
339
|
+
4. LOCATE (macro) - resolve_job_state tells you where you are and what to do next;
|
|
340
|
+
get_test_report + inspect_job_execution drill a failing case down to
|
|
341
|
+
the failing job step and its workflow's internal flow-runs.
|
|
342
|
+
5. FIX AT THE RIGHT LEVEL:
|
|
343
|
+
5a. Job-level - update_job variant=draft, then replay JUST the failing slice:
|
|
344
|
+
run_job variant=draft fromStepId=<failing step>
|
|
345
|
+
seedExecutionId=<failed execution> (seeds ctx so mid-graph starts
|
|
346
|
+
resolve earlier steps' values).
|
|
347
|
+
5b. Workflow-level (microscope) - the failing step's workflowId -> dev_mode_load,
|
|
348
|
+
fix/run the draft's flow steps (dev_mode_run needs vmId),
|
|
349
|
+
dev_mode_publish - jobs execute PUBLISHED workflows.
|
|
350
|
+
6. PUBLISH - when run_tests variant=draft is green, publish_job promotes the draft;
|
|
351
|
+
runtime traffic at /m/:slug serves it immediately.
|
|
323
352
|
```
|
|
324
353
|
|
|
325
|
-
A job step supports `saveAs`, `when` (conditional skip), `forEach` (fan-out), `onError` (`abort`/`skip`/`continue`/`{goto}`
|
|
354
|
+
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.
|
|
355
|
+
|
|
356
|
+
### Execution control - stop, ranges, and traffic lenses
|
|
357
|
+
|
|
358
|
+
- **Stopping:** there is no pause/resume. `stop_job_execution` is a cooperative cancel: a queued run cancels immediately; a running one stops at the runner's next checkpoint (between steps, between `forEach` items, or while waiting on a workflow). A workflow already running on a VM is not killed - it finishes on its own; the job just stops waiting and starts no new steps.
|
|
359
|
+
- **Long or risky runs:** `run_job` with `waitForResult: false` returns the `jobExecutionId` immediately - poll it with `get_job_execution`, stop it with `stop_job_execution`.
|
|
360
|
+
- **Step-range replays:** `run_job` with `fromStepId` / `toStepId` runs only a slice of the step graph (both inclusive, by step id). For any mid-graph start, pass `seedExecutionId` - it hydrates `ctx` from that prior execution so steps you aren't re-running still have their `ctx.*` values. The job's `output` block only runs when the range covers the last step.
|
|
361
|
+
- **Finding runs:** `list_job_executions` returns paginated summaries with `status` and `trigger` filters - `trigger: "test"` isolates suite runs, `trigger: "api,mcp,cron"` isolates live traffic (the same split as the platform's Development/Production lenses).
|
|
362
|
+
- **Autonomous builds:** watch with `get_build_status`, unblock with `answer_build_question`, take over with `cancel_build`. Don't edit the draft while a build is running - coordinate or cancel first.
|
|
326
363
|
|
|
327
364
|
### Teach-by-example: draft vs published
|
|
328
365
|
|
|
@@ -351,7 +388,7 @@ The `definition` you pass to `register_job` (see `register_job`'s `definition` p
|
|
|
351
388
|
|
|
352
389
|
```ts
|
|
353
390
|
{
|
|
354
|
-
region: "us" | "ca", // region awareness
|
|
391
|
+
region: "us" | "ca", // region awareness - also auto-detected from workspace
|
|
355
392
|
resource?: { configStoreId?, lockKey? }, // configStoreId = which VM/desktop; lockKey serializes runs
|
|
356
393
|
inputSchema: JSONSchema,
|
|
357
394
|
outputSchema: JSONSchema,
|
|
@@ -363,7 +400,7 @@ The `definition` you pass to `register_job` (see `register_job`'s `definition` p
|
|
|
363
400
|
}
|
|
364
401
|
```
|
|
365
402
|
|
|
366
|
-
**`ctx` is the data through-line** between steps
|
|
403
|
+
**`ctx` is the data through-line** between steps - each step's `saveAs` key is what the next step reads:
|
|
367
404
|
|
|
368
405
|
- `ctx` starts as `{ input: <the job input> }`.
|
|
369
406
|
- Each step's result is stored at `ctx.<saveAs>` (when `saveAs` is set), so later steps / `when` / `forEach` / `output` can read it.
|
|
@@ -374,7 +411,7 @@ Assertions (`add_test_case`) are either `expr` (TS booleans over `{ input, outpu
|
|
|
374
411
|
|
|
375
412
|
### Region & API key
|
|
376
413
|
|
|
377
|
-
Jobs are **region-aware** (`us` / `ca`)
|
|
414
|
+
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.
|
|
378
415
|
|
|
379
416
|
## Skills System
|
|
380
417
|
|
|
@@ -383,6 +420,7 @@ Skills are reusable RPA patterns that accumulate as you build automations. The M
|
|
|
383
420
|
|
|
384
421
|
| Skill | Purpose |
|
|
385
422
|
| ---------------------------- | --------------------------------------------------------------------------------- |
|
|
423
|
+
| `job-build-loop` | **Mandatory for job work.** The test-case-first build/debug loop: green-gate first, macroscope (job) → microscope (workflow steps), seeded range replays, stop semantics, publish gates |
|
|
386
424
|
| `rpa-testing-workflow` | **Mandatory.** Exact tool call sequences for testing through the Minicor executor |
|
|
387
425
|
| `cdp-browser-automation` | CDP starter template, React-safe setters, parallel execution |
|
|
388
426
|
| `desktop-uiautomation` | Framework selection, element selectors, wait/retry patterns |
|
|
@@ -407,18 +445,18 @@ Skills are automatically persisted through the Minicor platform API. When authen
|
|
|
407
445
|
|
|
408
446
|
Skill tiers:
|
|
409
447
|
|
|
410
|
-
- **Global**
|
|
411
|
-
- **Per-workspace**
|
|
412
|
-
- **Customer-specific**
|
|
448
|
+
- **Global** - general RPA knowledge (read-only, bundled with the MCP)
|
|
449
|
+
- **Per-workspace** - learned from agent sessions, persisted across runs
|
|
450
|
+
- **Customer-specific** - app quirks, environment patterns
|
|
413
451
|
|
|
414
452
|
Without authentication, skills fall back to the bundled `.md` files in the package.
|
|
415
453
|
|
|
416
454
|
## What You Can Automate
|
|
417
455
|
|
|
418
|
-
- **Web portals**
|
|
419
|
-
- **Desktop applications**
|
|
420
|
-
- **APIs**
|
|
421
|
-
- **Hybrid workflows**
|
|
456
|
+
- **Web portals** - Chrome via CDP. React/Angular SPAs, anti-bot sites, payor portals, EHR web apps.
|
|
457
|
+
- **Desktop applications** - Windows apps via uiautomation, pywinauto, pyautogui. EMR clients, billing software, legacy systems.
|
|
458
|
+
- **APIs** - Direct HTTP when the agent discovers usable endpoints behind a portal.
|
|
459
|
+
- **Hybrid workflows** - Mix browser, desktop, and API steps in one workflow.
|
|
422
460
|
|
|
423
461
|
## VM Setup
|
|
424
462
|
|
|
@@ -430,7 +468,7 @@ The easiest path is to let the agent handle everything:
|
|
|
430
468
|
|
|
431
469
|
The agent runs: `deploy_vm` → `install_mds_on_vm` → `start_mds_on_vm` → `vm_connect`
|
|
432
470
|
|
|
433
|
-
Once the VM is provisioned, a secure tunnel is automatically created. The agent connects via the tunnel URL and is ready to build automations. Chrome instances for browser automation are managed automatically by the MDS
|
|
471
|
+
Once the VM is provisioned, a secure tunnel is automatically created. The agent connects via the tunnel URL and is ready to build automations. Chrome instances for browser automation are managed automatically by the MDS - no manual setup needed.
|
|
434
472
|
|
|
435
473
|
For manual setup, use `get_lds_setup_guide` for step-by-step instructions.
|
|
436
474
|
|
|
@@ -487,25 +525,32 @@ For manual setup, use `get_lds_setup_guide` for step-by-step instructions.
|
|
|
487
525
|
|
|
488
526
|
| Tool | What it does |
|
|
489
527
|
| --------------------- | -------------------------------------------------------------------------------------------- |
|
|
490
|
-
| `
|
|
528
|
+
| `resolve_job_state` | "Where am I in the build loop?" - job state + `nextActions` (the concrete next tool calls) |
|
|
529
|
+
| `list_middlewares` | List the routers (middlewares) in a workspace - jobs attach to one of these |
|
|
491
530
|
| `register_middleware` | Create a router (middleware) jobs attach to; `autoConfigure` populates its workspace API key |
|
|
492
|
-
| `register_job` | Create a job
|
|
531
|
+
| `register_job` | Create a job - a middleware route backed by a structured `definition` (step graph) |
|
|
493
532
|
| `update_job` | Update a job's `definition`, path, method, or description; `variant: "draft"` writes the draft |
|
|
494
533
|
| `get_job` | Get a job (route) incl. its `definition`; omit `routeId` to list all routes on the router |
|
|
495
|
-
| `run_job` | Run a job once
|
|
534
|
+
| `run_job` | Run a job once, poll to completion; `variant: "draft"` runs the draft. Step-range replays via `fromStepId`/`toStepId` + `seedExecutionId`; `waitForResult: false` returns the execution id immediately |
|
|
535
|
+
| `stop_job_execution` | Cooperative cancel of a queued/running execution (no pause; VM workflows finish on their own) |
|
|
536
|
+
| `list_job_executions` | Paginated execution summaries with `status`/`trigger` filters (test vs live traffic) |
|
|
537
|
+
| `get_job_execution` | Fetch one execution's full trace (status, input, output, `ctx`, per-step results) |
|
|
538
|
+
| `inspect_job_execution` | Deep-drill: job → steps → workflow execution's internal flow-runs, with RPA failure analysis |
|
|
496
539
|
| `teach_job` | Teach by example: upsert a judge-graded test case from input + prompt; runs the draft if one exists |
|
|
497
540
|
| `publish_job` | Promote a job's draft definition to published (snapshots a version); live traffic serves it |
|
|
498
541
|
| `get_build_status` | Get a job's build runs: status, folded-in test cases, builder summary, open questions |
|
|
499
542
|
| `answer_build_question` | Answer a `needs_input` question on a build run; the build re-queues when none remain |
|
|
543
|
+
| `cancel_build` | Stop a queued/running autonomous build run (flips to `failed`; the draft is left as-is) |
|
|
500
544
|
| `update_build_run` | Builder-facing: set a build run's status (`running`/`green`/`failed`/`needs_input`) + summary |
|
|
501
545
|
| `add_test_case` | Attach a test case to a job: named `input` + `expr` or `judge` assertions (the green-gate) |
|
|
546
|
+
| `update_test_case` | Update a test case (tighten assertions, or `enabled: false` to disable without deleting) |
|
|
502
547
|
| `run_tests` | Run a job's test suite, poll to completion, return the report; `variant: "draft"` tests the draft |
|
|
503
548
|
| `get_test_report` | Fetch a test run report by id (totals + per-case results, each linking to its job execution) |
|
|
504
|
-
| `list_workspace_api_keys` | List a workspace's API keys (id, apiKey, name)
|
|
549
|
+
| `list_workspace_api_keys` | List a workspace's API keys (id, apiKey, name) - find one for the two tools below |
|
|
505
550
|
| `set_middleware_api_key` | Set a router's `workspaceApiKey` (then re-reads to confirm it stuck) |
|
|
506
551
|
|
|
507
552
|
|
|
508
|
-
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
|
|
553
|
+
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`.
|
|
509
554
|
|
|
510
555
|
### 2FA / OTP
|
|
511
556
|
|
|
@@ -541,7 +586,7 @@ Workflows that encounter 2FA prompts use these tools to auto-resolve OTP challen
|
|
|
541
586
|
| `update_config_property` / `remove_config_property` | Manage individual properties |
|
|
542
587
|
|
|
543
588
|
|
|
544
|
-
Scripts reference credentials as `{{config.propertyKey}}`
|
|
589
|
+
Scripts reference credentials as `{{config.propertyKey}}` - resolved at runtime by the workflow engine.
|
|
545
590
|
|
|
546
591
|
### Issues
|
|
547
592
|
|
|
@@ -557,9 +602,9 @@ Scripts reference credentials as `{{config.propertyKey}}` — resolved at runtim
|
|
|
557
602
|
|
|
558
603
|
Agents use Issues to persist context across runs with the `[Agent:<name>]` title convention:
|
|
559
604
|
|
|
560
|
-
- `[Agent:my-bot] Rules`
|
|
561
|
-
- `[Agent:my-bot] Context`
|
|
562
|
-
- `[Agent:my-bot] Session 2026-04-09`
|
|
605
|
+
- `[Agent:my-bot] Rules` - learned behaviors (e.g., "login page takes 15s to load")
|
|
606
|
+
- `[Agent:my-bot] Context` - persistent state (URLs, credential rotation dates)
|
|
607
|
+
- `[Agent:my-bot] Session 2026-04-09` - run summaries
|
|
563
608
|
|
|
564
609
|
### Agents
|
|
565
610
|
|
|
@@ -622,9 +667,9 @@ Agents are autonomous runners that execute tasks on VMs using MCP tools. After b
|
|
|
622
667
|
|
|
623
668
|
The `build-rpa-workflow` prompt accepts a `mode` parameter:
|
|
624
669
|
|
|
625
|
-
- `**poc**`
|
|
626
|
-
- `**production**`
|
|
627
|
-
- **Omitted**
|
|
670
|
+
- `**poc**` - Quick prototype. Streamlined testing, no state verification or monitoring.
|
|
671
|
+
- `**production**` - Full hardening. State verification, idempotency testing, monitoring agent, context persistence.
|
|
672
|
+
- **Omitted** - The agent asks the user which mode to use.
|
|
628
673
|
|
|
629
674
|
## Environment Variables
|
|
630
675
|
|
|
@@ -664,23 +709,23 @@ npm test
|
|
|
664
709
|
|
|
665
710
|
```
|
|
666
711
|
src/
|
|
667
|
-
index.ts
|
|
668
|
-
lib.ts
|
|
669
|
-
bootstrap.ts
|
|
670
|
-
config.ts
|
|
671
|
-
state.ts
|
|
672
|
-
skills.ts
|
|
712
|
+
index.ts - CLI MCP entry (stdio transport, token management, disconnect hooks)
|
|
713
|
+
lib.ts - Embeddable server factory (in-process, no side effects)
|
|
714
|
+
bootstrap.ts - CLI to generate harness configs (CLAUDE.md, .cursor/rules, etc.)
|
|
715
|
+
config.ts - User config (~/.minicor/config.json) - workspacesRoot, etc.
|
|
716
|
+
state.ts - Shared mutable state (VM connections, active session tracking)
|
|
717
|
+
skills.ts - Skill loader, parser, index builder
|
|
673
718
|
tools/
|
|
674
|
-
session.ts
|
|
675
|
-
skills.ts
|
|
676
|
-
sync-tools.ts
|
|
677
|
-
vm.ts, vm-rpa.ts
|
|
678
|
-
core.ts
|
|
719
|
+
session.ts - session_start, session_checkpoint, session_end, auto-checkpoint
|
|
720
|
+
skills.ts - list_skills, get_skill, save_skill, generate_skill
|
|
721
|
+
sync-tools.ts - clone_workspace, init_project, pull/push, set_workspaces_root
|
|
722
|
+
vm.ts, vm-rpa.ts - VM and RPA tools
|
|
723
|
+
core.ts - Workflows, flows, executions
|
|
679
724
|
...
|
|
680
725
|
prompts/
|
|
681
|
-
workspace-session.ts
|
|
682
|
-
build-rpa.ts
|
|
726
|
+
workspace-session.ts - Workspace session guide prompt
|
|
727
|
+
build-rpa.ts - Guided RPA building (POC/production modes)
|
|
683
728
|
...
|
|
684
729
|
skills/
|
|
685
|
-
general/
|
|
730
|
+
general/ - Bundled skill files (shipped with npm package)
|
|
686
731
|
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/discovery.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
2
|
import { clearWorkspaceRegionMap, setWorkspaceRegion, } from "../state.js";
|
|
3
|
-
import { registerVmDiscovery, registerWorkflowDiscovery, } from "../tools/
|
|
3
|
+
import { registerVmDiscovery, registerWorkflowDiscovery, } from "../tools/discovery.js";
|
|
4
4
|
import * as vmService from "../vm-service-client.js";
|
|
5
5
|
vi.mock("../vm-service-client.js", () => ({
|
|
6
6
|
listVms: vi.fn(),
|
|
@@ -45,7 +45,7 @@ beforeEach(() => {
|
|
|
45
45
|
clearWorkspaceRegionMap();
|
|
46
46
|
vi.clearAllMocks();
|
|
47
47
|
});
|
|
48
|
-
describe("
|
|
48
|
+
describe("discovery tools", () => {
|
|
49
49
|
it("registers only the five lightweight discovery operations", () => {
|
|
50
50
|
const { deps, handlers } = setup();
|
|
51
51
|
registerWorkflowDiscovery(deps);
|
|
@@ -117,4 +117,4 @@ describe("builder discovery tools", () => {
|
|
|
117
117
|
});
|
|
118
118
|
});
|
|
119
119
|
});
|
|
120
|
-
//# sourceMappingURL=
|
|
120
|
+
//# sourceMappingURL=discovery.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.test.js","sourceRoot":"","sources":["../../src/__tests__/discovery.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-seal.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/http-seal.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { TokenSealer } from "../http-seal.js";
|
|
3
|
+
const SECRET = "test-secret-that-is-long-enough-for-aes-256";
|
|
4
|
+
describe("TokenSealer", () => {
|
|
5
|
+
it("rejects short secrets", () => {
|
|
6
|
+
expect(() => new TokenSealer("short")).toThrow(/MCP_TOKEN_SECRET/);
|
|
7
|
+
});
|
|
8
|
+
it("round-trips a payload", () => {
|
|
9
|
+
const sealer = new TokenSealer(SECRET);
|
|
10
|
+
const blob = sealer.seal("access", { hello: "world" }, 60_000);
|
|
11
|
+
expect(sealer.unseal("access", blob)).toEqual({
|
|
12
|
+
hello: "world",
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
it("rejects the wrong type", () => {
|
|
16
|
+
const sealer = new TokenSealer(SECRET);
|
|
17
|
+
const blob = sealer.seal("code", { a: 1 }, 60_000);
|
|
18
|
+
expect(sealer.unseal("access", blob)).toBeNull();
|
|
19
|
+
});
|
|
20
|
+
it("rejects expired blobs", () => {
|
|
21
|
+
const sealer = new TokenSealer(SECRET);
|
|
22
|
+
const blob = sealer.seal("access", { a: 1 }, -1);
|
|
23
|
+
expect(sealer.unseal("access", blob)).toBeNull();
|
|
24
|
+
});
|
|
25
|
+
it("rejects tampered blobs and garbage", () => {
|
|
26
|
+
const sealer = new TokenSealer(SECRET);
|
|
27
|
+
const blob = sealer.seal("access", { a: 1 }, 60_000);
|
|
28
|
+
const tampered = blob.slice(0, -2) + (blob.endsWith("AA") ? "BB" : "AA");
|
|
29
|
+
expect(sealer.unseal("access", tampered)).toBeNull();
|
|
30
|
+
expect(sealer.unseal("access", "not-a-token")).toBeNull();
|
|
31
|
+
expect(sealer.unseal("access", "")).toBeNull();
|
|
32
|
+
});
|
|
33
|
+
it("rejects blobs sealed with a different secret", () => {
|
|
34
|
+
const a = new TokenSealer(SECRET);
|
|
35
|
+
const b = new TokenSealer(SECRET + "-other");
|
|
36
|
+
const blob = a.seal("access", { a: 1 }, 60_000);
|
|
37
|
+
expect(b.unseal("access", blob)).toBeNull();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=http-seal.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-seal.test.js","sourceRoot":"","sources":["../../src/__tests__/http-seal.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,MAAM,MAAM,GAAG,6CAA6C,CAAC;AAE7D,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,CAAC;QAC/D,MAAM,CAAC,MAAM,CAAC,MAAM,CAAoB,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;YAC/D,KAAK,EAAE,OAAO;SACf,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACnD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACrD,MAAM,QAAQ,GACZ,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QACrD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC1D,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,CAAC,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,IAAI,WAAW,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAChD,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration test for the hosted HTTP server: walks the full OAuth flow
|
|
3
|
+
* (discovery, registration, login form, PKCE code exchange, refresh) and
|
|
4
|
+
* then talks MCP over Streamable HTTP against a mocked Minicor API.
|
|
5
|
+
*/
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=http-server.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-server.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/http-server.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|