Skip to content

Commit 65bd2ff

Browse files
authored
Move control tools under executor namespace (#756)
1 parent d805711 commit 65bd2ff

24 files changed

Lines changed: 329 additions & 200 deletions

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Open `http://127.0.0.1:4788`, go to **Add Source**, paste a URL, and Executor wi
4848
### Via the CLI
4949

5050
```bash
51-
executor call openapi addSource '{
51+
executor call executor openapi addSource '{
5252
"spec": "https://petstore3.swagger.io/api/v3/openapi.json",
5353
"namespace": "petstore",
5454
"baseUrl": "https://petstore3.swagger.io/api/v3"

‎apps/cloud/src/mcp-miniflare.e2e.node.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
//
1515
// Elicitation coverage uses the openapi plugin: a tiny Effect HttpApi
1616
// upstream is stood up in-process, its generated spec is handed to
17-
// `tools.openapi.addSource`, and the cloud engine invokes a POST operation.
17+
// `tools.executor.openapi.addSource`, and the cloud engine invokes a POST operation.
1818
// `requiresApproval: true` fires the executor's approval elicitation, which
1919
// round-trips back to the SDK Client's `ElicitRequestSchema` handler.
2020
// ---------------------------------------------------------------------------
@@ -455,7 +455,7 @@ layer(TestEnv, { timeout: 60_000 })("cloud MCP over real HTTP (miniflare)", (it)
455455
method: "POST",
456456
headers: {
457457
accept: "application/json, text/event-stream",
458-
authorization: "Bearer bogus",
458+
authorization: "Bearer bogus.bogus.bogus",
459459
"content-type": "application/json",
460460
},
461461
body: JSON.stringify({
@@ -794,7 +794,7 @@ layer(TestEnv, { timeout: 60_000 })("cloud MCP over real HTTP (miniflare)", (it)
794794
// `HttpApiGroup` name ("approve") becomes part of the sandbox path,
795795
// so the invocation reads `tools.approveapi.approve.approveThing`.
796796
const code = [
797-
`await tools.openapi.addSource({ scope: ${JSON.stringify(orgId)}, spec: ${JSON.stringify(specJson)}, namespace: "approveapi" });`,
797+
`await tools.executor.openapi.addSource({ scope: ${JSON.stringify(orgId)}, spec: ${JSON.stringify(specJson)}, namespace: "approveapi" });`,
798798
`return await tools.approveapi.approve.approveThing({});`,
799799
].join("\n");
800800
const result = yield* Effect.promise(() =>

‎apps/cloud/src/mcp-session.e2e.node.test.ts‎

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
// before prod does.
1616

1717
import { describe, expect, it } from "@effect/vitest";
18-
import { Effect } from "effect";
18+
import { Effect, Schema } from "effect";
1919
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2020
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
2121
import { ElicitRequestSchema } from "@modelcontextprotocol/sdk/types.js";
@@ -44,11 +44,9 @@ import { DbService } from "./services/db";
4444
// eliciting test drive the real engine + sandbox rather than a stub engine.
4545
// ---------------------------------------------------------------------------
4646

47-
const EMPTY_INPUT_SCHEMA = {
48-
type: "object",
49-
properties: {},
50-
additionalProperties: false,
51-
} as const;
47+
const EMPTY_INPUT_SCHEMA = Schema.toStandardSchemaV1(
48+
Schema.toStandardJSONSchemaV1(Schema.Struct({})),
49+
);
5250

5351
const elicitingTestPlugin = definePlugin(() => ({
5452
id: "eliciting-test" as const,

‎apps/cloud/src/services/sources-api.node.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -676,13 +676,13 @@ describe("sources api (HTTP)", () => {
676676
it.effect("sources.remove on a static source is rejected", () =>
677677
Effect.gen(function* () {
678678
// `canRemove: false` is reserved for static (plugin-declared)
679-
// sources. The openapi plugin declares one with id "openapi"
680-
// for its control tools.
679+
// sources. Plugin-owned executor tools are mounted under the
680+
// built-in executor source.
681681
const org = `org_${crypto.randomUUID()}`;
682682

683683
const result = yield* asOrg(org, (client) =>
684684
client.sources
685-
.remove({ params: { scopeId: ScopeId.make(org), sourceId: "openapi" } })
685+
.remove({ params: { scopeId: ScopeId.make(org), sourceId: "executor" } })
686686
.pipe(Effect.result),
687687
);
688688
expect(Result.isFailure(result)).toBe(true);

‎apps/local/src/web/shell.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ function SourceList(props: { pathname: string; onNavigate?: () => void }) {
298298
: "text-sidebar-foreground hover:bg-sidebar-active/60 hover:text-foreground",
299299
].join(" ")}
300300
>
301-
<SourceFavicon url={s.url} />
301+
<SourceFavicon sourceId={s.id} url={s.url} />
302302
<span className="flex-1 truncate">{s.name}</span>
303303
<span className="rounded bg-secondary/50 px-1 py-px text-xs font-medium text-muted-foreground">
304304
{s.kind}

‎apps/local/vite.config.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { readFileSync } from "node:fs";
2+
import { resolve } from "node:path";
23
import { Readable } from "node:stream";
34
import { defineConfig, type Plugin } from "vite";
45
import react from "@vitejs/plugin-react";
@@ -38,6 +39,11 @@ function executorApiPlugin(): Plugin {
3839
return {
3940
name: "executor-api",
4041
configureServer(server) {
42+
server.watcher.on("change", (path) => {
43+
if (path.includes("/src/server/") || path.endsWith("/executor.config.ts")) {
44+
handlers = null;
45+
}
46+
});
4147
server.middlewares.use(async (req, res, next) => {
4248
const rawUrl = req.url ?? "/";
4349
const isApi = rawUrl.startsWith("/api/") || rawUrl === "/api";
@@ -121,6 +127,9 @@ export default defineConfig({
121127
usePolling: true,
122128
interval: 200,
123129
},
130+
fs: {
131+
allow: [resolve(import.meta.dirname, "../..")],
132+
},
124133
},
125134
plugins: [
126135
tailwindcss(),

‎bun.lock‎

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎notes/old/mcp-testing.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ Pattern used in `mcp-miniflare.e2e.node.test.ts`:
9090
5. SDK Client advertises `capabilities: { elicitation: { form: {} } }` and
9191
registers `client.setRequestHandler(ElicitRequestSchema, ...)` that returns
9292
`{ action: "accept", content: {} }`.
93-
6. Call `execute` with code that (a) calls `tools.openapi.addSource({ spec,
93+
6. Call `execute` with code that (a) calls `tools.executor.openapi.addSource({ spec,
9494
namespace })`, (b) invokes the POST operation.
9595

9696
**Tool id format inside the `execute` sandbox.**

‎packages/core/execution/src/description.test.ts‎

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import { describe, expect, it } from "@effect/vitest";
2-
import { Effect } from "effect";
2+
import { Effect, Schema } from "effect";
33

44
import { createExecutor, definePlugin, makeTestConfig } from "@executor-js/sdk";
55

66
import { buildExecuteDescription } from "./description";
77

8-
const EmptyInputSchema = {
9-
type: "object",
10-
properties: {},
11-
additionalProperties: false,
12-
} as const;
8+
const EmptyInputSchema = Schema.toStandardSchemaV1(
9+
Schema.toStandardJSONSchemaV1(Schema.Struct({})),
10+
);
1311

1412
// Two plugins registering static sources whose ids are distinct from their
1513
// pluginIds/names. If `buildExecuteDescription` ever renders the wrong field

‎packages/core/execution/src/tool-invoker.test.ts‎

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, it } from "@effect/vitest";
2-
import { Effect, Fiber } from "effect";
2+
import { Effect, Fiber, Schema } from "effect";
33

44
import {
55
ElicitationResponse,
@@ -14,30 +14,17 @@ import { describeTool, makeExecutorToolInvoker, searchTools } from "./tool-invok
1414

1515
const codeExecutor = makeQuickJsExecutor();
1616

17-
const RepoInputSchema = {
18-
type: "object",
19-
properties: {
20-
owner: { type: "string" },
21-
repo: { type: "string" },
22-
},
23-
required: ["owner", "repo"],
24-
additionalProperties: false,
25-
} as const;
26-
27-
const ContactInputSchema = {
28-
type: "object",
29-
properties: {
30-
email: { type: "string" },
31-
},
32-
required: ["email"],
33-
additionalProperties: false,
34-
} as const;
35-
36-
const EmptyInputSchema = {
37-
type: "object",
38-
properties: {},
39-
additionalProperties: false,
40-
} as const;
17+
const RepoInputSchema = Schema.toStandardSchemaV1(
18+
Schema.toStandardJSONSchemaV1(Schema.Struct({ owner: Schema.String, repo: Schema.String })),
19+
);
20+
21+
const ContactInputSchema = Schema.toStandardSchemaV1(
22+
Schema.toStandardJSONSchemaV1(Schema.Struct({ email: Schema.String })),
23+
);
24+
25+
const EmptyInputSchema = Schema.toStandardSchemaV1(
26+
Schema.toStandardJSONSchemaV1(Schema.Struct({})),
27+
);
4128

4229
const acceptAll = () => Effect.succeed(new ElicitationResponse({ action: "accept" }));
4330

0 commit comments

Comments
 (0)