History

Revert the outbound MCP SDK v2 migration (#1697)

* Revert the outbound MCP SDK v2 migration * Drop the v2 MCP packages from the lockfile

Rhys Sullivan committed 2 months ago

Commit a5800a11 parent 54cde5e91 files · +5838 −10032Browse files

Showing the changes to packages/core/api/src/server (2 of 91 files). Show all

Unified · Split

2 files
  1. packages/core/api/src/server/executor-app.ts +14 −33
  2. packages/core/api/src/server/mcp-build.ts +31 −49
packages/core/api/src/server/executor-app.ts+14−33
@@ -15,8 +15,8 @@ · 14 unmodified lines
1515 // + that stack + plugin tuple + failure strategy) (auth + per-request executor)
1616 // 3. the protected (plugin) API = makeProtectedApiLayer(plugins, { errorCapture,
1717 // router: prefixed(mountPrefix) }) wrapped by (2)
18−// 4. the MCP serving envelope = McpServingRoutes + the auth/session/modern
19−// builder/reporter seams (the seams)
18+// 4. the MCP serving envelope = McpServingRoutes + the 2-3 seams (auth/sessions
19+// /reporter), double-provided like the host did (the seams)
2020 // 5. the account API = makeAccountApiLayer(accountMiddleware, { router })
2121 // 6. each extensions.route (Better Auth handler, Swagger, marketing, /autumn)
2222 // 7. provideMerge(boot) (+ optional requestScoped) -> the AppLayer
@@ -53,7 +53,6 @@ import { · 30 unmodified lines
5353 McpErrorReporterNoop,
5454 type McpAuthProvider,
5555 type McpErrorReporter,
56− type McpModernServerBuilder,
5756 type McpSessionStore,
5857 } from "@executor-js/host-mcp";
5958
@@ -133,32 +132,19 @@ export interface EngineProviders<REngine = never> { · 73 unmodified lines
133132 * identity fallback sets `RMcpAuth = IdentityProvider` (self-host) and one whose
134133 * MCP plane is a separate credential surface leaves it `never` (cloud).
135134 */
136−interface McpProviderBase<RMcpAuth> {
135+export interface McpProviders<RMcpAuth = never> {
137136 /** Resolve a request to an MCP `AuthOutcome` + declare the discovery routes. */
138137 readonly auth: Layer.Layer<McpAuthProvider, never, RMcpAuth>;
138+ /**
139+ * Owns the entire serving-session lifecycle (in-process Map vs DO). Optional:
140+ * a host that serves `/mcp` transport outside this envelope (the Cloudflare
141+ * Agent bridge) omits it, and only the discovery routes are mounted.
142+ */
143+ readonly sessions?: Layer.Layer<McpSessionStore>;
139144 /** Forward an orchestration defect to the host's capture; default no-op. */
140145 readonly reporter?: Layer.Layer<McpErrorReporter>;
141146 }
142147
143−/** MCP providers either serve discovery alone or provide both protocol eras. */
144−export type McpProviders<RMcpAuth = never> = McpProviderBase<RMcpAuth> &
145− (
146− | {
147− /**
148− * Owns the legacy serving-session lifecycle (in-process Map vs DO).
149− */
150− readonly sessions: Layer.Layer<McpSessionStore>;
151− /** Builds one stateless MCP server for each modern request. */
152− readonly modern: Layer.Layer<McpModernServerBuilder>;
153− }
154− | {
155− /** Omitted when another platform surface serves `/mcp` transport. */
156− readonly sessions?: undefined;
157− /** Discovery-only providers do not construct modern servers here. */
158− readonly modern?: undefined;
159− }
160− );
161−
162148 /**
163149 * The provider seams common to BOTH execution models (scoped + fixed): identity,
164150 * the optional account API, the optional MCP envelope, and error capture. The
@@ -560,12 +546,12 @@ export const make = < · 395 unmodified lines
560546 : pluginApiLive;
561547
562548 // ---- (4) the MCP serving envelope (optional) --------------------------
563− // The serving providers, by design (mirrors makeSelfHostMcp):
549+ // The two providers, by design (mirrors makeSelfHostMcp):
564550 // - `Layer.provide(mcpAuth)` satisfies the `HttpRouter.use` callback's
565551 // build-time `McpAuthProvider` requirement (it registers a GET per
566552 // provider-declared discovery path).
567553 // - `HttpRouter.provideRequest(McpSeams)` clears the route handlers'
568− // per-request `Requires` markers (auth + both eras + reporter) so the
554+ // per-request `Requires` markers (auth + session store + reporter) so the
569555 // /mcp routes carry no leftover requirements when merged into the router.
570556 // The auth seam may require the neutral `IdentityProvider` (`RMcpAuth =
571557 // IdentityProvider` for self-host, whose MCP auth genuinely reads the fallback;
@@ -617,7 +603,7 @@ export const make = < · 45 unmodified lines
617603 };
618604
619605 /**
620− * Compose the MCP serving routes over the auth/legacy/modern/reporter seams. The auth
606+ * Compose the MCP serving routes over the auth/sessions/reporter seams. The auth
621607 * seam may require the neutral `IdentityProvider` (`RMcpAuth`); the facade provides
622608 * the complete identity seam ONCE (memoized) and shares it across the build-time
623609 * `Layer.provide` AND the per-request `HttpRouter.provideRequest`, so a single
@@ -639,15 +625,10 @@ const buildMcpRoutes = <RMcpAuth>( · 15 unmodified lines
639625 // No session store: the host serves `/mcp` transport elsewhere (the Cloudflare
640626 // Agent bridge), so mount only the auth-declared discovery routes. The discovery
641627 // handlers are captured from the auth seam at build, so no per-request seams.
642− if (mcp.sessions === undefined) {
628+ if (!mcp.sessions) {
643629 return McpDiscoveryRoutes.pipe(Layer.provide(mcpAuthLive));
644630 }
645− const mcpSeams = Layer.mergeAll(
646− mcpAuthLive,
647− mcp.sessions,
648− mcp.modern,
649− mcp.reporter ?? McpErrorReporterNoop,
650− );
631+ const mcpSeams = Layer.mergeAll(mcpAuthLive, mcp.sessions, mcp.reporter ?? McpErrorReporterNoop);
651632 return McpServingRoutes.pipe(HttpRouter.provideRequest(mcpSeams), Layer.provide(mcpAuthLive));
652633 };
653634
packages/core/api/src/server/mcp-build.ts+31−49
@@ -1,17 +1,12 @@
11 import { Effect, Layer } from "effect";
22
3−import {
4− McpErrorReporter,
5− type McpModernServerBuilder,
6− type McpModernServerBuildOptions,
7− type Principal,
8−} from "@executor-js/host-mcp";
3+import { McpErrorReporter, type Principal } from "@executor-js/host-mcp";
94 import {
105 McpEngineBuildError,
11− type McpBuildServer as McpSessionBuildServer,
12− type McpBuildServerOptions as McpSessionBuildOptions,
6+ type McpBuildServer,
7+ type McpBuildServerOptions,
138 } from "@executor-js/host-mcp/in-memory-session-store";
14−import { buildMcpServer } from "@executor-js/host-mcp/tool-server";
9+import { createExecutorMcpServer } from "@executor-js/host-mcp/tool-server";
1510 import {
1611 artifactUrlFor,
1712 type ArtifactSmokeRenderResult,
@@ -25,8 +20,14 @@ import { HostConfig, PluginsProvider, RequestOrgSlug } from "./scoped-executor"; · 7 unmodified lines
2520 // ---------------------------------------------------------------------------
2621 // Shared in-process MCP host helpers.
2722 //
28−// Neutral hosts build both sessionful legacy-wire connections and stateless
29−// modern requests from the same assembly over a scoped execution stack.
23+// Every host that serves MCP from one isolate (self-host, the Cloudflare QuickJS
24+// host) builds its per-session McpServer the same way — assemble the scoped
25+// engine via `makeExecutionStack`, wrap it with `createExecutorMcpServer` — and
26+// reports orchestration defects through the same console `ErrorCapture` seam.
27+// These two factories are the single home for that logic; a host supplies ONLY
28+// its fully-provided execution-stack layer and its `ErrorCapture` layer. The
29+// cross-isolate variant (cloud's Durable Object store) is the exception that
30+// builds its engine inside the DO.
3031 // ---------------------------------------------------------------------------
3132
3233 /** The five execution-stack seams a host fully provides (no residual). */
@@ -34,55 +35,36 @@ export type McpExecutionStackLayer = Layer.Layer< · 1 unmodified line
3435 DbProvider | PluginsProvider | HostConfig | CodeExecutorProvider | EngineDecorator
3536 >;
3637
37−type McpSessionBuildEffect = ReturnType<McpSessionBuildServer>;
38−type McpModernBuildEffect = ReturnType<McpModernServerBuilder["Service"]["build"]>;
39−
40−/** A single build seam accepted by both the session store and modern envelope. */
41−export interface McpBuildServer {
42− /** Build a connection-lifetime server and retain its engine for session-owned approvals. */
43− (principal: Principal, options: McpSessionBuildOptions): McpSessionBuildEffect;
44− /** Build a stateless server for one modern request. */
45− (principal: Principal, options: McpModernServerBuildOptions): McpModernBuildEffect;
46−}
47−
4838 /**
49− * Build the unified MCP server factory over a host's execution stack.
50− * Session callers receive the server plus its approval-owning engine; modern
51− * request callers receive the server directly.
39+ * Build the per-session MCP server factory over a host's execution stack:
40+ * `makeExecutionStack` → engine → `createExecutorMcpServer`. Hosts differ only
41+ * in the injected stack layer (libSQL vs D1, etc.).
5242 */
53−export const makeMcpBuildServer = (
54− executionStack: McpExecutionStackLayer,
55− hostOptions?: McpBuildHostOptions,
56−): McpBuildServer => {
57− function build(principal: Principal, options: McpSessionBuildOptions): McpSessionBuildEffect;
58− function build(principal: Principal, options: McpModernServerBuildOptions): McpModernBuildEffect;
59− function build(
60− principal: Principal,
61− options: McpSessionBuildOptions | McpModernServerBuildOptions,
62− ): Effect.Effect<
63− Effect.Success<McpSessionBuildEffect> | Effect.Success<McpModernBuildEffect>,
64− Effect.Error<McpSessionBuildEffect> | Effect.Error<McpModernBuildEffect>
65− > {
66− const { resource, ...serverOptions } = options;
67− return Effect.gen(function* () {
43+export const makeMcpBuildServer =
44+ (executionStack: McpExecutionStackLayer, hostOptions?: McpBuildHostOptions): McpBuildServer =>
45+ (principal: Principal, options?: McpBuildServerOptions) =>
46+ Effect.gen(function* () {
6847 const { engine, executor } = yield* makeExecutionStack(
6948 principal.accountId,
7049 principal.organizationId,
7150 principal.organizationName,
72− { mcpResource: resource },
51+ { mcpResource: options?.resource },
7352 ).pipe(Effect.withSpan("mcp.execution_stack.build"));
7453 // Read inside the provided boundary: `webBaseUrl` is a host seam, and
75− // hosts that cannot know their public URL at boot leave it unset.
54+ // hosts that can't know their public URL at boot leave it unset — in
55+ // which case artifacts still persist but carry no deep link.
7656 const hostConfig = yield* HostConfig;
7757 return { engine, executor, webBaseUrl: hostConfig.webBaseUrl };
7858 }).pipe(
59+ // Pin browser-handoff URLs to the principal's org slug when present;
60+ // absent slug leaves the service unprovided and the URL stays bare.
7961 principal.organizationSlug !== undefined
8062 ? Effect.provideService(RequestOrgSlug, { slug: principal.organizationSlug })
8163 : (effect) => effect,
8264 Effect.provide(executionStack),
8365 Effect.mapError((cause) => new McpEngineBuildError({ cause })),
8466 Effect.flatMap(({ engine, executor, webBaseUrl }) =>
85− buildMcpServer({
67+ createExecutorMcpServer({
8668 engine,
8769 artifacts: executor.artifacts,
8870 connections: executor.connections,
@@ -93,20 +75,20 @@ export const makeMcpBuildServer = ( · 4 unmodified lines
9375 ? { smokeRenderArtifact: hostOptions.smokeRenderArtifact }
9476 : {}),
9577 ...(hostOptions?.onArtifactUsage ? { onArtifactUsage: hostOptions.onArtifactUsage } : {}),
78+ // Same org pinning as `RequestOrgSlug` above: self-host serves its
79+ // console under `/<org-slug>` (`default` when unconfigured), so the
80+ // deep link carries the principal's slug rather than relying on the
81+ // browser's active org to canonicalize a bare path after landing.
9682 ...(webBaseUrl
9783 ? { artifactUrl: artifactUrlFor(webBaseUrl, principal.organizationSlug) }
9884 : {}),
99− ...serverOptions,
85+ ...(options ?? {}),
10086 }).pipe(
10187 Effect.withSpan("mcp.server.create"),
102− Effect.map((mcpServer) => ("sessionful" in options ? { mcpServer, engine } : mcpServer)),
88+ Effect.map((mcpServer) => ({ mcpServer, engine })),
10389 ),
10490 ),
10591 );
106− }
107−
108− return build;
109−};
11092
11193 /** Per-host (not per-session) MCP wiring. Kept separate from
11294 * `McpBuildServerOptions`, which the session store fills in per request. */