| @@ -107,10 +107,12 @@ type ExecutorMcpClient = { · 106 unmodified lines |
| 107 | close: () => Promise<void>; | 107 | close: () => Promise<void>; |
| 108 | }; | 108 | }; |
| 109 | | 109 | |
| | 110 | type ElicitationHandler = NonNullable<Parameters<Client["setRequestHandler"]>[1]>; |
| | 111 | |
| 110 | const makeExecutorMcpClient = (input: { | 112 | const makeExecutorMcpClient = (input: { |
| 111 | baseUrl: string; | 113 | baseUrl: string; |
| 112 | capabilities?: Record<string, unknown>; | 114 | capabilities?: Record<string, unknown>; |
| 113 | onElicitation?: (request: { params: { message?: string } }) => Promise<unknown>; | 115 | onElicitation?: ElicitationHandler; |
| 114 | }) => | 116 | }) => |
| 115 | Effect.acquireRelease( | 117 | Effect.acquireRelease( |
| 116 | Effect.promise<ExecutorMcpClient>(async () => { | 118 | Effect.promise<ExecutorMcpClient>(async () => { |
| @@ -898,6 +900,11 @@ describe("local-executor-server", () => { · 781 unmodified lines |
| 898 | | 900 | |
| 899 | it.scoped("updates an existing demo MCP source instead of creating a duplicate", () => | 901 | it.scoped("updates an existing demo MCP source instead of creating a duplicate", () => |
| 900 | Effect.gen(function* () { | 902 | Effect.gen(function* () { |
| | 903 | const demoServer = yield* Effect.acquireRelease( |
| | 904 | Effect.promise(() => startMcpElicitationDemoServer()), |
| | 905 | (server) => Effect.promise(() => server.close()).pipe(Effect.orDie), |
| | 906 | ); |
| | 907 | |
| 901 | const server = yield* createLocalExecutorServer({ | 908 | const server = yield* createLocalExecutorServer({ |
| 902 | port: 0, | 909 | port: 0, |
| 903 | localDataDir: ":memory:", | 910 | localDataDir: ":memory:", |
| @@ -933,7 +940,7 @@ describe("local-executor-server", () => { · 29 unmodified lines |
| 933 | const seeded = yield* seedDemoMcpSourceInWorkspace({ | 940 | const seeded = yield* seedDemoMcpSourceInWorkspace({ |
| 934 | client, | 941 | client, |
| 935 | workspaceId: installation.workspaceId, | 942 | workspaceId: installation.workspaceId, |
| 936 | endpoint: "http://127.0.0.1:58506/mcp", | 943 | endpoint: demoServer.endpoint, |
| 937 | name: "Demo", | 944 | name: "Demo", |
| 938 | namespace: "demo", | 945 | namespace: "demo", |
| 939 | }); | 946 | }); |
| @@ -948,7 +955,7 @@ describe("local-executor-server", () => { · 8 unmodified lines |
| 948 | }); | 955 | }); |
| 949 | | 956 | |
| 950 | expect(sources).toHaveLength(1); | 957 | expect(sources).toHaveLength(1); |
| 951 | expect(sources[0]?.endpoint).toBe("http://127.0.0.1:58506/mcp"); | 958 | expect(sources[0]?.endpoint).toBe(demoServer.endpoint); |
| 952 | }), | 959 | }), |
| 953 | ); | 960 | ); |
| 954 | | 961 | |
| @@ -1295,9 +1302,10 @@ describe("local-executor-server", () => { · 340 unmodified lines |
| 1295 | accountId: installation.accountId, | 1302 | accountId: installation.accountId, |
| 1296 | }); | 1303 | }); |
| 1297 | | 1304 | |
| | 1305 | const sourceId = SourceIdSchema.make("src_invalid_mcp_endpoint"); |
| 1298 | const now = Date.now(); | 1306 | const now = Date.now(); |
| 1299 | yield* server.runtime.persistence.rows.sources.insert({ | 1307 | yield* server.runtime.persistence.rows.sources.insert({ |
| 1300 | id: SourceIdSchema.make("src_invalid_mcp_endpoint"), | 1308 | id: sourceId, |
| 1301 | workspaceId: installation.workspaceId, | 1309 | workspaceId: installation.workspaceId, |
| 1302 | name: "Demo", | 1310 | name: "Demo", |
| 1303 | kind: "mcp", | 1311 | kind: "mcp", |
| @@ -1316,6 +1324,44 @@ describe("local-executor-server", () => { · 12 unmodified lines |
| 1316 | createdAt: now, | 1324 | createdAt: now, |
| 1317 | updatedAt: now, | 1325 | updatedAt: now, |
| 1318 | }); | 1326 | }); |
| | 1327 | yield* server.runtime.persistence.rows.toolArtifacts.replaceForSource({ |
| | 1328 | workspaceId: installation.workspaceId, |
| | 1329 | sourceId, |
| | 1330 | artifacts: [{ |
| | 1331 | artifact: { |
| | 1332 | workspaceId: installation.workspaceId, |
| | 1333 | path: "demo.gated_echo", |
| | 1334 | toolId: "gated_echo", |
| | 1335 | sourceId, |
| | 1336 | title: "gated_echo", |
| | 1337 | description: "Asks for approval before echoing a value", |
| | 1338 | searchNamespace: "demo.gated_echo", |
| | 1339 | searchText: "demo.gated_echo gated echo approval", |
| | 1340 | inputSchemaJson: JSON.stringify({ |
| | 1341 | type: "object", |
| | 1342 | properties: { |
| | 1343 | value: { |
| | 1344 | type: "string", |
| | 1345 | }, |
| | 1346 | }, |
| | 1347 | required: ["value"], |
| | 1348 | additionalProperties: false, |
| | 1349 | }), |
| | 1350 | outputSchemaJson: null, |
| | 1351 | providerKind: "mcp", |
| | 1352 | mcpToolName: "gated_echo", |
| | 1353 | openApiMethod: null, |
| | 1354 | openApiPathTemplate: null, |
| | 1355 | openApiOperationHash: null, |
| | 1356 | openApiRawToolId: null, |
| | 1357 | openApiOperationId: null, |
| | 1358 | openApiTagsJson: null, |
| | 1359 | openApiRequestBodyRequired: null, |
| | 1360 | createdAt: now, |
| | 1361 | updatedAt: now, |
| | 1362 | }, |
| | 1363 | }], |
| | 1364 | }); |
| 1319 | | 1365 | |
| 1320 | const failure = yield* client.executions | 1366 | const failure = yield* client.executions |
| 1321 | .create({ | 1367 | .create({ |