@minicor/mcp-server 3.9.1 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -100
- package/dist/__tests__/dev-mode-draft.test.js +27 -19
- package/dist/__tests__/dev-mode-draft.test.js.map +1 -1
- 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 +337 -0
- package/dist/__tests__/jobs-tools.test.js.map +1 -0
- package/dist/__tests__/laminar-client.test.js +31 -31
- package/dist/__tests__/laminar-client.test.js.map +1 -1
- package/dist/__tests__/lib-dev-mode.test.js +2 -2
- package/dist/__tests__/lib-dev-mode.test.js.map +1 -1
- 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} +120 -100
- 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/laminar-client.d.ts +11 -14
- package/dist/laminar-client.d.ts.map +1 -1
- package/dist/laminar-client.js +25 -25
- package/dist/laminar-client.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 +113 -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 +34 -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/dev-mode-draft.d.ts.map +1 -1
- package/dist/tools/dev-mode-draft.js +63 -49
- package/dist/tools/dev-mode-draft.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 +166 -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
|
@@ -3,8 +3,8 @@ import { clearWorkspaceRegionMap, setWorkspaceRegion } from "../state.js";
|
|
|
3
3
|
import { register } from "../tools/dev-mode-draft.js";
|
|
4
4
|
function createClient() {
|
|
5
5
|
return {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
createDevModeBranch: vi.fn().mockResolvedValue({ id: 7 }),
|
|
7
|
+
getDevModeBranch: vi.fn().mockResolvedValue({ id: 7 }),
|
|
8
8
|
listDevModeSteps: vi.fn().mockResolvedValue([]),
|
|
9
9
|
getDevModeStep: vi.fn().mockResolvedValue({ key: "f1" }),
|
|
10
10
|
editDevModeStep: vi.fn().mockResolvedValue({ step: { key: "f1" } }),
|
|
@@ -21,8 +21,8 @@ function createClient() {
|
|
|
21
21
|
stopRequested: false,
|
|
22
22
|
}),
|
|
23
23
|
stopDevModeRun: vi.fn().mockResolvedValue({ stopRequested: true }),
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
deleteDevModeBranch: vi.fn().mockResolvedValue(undefined),
|
|
25
|
+
publishDevModeBranch: vi.fn().mockResolvedValue({ id: 7 }),
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
function setup() {
|
|
@@ -78,7 +78,7 @@ describe("Dev Mode draft tools", () => {
|
|
|
78
78
|
expect(handlers.has("dev_mode_current_draft")).toBe(false);
|
|
79
79
|
expect(handlers.has("dev_mode_run_external")).toBe(false);
|
|
80
80
|
});
|
|
81
|
-
it("
|
|
81
|
+
it("branches a workflow through the client selected for the workspace", async () => {
|
|
82
82
|
setWorkspaceRegion(11, "ca");
|
|
83
83
|
const { client, clientForRegion, handlers } = setup();
|
|
84
84
|
const response = await call(handlers, "dev_mode_load", {
|
|
@@ -87,14 +87,13 @@ describe("Dev Mode draft tools", () => {
|
|
|
87
87
|
});
|
|
88
88
|
expect(jsonResult(response)).toEqual({ id: 7 });
|
|
89
89
|
expect(clientForRegion).toHaveBeenCalledWith("ca");
|
|
90
|
-
expect(client.
|
|
91
|
-
workflowId: 42,
|
|
92
|
-
});
|
|
90
|
+
expect(client.createDevModeBranch).toHaveBeenCalledWith(11, 42);
|
|
93
91
|
});
|
|
94
92
|
it("uses the canonical program field and defaults add folder to false", async () => {
|
|
95
93
|
const { client, handlers } = setup();
|
|
96
94
|
await call(handlers, "dev_mode_edit_step", {
|
|
97
95
|
workspaceId: 11,
|
|
96
|
+
workflowId: 42,
|
|
98
97
|
key: "f1",
|
|
99
98
|
program: "(data) => data",
|
|
100
99
|
flowType: "MANAGE_CONFIG",
|
|
@@ -102,15 +101,16 @@ describe("Dev Mode draft tools", () => {
|
|
|
102
101
|
});
|
|
103
102
|
await call(handlers, "dev_mode_add_step", {
|
|
104
103
|
workspaceId: 11,
|
|
104
|
+
workflowId: 42,
|
|
105
105
|
name: "New step",
|
|
106
106
|
flowType: "SHELL_SCRIPT",
|
|
107
107
|
});
|
|
108
|
-
expect(client.editDevModeStep).toHaveBeenCalledWith(11, "f1", {
|
|
108
|
+
expect(client.editDevModeStep).toHaveBeenCalledWith(11, 42, "f1", {
|
|
109
109
|
program: "(data) => data",
|
|
110
110
|
flowType: "MANAGE_CONFIG",
|
|
111
111
|
language: "js",
|
|
112
112
|
});
|
|
113
|
-
expect(client.addDevModeStep).toHaveBeenCalledWith(11, {
|
|
113
|
+
expect(client.addDevModeStep).toHaveBeenCalledWith(11, 42, {
|
|
114
114
|
name: "New step",
|
|
115
115
|
flowType: "SHELL_SCRIPT",
|
|
116
116
|
folder: false,
|
|
@@ -125,10 +125,12 @@ describe("Dev Mode draft tools", () => {
|
|
|
125
125
|
]);
|
|
126
126
|
await call(handlers, "dev_mode_regroup", {
|
|
127
127
|
workspaceId: 11,
|
|
128
|
+
workflowId: 42,
|
|
128
129
|
key: "f3",
|
|
129
130
|
group: 4,
|
|
130
131
|
});
|
|
131
|
-
expect(client.
|
|
132
|
+
expect(client.listDevModeSteps).toHaveBeenCalledWith(11, 42);
|
|
133
|
+
expect(client.editDevModeStep).toHaveBeenCalledWith(11, 42, "f3", {
|
|
132
134
|
name: "Verify 4 - Check customer",
|
|
133
135
|
});
|
|
134
136
|
expect(client.reorderDevModeSteps).not.toHaveBeenCalled();
|
|
@@ -140,23 +142,25 @@ describe("Dev Mode draft tools", () => {
|
|
|
140
142
|
]);
|
|
141
143
|
await call(handlers, "dev_mode_regroup", {
|
|
142
144
|
workspaceId: 11,
|
|
145
|
+
workflowId: 42,
|
|
143
146
|
key: "f1",
|
|
144
147
|
group: null,
|
|
145
148
|
});
|
|
146
|
-
expect(client.editDevModeStep).toHaveBeenCalledWith(11, "f1", {
|
|
149
|
+
expect(client.editDevModeStep).toHaveBeenCalledWith(11, 42, "f1", {
|
|
147
150
|
name: "Check result",
|
|
148
151
|
});
|
|
149
152
|
});
|
|
150
|
-
it("always sends vmId and an input object when running a
|
|
153
|
+
it("always sends vmId and an input object when running a branch", async () => {
|
|
151
154
|
const { client, handlers } = setup();
|
|
152
155
|
await call(handlers, "dev_mode_run", {
|
|
153
156
|
workspaceId: 11,
|
|
157
|
+
workflowId: 42,
|
|
154
158
|
vmId: 99,
|
|
155
159
|
startStep: 2,
|
|
156
160
|
endStep: 4,
|
|
157
161
|
configurationExternalId: "test-config",
|
|
158
162
|
});
|
|
159
|
-
expect(client.runDevModeDraft).toHaveBeenCalledWith(11, {
|
|
163
|
+
expect(client.runDevModeDraft).toHaveBeenCalledWith(11, 42, {
|
|
160
164
|
startStep: 2,
|
|
161
165
|
endStep: 4,
|
|
162
166
|
input: {},
|
|
@@ -168,6 +172,7 @@ describe("Dev Mode draft tools", () => {
|
|
|
168
172
|
const { client, handlers } = setup();
|
|
169
173
|
const response = await call(handlers, "dev_mode_run", {
|
|
170
174
|
workspaceId: 11,
|
|
175
|
+
workflowId: 42,
|
|
171
176
|
vmId: 99,
|
|
172
177
|
startStep: 5,
|
|
173
178
|
endStep: 2,
|
|
@@ -186,6 +191,7 @@ describe("Dev Mode draft tools", () => {
|
|
|
186
191
|
});
|
|
187
192
|
const response = await call(handlers, "dev_mode_run", {
|
|
188
193
|
workspaceId: 11,
|
|
194
|
+
workflowId: 42,
|
|
189
195
|
vmId: 99,
|
|
190
196
|
input: {},
|
|
191
197
|
});
|
|
@@ -220,7 +226,7 @@ describe("Dev Mode draft tools", () => {
|
|
|
220
226
|
expect(client.stopDevModeRun).toHaveBeenCalledWith(11, 42);
|
|
221
227
|
expect(jsonResult(response)).toEqual({ stopRequested: true });
|
|
222
228
|
});
|
|
223
|
-
it("requires confirmation before permanently deleting a
|
|
229
|
+
it("requires confirmation before permanently deleting a branch", async () => {
|
|
224
230
|
const { client, handlers } = setup();
|
|
225
231
|
const declined = await call(handlers, "dev_mode_delete_draft", {
|
|
226
232
|
workspaceId: 11,
|
|
@@ -228,13 +234,13 @@ describe("Dev Mode draft tools", () => {
|
|
|
228
234
|
confirm: false,
|
|
229
235
|
});
|
|
230
236
|
expect(declined.content[0].text).toContain("not confirmed");
|
|
231
|
-
expect(client.
|
|
237
|
+
expect(client.deleteDevModeBranch).not.toHaveBeenCalled();
|
|
232
238
|
const deleted = await call(handlers, "dev_mode_delete_draft", {
|
|
233
239
|
workspaceId: 11,
|
|
234
240
|
workflowId: 42,
|
|
235
241
|
confirm: true,
|
|
236
242
|
});
|
|
237
|
-
expect(client.
|
|
243
|
+
expect(client.deleteDevModeBranch).toHaveBeenCalledWith(11, 42);
|
|
238
244
|
expect(jsonResult(deleted)).toEqual({
|
|
239
245
|
deleted: true,
|
|
240
246
|
workspaceId: 11,
|
|
@@ -245,16 +251,18 @@ describe("Dev Mode draft tools", () => {
|
|
|
245
251
|
const { client, handlers } = setup();
|
|
246
252
|
const declined = await call(handlers, "dev_mode_publish", {
|
|
247
253
|
workspaceId: 11,
|
|
254
|
+
workflowId: 42,
|
|
248
255
|
confirm: false,
|
|
249
256
|
});
|
|
250
257
|
expect(declined.content[0].text).toContain("not confirmed");
|
|
251
|
-
expect(client.
|
|
258
|
+
expect(client.publishDevModeBranch).not.toHaveBeenCalled();
|
|
252
259
|
const published = await call(handlers, "dev_mode_publish", {
|
|
253
260
|
workspaceId: 11,
|
|
261
|
+
workflowId: 42,
|
|
254
262
|
confirm: true,
|
|
255
263
|
});
|
|
256
264
|
expect(jsonResult(published)).toEqual({ id: 7 });
|
|
257
|
-
expect(client.
|
|
265
|
+
expect(client.publishDevModeBranch).toHaveBeenCalledWith(11, 42);
|
|
258
266
|
});
|
|
259
267
|
});
|
|
260
268
|
//# sourceMappingURL=dev-mode-draft.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-mode-draft.test.js","sourceRoot":"","sources":["../../src/__tests__/dev-mode-draft.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAG9D,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAE1E,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAItD,SAAS,YAAY;IACnB,OAAO;QACL,
|
|
1
|
+
{"version":3,"file":"dev-mode-draft.test.js","sourceRoot":"","sources":["../../src/__tests__/dev-mode-draft.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -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":"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"}
|
|
@@ -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"}
|
|
@@ -0,0 +1,317 @@
|
|
|
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
|
+
import crypto from "node:crypto";
|
|
7
|
+
import http from "node:http";
|
|
8
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
9
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
10
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
11
|
+
import { startHttpServer } from "../http-server.js";
|
|
12
|
+
const SECRET = "integration-test-secret-0123456789abcdef";
|
|
13
|
+
const EMAIL = "faiz@laminar.run";
|
|
14
|
+
const PASSWORD = "correct-horse-battery";
|
|
15
|
+
function readJson(req) {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const chunks = [];
|
|
18
|
+
req.on("data", (c) => chunks.push(c));
|
|
19
|
+
req.on("end", () => {
|
|
20
|
+
try {
|
|
21
|
+
resolve(JSON.parse(Buffer.concat(chunks).toString() || "{}"));
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
reject(e);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
describe("hosted HTTP server", () => {
|
|
30
|
+
let upstream;
|
|
31
|
+
let upstreamCalls = [];
|
|
32
|
+
let validAccessTokens = new Set(["up-access-1"]);
|
|
33
|
+
let server;
|
|
34
|
+
let base;
|
|
35
|
+
const savedApiBase = process.env.MINICOR_API_BASE_URL;
|
|
36
|
+
beforeAll(async () => {
|
|
37
|
+
upstream = http.createServer(async (req, res) => {
|
|
38
|
+
const body = req.method === "POST" ? await readJson(req).catch(() => ({})) : undefined;
|
|
39
|
+
upstreamCalls.push({
|
|
40
|
+
method: req.method ?? "",
|
|
41
|
+
path: req.url ?? "",
|
|
42
|
+
auth: req.headers.authorization,
|
|
43
|
+
body,
|
|
44
|
+
});
|
|
45
|
+
const send = (status, payload) => {
|
|
46
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
47
|
+
res.end(JSON.stringify(payload));
|
|
48
|
+
};
|
|
49
|
+
if (req.method === "POST" && req.url === "/auth/signin") {
|
|
50
|
+
if (body.username === EMAIL && body.password === PASSWORD) {
|
|
51
|
+
return send(200, {
|
|
52
|
+
access_token: "up-access-1",
|
|
53
|
+
refresh_token: "up-refresh-1",
|
|
54
|
+
expires_in: 3600,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return send(401, { message: "Invalid credentials" });
|
|
58
|
+
}
|
|
59
|
+
if (req.method === "POST" && req.url === "/auth/refresh") {
|
|
60
|
+
if (body.refreshToken === "up-refresh-1") {
|
|
61
|
+
validAccessTokens.add("up-access-2");
|
|
62
|
+
return send(200, {
|
|
63
|
+
access_token: "up-access-2",
|
|
64
|
+
refresh_token: "up-refresh-2",
|
|
65
|
+
expires_in: 3600,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return send(401, { message: "Invalid refresh token" });
|
|
69
|
+
}
|
|
70
|
+
if (req.method === "GET" && req.url === "/workspaces") {
|
|
71
|
+
const token = (req.headers.authorization ?? "").replace("Bearer ", "");
|
|
72
|
+
if (!validAccessTokens.has(token)) {
|
|
73
|
+
return send(401, { message: "Unauthorized" });
|
|
74
|
+
}
|
|
75
|
+
return send(200, [{ id: 42, name: "Test Workspace" }]);
|
|
76
|
+
}
|
|
77
|
+
send(404, { message: "not found" });
|
|
78
|
+
});
|
|
79
|
+
await new Promise((resolve) => upstream.listen(0, "127.0.0.1", resolve));
|
|
80
|
+
const upstreamPort = upstream.address().port;
|
|
81
|
+
process.env.MINICOR_API_BASE_URL = `http://127.0.0.1:${upstreamPort}`;
|
|
82
|
+
server = await startHttpServer({
|
|
83
|
+
port: 0,
|
|
84
|
+
host: "127.0.0.1",
|
|
85
|
+
tokenSecret: SECRET,
|
|
86
|
+
allowedEmailDomains: ["laminar.run"],
|
|
87
|
+
});
|
|
88
|
+
base = server.url;
|
|
89
|
+
});
|
|
90
|
+
afterAll(async () => {
|
|
91
|
+
await server?.close();
|
|
92
|
+
upstream?.close();
|
|
93
|
+
if (savedApiBase === undefined)
|
|
94
|
+
delete process.env.MINICOR_API_BASE_URL;
|
|
95
|
+
else
|
|
96
|
+
process.env.MINICOR_API_BASE_URL = savedApiBase;
|
|
97
|
+
});
|
|
98
|
+
it("serves OAuth discovery metadata", async () => {
|
|
99
|
+
const rs = await (await fetch(`${base}/.well-known/oauth-protected-resource`)).json();
|
|
100
|
+
expect(rs.resource).toBe(`${base}/mcp`);
|
|
101
|
+
expect(rs.authorization_servers).toEqual([base]);
|
|
102
|
+
const as = await (await fetch(`${base}/.well-known/oauth-authorization-server`)).json();
|
|
103
|
+
expect(as.issuer).toBe(base);
|
|
104
|
+
expect(as.authorization_endpoint).toBe(`${base}/authorize`);
|
|
105
|
+
expect(as.token_endpoint).toBe(`${base}/token`);
|
|
106
|
+
expect(as.registration_endpoint).toBe(`${base}/register`);
|
|
107
|
+
expect(as.code_challenge_methods_supported).toEqual(["S256"]);
|
|
108
|
+
});
|
|
109
|
+
it("rejects unauthenticated /mcp with a WWW-Authenticate challenge", async () => {
|
|
110
|
+
const res = await fetch(`${base}/mcp`, {
|
|
111
|
+
method: "POST",
|
|
112
|
+
headers: {
|
|
113
|
+
"Content-Type": "application/json",
|
|
114
|
+
Accept: "application/json, text/event-stream",
|
|
115
|
+
},
|
|
116
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "ping" }),
|
|
117
|
+
});
|
|
118
|
+
expect(res.status).toBe(401);
|
|
119
|
+
const challenge = res.headers.get("www-authenticate") ?? "";
|
|
120
|
+
expect(challenge).toContain("Bearer");
|
|
121
|
+
expect(challenge).toContain(`resource_metadata="${base}/.well-known/oauth-protected-resource"`);
|
|
122
|
+
});
|
|
123
|
+
it("rejects registration with non-https redirect URIs", async () => {
|
|
124
|
+
const res = await fetch(`${base}/register`, {
|
|
125
|
+
method: "POST",
|
|
126
|
+
headers: { "Content-Type": "application/json" },
|
|
127
|
+
body: JSON.stringify({ redirect_uris: ["http://evil.example.com/cb"] }),
|
|
128
|
+
});
|
|
129
|
+
expect(res.status).toBe(400);
|
|
130
|
+
});
|
|
131
|
+
describe("full OAuth + MCP flow", () => {
|
|
132
|
+
const redirectUri = "http://127.0.0.1:39999/callback";
|
|
133
|
+
const verifier = crypto.randomBytes(32).toString("base64url");
|
|
134
|
+
const challenge = crypto
|
|
135
|
+
.createHash("sha256")
|
|
136
|
+
.update(verifier)
|
|
137
|
+
.digest("base64url");
|
|
138
|
+
let clientId;
|
|
139
|
+
let code;
|
|
140
|
+
let accessToken;
|
|
141
|
+
let refreshToken;
|
|
142
|
+
it("registers a client", async () => {
|
|
143
|
+
const res = await fetch(`${base}/register`, {
|
|
144
|
+
method: "POST",
|
|
145
|
+
headers: { "Content-Type": "application/json" },
|
|
146
|
+
body: JSON.stringify({ redirect_uris: [redirectUri] }),
|
|
147
|
+
});
|
|
148
|
+
expect(res.status).toBe(201);
|
|
149
|
+
const body = await res.json();
|
|
150
|
+
clientId = body.client_id;
|
|
151
|
+
expect(clientId).toBeTruthy();
|
|
152
|
+
expect(body.token_endpoint_auth_method).toBe("none");
|
|
153
|
+
});
|
|
154
|
+
it("serves the login form on GET /authorize", async () => {
|
|
155
|
+
const url = new URL(`${base}/authorize`);
|
|
156
|
+
url.searchParams.set("client_id", clientId);
|
|
157
|
+
url.searchParams.set("redirect_uri", redirectUri);
|
|
158
|
+
url.searchParams.set("response_type", "code");
|
|
159
|
+
url.searchParams.set("state", "xyz-state");
|
|
160
|
+
url.searchParams.set("code_challenge", challenge);
|
|
161
|
+
url.searchParams.set("code_challenge_method", "S256");
|
|
162
|
+
const res = await fetch(url);
|
|
163
|
+
expect(res.status).toBe(200);
|
|
164
|
+
const html = await res.text();
|
|
165
|
+
expect(html).toContain("Sign in");
|
|
166
|
+
expect(html).toContain('name="password"');
|
|
167
|
+
expect(html).toContain("xyz-state");
|
|
168
|
+
// Anti-phishing: the form must identify where the auth code will go.
|
|
169
|
+
expect(html).toContain("<strong>127.0.0.1</strong>");
|
|
170
|
+
});
|
|
171
|
+
function loginForm(overrides = {}) {
|
|
172
|
+
return new URLSearchParams({
|
|
173
|
+
client_id: clientId,
|
|
174
|
+
redirect_uri: redirectUri,
|
|
175
|
+
state: "xyz-state",
|
|
176
|
+
code_challenge: challenge,
|
|
177
|
+
email: EMAIL,
|
|
178
|
+
password: PASSWORD,
|
|
179
|
+
region: "us",
|
|
180
|
+
...overrides,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
it("blocks disallowed email domains before hitting upstream", async () => {
|
|
184
|
+
const before = upstreamCalls.length;
|
|
185
|
+
const res = await fetch(`${base}/authorize`, {
|
|
186
|
+
method: "POST",
|
|
187
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
188
|
+
body: loginForm({ email: "someone@evil.com" }),
|
|
189
|
+
});
|
|
190
|
+
expect(res.status).toBe(200);
|
|
191
|
+
expect(await res.text()).toContain("not allowed");
|
|
192
|
+
expect(upstreamCalls.length).toBe(before);
|
|
193
|
+
});
|
|
194
|
+
it("re-renders the form on bad credentials", async () => {
|
|
195
|
+
const res = await fetch(`${base}/authorize`, {
|
|
196
|
+
method: "POST",
|
|
197
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
198
|
+
body: loginForm({ password: "wrong" }),
|
|
199
|
+
});
|
|
200
|
+
expect(res.status).toBe(200);
|
|
201
|
+
expect(await res.text()).toContain("Sign-in failed");
|
|
202
|
+
});
|
|
203
|
+
it("signs in and redirects back with an auth code", async () => {
|
|
204
|
+
const res = await fetch(`${base}/authorize`, {
|
|
205
|
+
method: "POST",
|
|
206
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
207
|
+
body: loginForm(),
|
|
208
|
+
redirect: "manual",
|
|
209
|
+
});
|
|
210
|
+
expect(res.status).toBe(302);
|
|
211
|
+
const location = new URL(res.headers.get("location") ?? "");
|
|
212
|
+
expect(location.origin + location.pathname).toBe(redirectUri);
|
|
213
|
+
expect(location.searchParams.get("state")).toBe("xyz-state");
|
|
214
|
+
code = location.searchParams.get("code") ?? "";
|
|
215
|
+
expect(code).toBeTruthy();
|
|
216
|
+
expect(upstreamCalls.some((c) => c.path === "/auth/signin" && c.body?.username === EMAIL)).toBe(true);
|
|
217
|
+
});
|
|
218
|
+
it("rejects the code exchange with a bad PKCE verifier", async () => {
|
|
219
|
+
const res = await fetch(`${base}/token`, {
|
|
220
|
+
method: "POST",
|
|
221
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
222
|
+
body: new URLSearchParams({
|
|
223
|
+
grant_type: "authorization_code",
|
|
224
|
+
code,
|
|
225
|
+
client_id: clientId,
|
|
226
|
+
redirect_uri: redirectUri,
|
|
227
|
+
code_verifier: "wrong-verifier-wrong-verifier-wrong-1234",
|
|
228
|
+
}),
|
|
229
|
+
});
|
|
230
|
+
expect(res.status).toBe(400);
|
|
231
|
+
expect((await res.json()).error).toBe("invalid_grant");
|
|
232
|
+
});
|
|
233
|
+
it("exchanges the code for tokens with the right verifier", async () => {
|
|
234
|
+
const res = await fetch(`${base}/token`, {
|
|
235
|
+
method: "POST",
|
|
236
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
237
|
+
body: new URLSearchParams({
|
|
238
|
+
grant_type: "authorization_code",
|
|
239
|
+
code,
|
|
240
|
+
client_id: clientId,
|
|
241
|
+
redirect_uri: redirectUri,
|
|
242
|
+
code_verifier: verifier,
|
|
243
|
+
}),
|
|
244
|
+
});
|
|
245
|
+
expect(res.status).toBe(200);
|
|
246
|
+
const body = await res.json();
|
|
247
|
+
expect(body.token_type).toBe("bearer");
|
|
248
|
+
expect(body.expires_in).toBeGreaterThan(0);
|
|
249
|
+
accessToken = body.access_token;
|
|
250
|
+
refreshToken = body.refresh_token;
|
|
251
|
+
expect(accessToken).toBeTruthy();
|
|
252
|
+
expect(refreshToken).toBeTruthy();
|
|
253
|
+
});
|
|
254
|
+
async function mcpClient(token) {
|
|
255
|
+
const client = new Client({ name: "test-client", version: "1.0.0" });
|
|
256
|
+
const transport = new StreamableHTTPClientTransport(new URL(`${base}/mcp`), { requestInit: { headers: { Authorization: `Bearer ${token}` } } });
|
|
257
|
+
await client.connect(transport);
|
|
258
|
+
return client;
|
|
259
|
+
}
|
|
260
|
+
it("serves MCP over streamable HTTP with the issued token", async () => {
|
|
261
|
+
const client = await mcpClient(accessToken);
|
|
262
|
+
const tools = await client.listTools();
|
|
263
|
+
const names = tools.tools.map((t) => t.name);
|
|
264
|
+
expect(names).toContain("list_workspaces");
|
|
265
|
+
const result = await client.callTool({
|
|
266
|
+
name: "list_workspaces",
|
|
267
|
+
arguments: {},
|
|
268
|
+
});
|
|
269
|
+
const text = result.content
|
|
270
|
+
.map((c) => c.text ?? "")
|
|
271
|
+
.join("\n");
|
|
272
|
+
expect(text).toContain("Test Workspace");
|
|
273
|
+
expect(upstreamCalls.some((c) => c.path === "/workspaces" && c.auth === "Bearer up-access-1")).toBe(true);
|
|
274
|
+
await client.close();
|
|
275
|
+
});
|
|
276
|
+
it("refreshes tokens via the refresh_token grant", async () => {
|
|
277
|
+
const res = await fetch(`${base}/token`, {
|
|
278
|
+
method: "POST",
|
|
279
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
280
|
+
body: new URLSearchParams({
|
|
281
|
+
grant_type: "refresh_token",
|
|
282
|
+
refresh_token: refreshToken,
|
|
283
|
+
client_id: clientId,
|
|
284
|
+
}),
|
|
285
|
+
});
|
|
286
|
+
expect(res.status).toBe(200);
|
|
287
|
+
const body = await res.json();
|
|
288
|
+
expect(body.access_token).toBeTruthy();
|
|
289
|
+
expect(upstreamCalls.some((c) => c.path === "/auth/refresh" && c.body?.refreshToken === "up-refresh-1")).toBe(true);
|
|
290
|
+
const client = await mcpClient(body.access_token);
|
|
291
|
+
const result = await client.callTool({
|
|
292
|
+
name: "list_workspaces",
|
|
293
|
+
arguments: {},
|
|
294
|
+
});
|
|
295
|
+
const text = result.content
|
|
296
|
+
.map((c) => c.text ?? "")
|
|
297
|
+
.join("\n");
|
|
298
|
+
expect(text).toContain("Test Workspace");
|
|
299
|
+
expect(upstreamCalls.some((c) => c.path === "/workspaces" && c.auth === "Bearer up-access-2")).toBe(true);
|
|
300
|
+
await client.close();
|
|
301
|
+
});
|
|
302
|
+
it("rejects a tampered access token", async () => {
|
|
303
|
+
const res = await fetch(`${base}/mcp`, {
|
|
304
|
+
method: "POST",
|
|
305
|
+
headers: {
|
|
306
|
+
"Content-Type": "application/json",
|
|
307
|
+
Accept: "application/json, text/event-stream",
|
|
308
|
+
Authorization: `Bearer ${accessToken.slice(0, -4)}AAAA`,
|
|
309
|
+
},
|
|
310
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "ping" }),
|
|
311
|
+
});
|
|
312
|
+
expect(res.status).toBe(401);
|
|
313
|
+
expect(res.headers.get("www-authenticate")).toContain("invalid_token");
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
//# sourceMappingURL=http-server.test.js.map
|