History

rework naming

Rhys Sullivan committed 7 months ago

Commit da613211 parent f0c45e713 files · +310 −304Browse files

Unified · Split

13 files
  1. examples/sqlite-sdk-consumer/src/sqlite-backend.ts +90 −87
  2. packages/platform/sdk-file/src/effect.ts +1 −1
  3. packages/platform/sdk-file/src/index.ts +41 −41
  4. packages/platform/sdk/src/backend.ts +61 −51
  5. packages/platform/sdk/src/executor.test.ts +14 −25
  6. packages/platform/sdk/src/runtime/execution/mcp-resume.test.ts +1 −1
  7. packages/platform/sdk/src/runtime/executor-runtime.test.ts +46 −30
  8. packages/platform/sdk/src/runtime/sources/source-adapters/__fixtures__/mcp-stdio-server.mjs +26 −0
  9. packages/platform/sdk/src/runtime/sources/source-adapters/mcp.test.ts +20 −38
  10. packages/platform/sdk/src/runtime/sources/source-discovery.test.ts +1 −1
  11. packages/platform/sdk/src/runtime/sources/source-inspection.test.ts +1 −1
  12. packages/sources/mcp/src/adapter.ts +3 −1
  13. packages/sources/mcp/src/connection.ts +5 −27
examples/sqlite-sdk-consumer/src/sqlite-backend.ts+90−87
@@ -14,10 +14,10 @@ import { · 13 unmodified lines
1414 import {
1515 createExecutorBackend,
1616 type ExecutorBackend,
17− type ExecutorBackendServices,
18− type ExecutorScopeConfigBackend,
19− type ExecutorScopeStateBackend,
20− type ExecutorSourceArtifactBackend,
17+ type ExecutorBackendRepositories,
18+ type ExecutorWorkspaceConfigRepository,
19+ type ExecutorWorkspaceStateRepository,
20+ type ExecutorWorkspaceSourceArtifactRepository,
2121 } from "@executor/platform-sdk";
2222 import type {
2323 AuthArtifact,
@@ -53,10 +53,13 @@ export type CreateSqliteExecutorBackendOptions = { · 29 unmodified lines
5353 actorScopeId?: string;
5454 };
5555
56−type ScopeConfig = Awaited<ReturnType<ExecutorScopeConfigBackend["load"]>>;
57−type ScopeState = Awaited<ReturnType<ExecutorScopeStateBackend["load"]>>;
58−type SourceArtifact = ReturnType<ExecutorSourceArtifactBackend["build"]>;
59−type SourceArtifactBuildInput = Parameters<ExecutorSourceArtifactBackend["build"]>[0];
56+type ScopeConfig = Awaited<ReturnType<ExecutorWorkspaceConfigRepository["load"]>>;
57+type ScopeState = Awaited<ReturnType<ExecutorWorkspaceStateRepository["load"]>>;
58+type SourceArtifact =
59+ ReturnType<ExecutorWorkspaceSourceArtifactRepository["build"]>;
60+type SourceArtifactBuildInput = Parameters<
61+ ExecutorWorkspaceSourceArtifactRepository["build"]
62+>[0];
6063
6164 type SecretMaterialSummary = {
6265 id: string;
@@ -924,7 +927,7 @@ export const createSqliteExecutorBackend = ( · 861 unmodified lines
924927 : (options.databasePath ?? ":memory:");
925928
926929 return createExecutorBackend({
927− loadServices: () => {
930+ loadRepositories: () => {
928931 const store = openSqliteStore(databasePath);
929932 const { auth, secrets, executions } = createStorageDomains(store);
930933
@@ -937,9 +940,21 @@ export const createSqliteExecutorBackend = ( · 6 unmodified lines
937940 databasePath,
938941 },
939942 },
940− storage: {
941− installation: {
942− load: () => {
943+ installation: {
944+ load: () => {
945+ const row = store.db.select().from(installations).where(eq(installations.key, "active")).get();
946+ return row
947+ ? {
948+ scopeId: row.scopeId as LocalInstallation["scopeId"],
949+ actorScopeId: row.actorScopeId as LocalInstallation["actorScopeId"],
950+ resolutionScopeIds: parseJson<LocalInstallation["resolutionScopeIds"]>(
951+ row.resolutionScopeIdsJson,
952+ ),
953+ }
954+ : createInstallation(options);
955+ },
956+ getOrProvision: () => {
957+ const installation = (() => {
943958 const row = store.db.select().from(installations).where(eq(installations.key, "active")).get();
944959 return row
945960 ? {
@@ -950,37 +965,25 @@ export const createSqliteExecutorBackend = ( · 4 unmodified lines
950965 ),
951966 }
952967 : createInstallation(options);
953− },
954− getOrProvision: () => {
955− const installation = (() => {
956− const row = store.db.select().from(installations).where(eq(installations.key, "active")).get();
957− return row
958− ? {
959− scopeId: row.scopeId as LocalInstallation["scopeId"],
960− actorScopeId: row.actorScopeId as LocalInstallation["actorScopeId"],
961− resolutionScopeIds: parseJson<LocalInstallation["resolutionScopeIds"]>(
962− row.resolutionScopeIdsJson,
963− ),
964− }
965− : createInstallation(options);
966− })();
967− store.db.insert(installations).values({
968− key: "active",
968+ })();
969+ store.db.insert(installations).values({
970+ key: "active",
971+ scopeId: installation.scopeId,
972+ actorScopeId: installation.actorScopeId,
973+ resolutionScopeIdsJson: JSON.stringify(installation.resolutionScopeIds),
974+ }).onConflictDoUpdate({
975+ target: installations.key,
976+ set: {
969977 scopeId: installation.scopeId,
970978 actorScopeId: installation.actorScopeId,
971979 resolutionScopeIdsJson: JSON.stringify(installation.resolutionScopeIds),
972− }).onConflictDoUpdate({
973− target: installations.key,
974− set: {
975− scopeId: installation.scopeId,
976− actorScopeId: installation.actorScopeId,
977− resolutionScopeIdsJson: JSON.stringify(installation.resolutionScopeIds),
978− },
979− }).run();
980− return installation;
981− },
980+ },
981+ }).run();
982+ return installation;
982983 },
983− scopeConfig: {
984+ },
985+ workspace: {
986+ config: {
984987 load: () => {
985988 const row = store.db.select().from(scopeConfigs).where(eq(scopeConfigs.key, "project")).get();
986989 const projectConfig = row
@@ -1003,7 +1006,7 @@ export const createSqliteExecutorBackend = ( · 16 unmodified lines
10031006 },
10041007 resolveRelativePath: ({ path, scopeRoot }) => resolvePath(scopeRoot, path),
10051008 },
1006− scopeState: {
1009+ state: {
10071010 load: () => {
10081011 const row = store.db.select().from(scopeStates).where(eq(scopeStates.key, "active")).get();
10091012 return row ? parseJson<ScopeState>(row.stateJson) : defaultScopeState();
@@ -1039,55 +1042,52 @@ export const createSqliteExecutorBackend = ( · 29 unmodified lines
10391042 store.db.delete(sourceArtifacts).where(eq(sourceArtifacts.sourceId, sourceId)).run();
10401043 },
10411044 },
1042− auth,
1043− secrets: {
1044− ...secrets,
1045− resolve: ({ ref }) => {
1046− const material = secrets.getById(
1047− ref.handle as SecretMaterial["id"],
1048− );
1049− if (!material || material.value === null) {
1050− throw new Error(`Missing secret material ${ref.handle}`);
1051− }
1052− return material.value;
1053− },
1054− store: ({ purpose, value, name, providerId }) => {
1055− const now = Date.now();
1056− const id = SecretMaterialIdSchema.make(`secret_${randomUUID()}`);
1057− const material: SecretMaterial = {
1058− id,
1059− providerId: providerId ?? SQLITE_SECRET_PROVIDER_ID,
1060− handle: id,
1061− name: name ?? null,
1062− purpose,
1063− value,
1064− createdAt: now,
1065− updatedAt: now,
1066− };
1067− secrets.upsert(material);
1068− return {
1069− providerId: material.providerId,
1070− handle: material.handle,
1071− } satisfies SecretRef;
1072− },
1073− delete: (ref) =>
1074− secrets.removeById(ref.handle as SecretMaterial["id"]),
1075− update: ({ ref, name, value }) => {
1076− const updated = secrets.updateById(
1077− ref.handle as SecretMaterial["id"],
1078− { name, value },
1079− );
1080− if (!updated) {
1081− throw new Error(`Missing secret material ${ref.handle}`);
1082− }
1083− return updated;
1084− },
1045+ sourceAuth: auth,
1046+ },
1047+ secrets: {
1048+ ...secrets,
1049+ resolve: ({ ref }) => {
1050+ const material = secrets.getById(
1051+ ref.handle as SecretMaterial["id"],
1052+ );
1053+ if (!material || material.value === null) {
1054+ throw new Error(`Missing secret material ${ref.handle}`);
1055+ }
1056+ return material.value;
10851057 },
1086− executions,
1087− close: async () => {
1088− store.close();
1058+ store: ({ purpose, value, name, providerId }) => {
1059+ const now = Date.now();
1060+ const id = SecretMaterialIdSchema.make(`secret_${randomUUID()}`);
1061+ const material: SecretMaterial = {
1062+ id,
1063+ providerId: providerId ?? SQLITE_SECRET_PROVIDER_ID,
1064+ handle: id,
1065+ name: name ?? null,
1066+ purpose,
1067+ value,
1068+ createdAt: now,
1069+ updatedAt: now,
1070+ };
1071+ secrets.upsert(material);
1072+ return {
1073+ providerId: material.providerId,
1074+ handle: material.handle,
1075+ } satisfies SecretRef;
1076+ },
1077+ delete: (ref) =>
1078+ secrets.removeById(ref.handle as SecretMaterial["id"]),
1079+ update: ({ ref, name, value }) => {
1080+ const updated = secrets.updateById(
1081+ ref.handle as SecretMaterial["id"],
1082+ { name, value },
1083+ );
1084+ if (!updated) {
1085+ throw new Error(`Missing secret material ${ref.handle}`);
1086+ }
1087+ return updated;
10891088 },
10901089 },
1090+ executions,
10911091 instanceConfig: {
10921092 resolve: () => ({
10931093 platform: "sqlite-sdk-example",
@@ -1101,7 +1101,10 @@ export const createSqliteExecutorBackend = ( · 7 unmodified lines
11011101 defaultSecretStoreProvider: SQLITE_SECRET_PROVIDER_ID,
11021102 }),
11031103 },
1104− } satisfies ExecutorBackendServices;
1104+ close: async () => {
1105+ store.close();
1106+ },
1107+ } satisfies ExecutorBackendRepositories;
11051108 },
11061109 });
11071110 };
packages/platform/sdk-file/src/effect.ts+1−1
@@ -2,7 +2,7 @@ export type { CreateLocalExecutorBackendOptions } from "./index"; · 1 unmodified line
22 export {
33 buildLocalSourceArtifact,
44 createLocalExecutorEffect,
5− createLocalExecutorServicesEffect,
5+ createLocalExecutorRepositoriesEffect,
66 deriveLocalInstallation,
77 getOrProvisionLocalInstallation,
88 loadLocalExecutorConfig,
packages/platform/sdk-file/src/index.ts+41−41
@@ -5,10 +5,10 @@ import { · 4 unmodified lines
55 createExecutorBackend,
66 type Executor,
77 type ExecutorBackend,
8− type ExecutorBackendServices,
9− type ExecutorLocalToolBackend,
10− type ExecutorSourceTypeDeclarationsBackend,
8+ type ExecutorBackendRepositories,
119 type ExecutorScopeDescriptor,
10+ type ExecutorWorkspaceLocalToolRepository,
11+ type ExecutorWorkspaceSourceTypeDeclarationsRepository,
1212 } from "@executor/platform-sdk";
1313 import {
1414 createExecutorEffect,
@@ -176,14 +176,14 @@ const createBoundSourceArtifactStore = ( · 161 unmodified lines
176176 const createBoundLocalToolRuntimeLoader = (
177177 context: ResolvedLocalWorkspaceContext,
178178 fileSystem: FileSystem.FileSystem,
179−): ExecutorLocalToolBackend => ({
179+): ExecutorWorkspaceLocalToolRepository => ({
180180 load: () =>
181181 bindFileSystem(fileSystem, loadLocalToolRuntime(context)),
182182 });
183183
184184 const createBoundSourceTypeDeclarationsRefresher = (
185185 context: ResolvedLocalWorkspaceContext,
186−): ExecutorSourceTypeDeclarationsBackend => ({
186+): ExecutorWorkspaceSourceTypeDeclarationsRepository => ({
187187 refreshWorkspaceInBackground: ({ entries }) =>
188188 Effect.sync(() => {
189189 refreshWorkspaceSourceTypeDeclarationsInBackground({
@@ -217,10 +217,10 @@ const toExecutorScopeContext = ( · 27 unmodified lines
217217 },
218218 });
219219
220−export const createLocalExecutorServicesEffect = (
220+export const createLocalExecutorRepositoriesEffect = (
221221 options: CreateLocalExecutorBackendOptions = {},
222222 runtimeOptions: ExecutorRuntimeOptions = {},
223−): Effect.Effect<ExecutorBackendServices, Error> =>
223+): Effect.Effect<ExecutorBackendRepositories, Error> =>
224224 Effect.gen(function* () {
225225 const fileSystem = yield* FileSystem.FileSystem;
226226 const workspaceContext = yield* bindFileSystem(
@@ -252,10 +252,10 @@ export const createLocalExecutorServicesEffect = ( · 25 unmodified lines
252252
253253 return {
254254 scope: toExecutorScopeContext(workspaceContext),
255− storage: {
256− installation: createBoundInstallationStore(workspaceContext),
257− scopeConfig: workspaceConfigStore,
258− scopeState: createBoundWorkspaceStateStore(
255+ installation: createBoundInstallationStore(workspaceContext),
256+ workspace: {
257+ config: workspaceConfigStore,
258+ state: createBoundWorkspaceStateStore(
259259 workspaceContext,
260260 fileSystem,
261261 ),
@@ -263,7 +263,7 @@ export const createLocalExecutorServicesEffect = ( · 1 unmodified line
263263 workspaceContext,
264264 fileSystem,
265265 ),
266− auth: {
266+ sourceAuth: {
267267 artifacts: executorStateStorage.executorState.authArtifacts,
268268 leases: executorStateStorage.executorState.authLeases,
269269 sourceOauthClients:
@@ -275,45 +275,45 @@ export const createLocalExecutorServicesEffect = ( · 5 unmodified lines
275275 sourceSessions:
276276 executorStateStorage.executorState.sourceAuthSessions,
277277 },
278− secrets: {
279− ...executorStateStorage.executorState.secretMaterials,
280− resolve: resolveSecretMaterial,
281− store: createDefaultSecretMaterialStorer({
282− executorState: executorStateStorage.executorState,
283− }),
284− delete: createDefaultSecretMaterialDeleter({
285− executorState: executorStateStorage.executorState,
286− }),
287− update: createDefaultSecretMaterialUpdater({
288− executorState: executorStateStorage.executorState,
289− }),
290− },
291− executions: {
292− runs: executorStateStorage.executorState.executions,
293− interactions:
294− executorStateStorage.executorState.executionInteractions,
295− steps: executorStateStorage.executorState.executionSteps,
296− },
297− close: executorStateStorage.close,
278+ localTools: createBoundLocalToolRuntimeLoader(
279+ workspaceContext,
280+ fileSystem,
281+ ),
282+ sourceTypeDeclarations:
283+ createBoundSourceTypeDeclarationsRefresher(workspaceContext),
284+ },
285+ secrets: {
286+ ...executorStateStorage.executorState.secretMaterials,
287+ resolve: resolveSecretMaterial,
288+ store: createDefaultSecretMaterialStorer({
289+ executorState: executorStateStorage.executorState,
290+ }),
291+ delete: createDefaultSecretMaterialDeleter({
292+ executorState: executorStateStorage.executorState,
293+ }),
294+ update: createDefaultSecretMaterialUpdater({
295+ executorState: executorStateStorage.executorState,
296+ }),
297+ },
298+ executions: {
299+ runs: executorStateStorage.executorState.executions,
300+ interactions:
301+ executorStateStorage.executorState.executionInteractions,
302+ steps: executorStateStorage.executorState.executionSteps,
298303 },
299− localTools: createBoundLocalToolRuntimeLoader(
300− workspaceContext,
301− fileSystem,
302− ),
303− sourceTypeDeclarations:
304− createBoundSourceTypeDeclarationsRefresher(workspaceContext),
305304 instanceConfig: {
306305 resolve: createLocalInstanceConfigResolver(),
307306 },
308− } satisfies ExecutorBackendServices;
307+ close: executorStateStorage.close,
308+ } satisfies ExecutorBackendRepositories;
309309 }).pipe(Effect.provide(NodeFileSystem.layer));
310310
311311 export const createLocalExecutorBackend = (
312312 options: CreateLocalExecutorBackendOptions = {},
313313 ): ExecutorBackend =>
314314 createExecutorBackend({
315− loadServices: (runtimeOptions: ExecutorRuntimeOptions) =>
316− createLocalExecutorServicesEffect(options, runtimeOptions),
315+ loadRepositories: (runtimeOptions: ExecutorRuntimeOptions) =>
316+ createLocalExecutorRepositoriesEffect(options, runtimeOptions),
317317 });
318318
319319 export const createLocalExecutorRuntime = (
packages/platform/sdk/src/backend.ts+61−51
@@ -47,15 +47,19 @@ type PublicizeObject<T> = { · 46 unmodified lines
4747 : T[Key];
4848 };
4949
50−export type ExecutorInstallationBackend = PublicizeObject<BoundInstallationStore>;
51−export type ExecutorScopeConfigBackend = PublicizeObject<BoundScopeConfigStore>;
52−export type ExecutorScopeStateBackend = PublicizeObject<BoundScopeStateStore>;
53−export type ExecutorSourceArtifactBackend = PublicizeObject<BoundSourceArtifactStore>;
54−export type ExecutorLocalToolBackend = PublicizeObject<BoundLocalToolRuntimeLoader>;
55−export type ExecutorSourceTypeDeclarationsBackend = PublicizeObject<
50+export type ExecutorInstallationRepository = PublicizeObject<BoundInstallationStore>;
51+export type ExecutorWorkspaceConfigRepository = PublicizeObject<BoundScopeConfigStore>;
52+export type ExecutorWorkspaceStateRepository = PublicizeObject<BoundScopeStateStore>;
53+export type ExecutorWorkspaceSourceArtifactRepository = PublicizeObject<
54+ BoundSourceArtifactStore
55+>;
56+export type ExecutorWorkspaceLocalToolRepository = PublicizeObject<
57+ BoundLocalToolRuntimeLoader
58+>;
59+export type ExecutorWorkspaceSourceTypeDeclarationsRepository = PublicizeObject<
5660 BoundSourceTypeDeclarationsRefresher
5761 >;
58−export type ExecutorAuthBackend = {
62+export type ExecutorWorkspaceSourceAuthRepository = {
5963 artifacts: PublicizeObject<RuntimeAuthStorageServices["artifacts"]>;
6064 leases: PublicizeObject<RuntimeAuthStorageServices["leases"]>;
6165 sourceOauthClients: PublicizeObject<RuntimeAuthStorageServices["sourceOauthClients"]>;
@@ -63,31 +67,33 @@ export type ExecutorAuthBackend = { · 1 unmodified line
6367 providerGrants: PublicizeObject<RuntimeAuthStorageServices["providerGrants"]>;
6468 sourceSessions: PublicizeObject<RuntimeAuthStorageServices["sourceSessions"]>;
6569 };
66−export type ExecutorSecretsBackend = PublicizeObject<RuntimeSecretsStorageServices>;
67−export type ExecutorExecutionsBackend = {
70+export type ExecutorSecretRepository = PublicizeObject<RuntimeSecretsStorageServices>;
71+export type ExecutorExecutionRepository = {
6872 runs: PublicizeObject<RuntimeExecutionStorageServices["runs"]>;
6973 interactions: PublicizeObject<RuntimeExecutionStorageServices["interactions"]>;
7074 steps: PublicizeObject<RuntimeExecutionStorageServices["steps"]>;
7175 };
72−export type ExecutorInstanceConfigBackend = PublicizeObject<RuntimeInstanceConfigService>;
76+export type ExecutorInstanceConfigRepository = PublicizeObject<
77+ RuntimeInstanceConfigService
78+>;
7379
74−export type ExecutorStorageBackend = {
75− installation: ExecutorInstallationBackend;
76− scopeConfig: ExecutorScopeConfigBackend;
77− scopeState: ExecutorScopeStateBackend;
78− sourceArtifacts: ExecutorSourceArtifactBackend;
79− auth: ExecutorAuthBackend;
80− secrets: ExecutorSecretsBackend;
81− executions: ExecutorExecutionsBackend;
82− close?: () => Promise<void>;
80+export type ExecutorWorkspaceRepository = {
81+ config: ExecutorWorkspaceConfigRepository;
82+ state: ExecutorWorkspaceStateRepository;
83+ sourceArtifacts: ExecutorWorkspaceSourceArtifactRepository;
84+ sourceAuth: ExecutorWorkspaceSourceAuthRepository;
85+ localTools?: ExecutorWorkspaceLocalToolRepository;
86+ sourceTypeDeclarations?: ExecutorWorkspaceSourceTypeDeclarationsRepository;
8387 };
8488
85−export type ExecutorBackendServices = {
89+export type ExecutorBackendRepositories = {
8690 scope: ExecutorScopeDescriptor;
87− storage: ExecutorStorageBackend;
88− instanceConfig: ExecutorInstanceConfigBackend;
89− localTools?: ExecutorLocalToolBackend;
90− sourceTypeDeclarations?: ExecutorSourceTypeDeclarationsBackend;
91+ installation: ExecutorInstallationRepository;
92+ workspace: ExecutorWorkspaceRepository;
93+ secrets: ExecutorSecretRepository;
94+ executions: ExecutorExecutionRepository;
95+ instanceConfig: ExecutorInstanceConfigRepository;
96+ close?: () => Promise<void>;
9197 };
9298
9399 const toError = (cause: unknown): Error =>
@@ -118,14 +124,14 @@ const toOptionEffect = <T>( · 24 unmodified lines
118124 );
119125
120126 const toInstallationBackend = (
121− input: ExecutorInstallationBackend,
127+ input: ExecutorInstallationRepository,
122128 ): BoundInstallationStore => ({
123129 load: () => toEffect(input.load()),
124130 getOrProvision: () => toEffect(input.getOrProvision()),
125131 });
126132
127133 const toScopeConfigBackend = (
128− input: ExecutorScopeConfigBackend,
134+ input: ExecutorWorkspaceConfigRepository,
129135 ): BoundScopeConfigStore => ({
130136 load: () => toEffect(input.load()),
131137 writeProject: (config) => toEffect(input.writeProject(config)),
@@ -133,14 +139,14 @@ const toScopeConfigBackend = ( · 1 unmodified line
133139 });
134140
135141 const toScopeStateBackend = (
136− input: ExecutorScopeStateBackend,
142+ input: ExecutorWorkspaceStateRepository,
137143 ): BoundScopeStateStore => ({
138144 load: () => toEffect(input.load()),
139145 write: (state) => toEffect(input.write(state)),
140146 });
141147
142148 const toSourceArtifactBackend = (
143− input: ExecutorSourceArtifactBackend,
149+ input: ExecutorWorkspaceSourceArtifactRepository,
144150 ): BoundSourceArtifactStore => ({
145151 build: input.build,
146152 read: (sourceId) => toEffect(input.read(sourceId)),
@@ -149,19 +155,19 @@ const toSourceArtifactBackend = ( · 2 unmodified lines
149155 });
150156
151157 const toInstanceConfigBackend = (
152− input: ExecutorInstanceConfigBackend,
158+ input: ExecutorInstanceConfigRepository,
153159 ): RuntimeInstanceConfigService => ({
154160 resolve: () => toEffect(input.resolve()),
155161 });
156162
157163 const toLocalToolBackend = (
158− input: ExecutorLocalToolBackend,
164+ input: ExecutorWorkspaceLocalToolRepository,
159165 ): BoundLocalToolRuntimeLoader => ({
160166 load: () => toEffect(input.load()),
161167 });
162168
163169 const toSourceTypeDeclarationsBackend = (
164− input: ExecutorSourceTypeDeclarationsBackend,
170+ input: ExecutorWorkspaceSourceTypeDeclarationsRepository,
165171 ): BoundSourceTypeDeclarationsRefresher => ({
166172 refreshWorkspaceInBackground: (payload) =>
167173 toEffect(input.refreshWorkspaceInBackground(payload)).pipe(Effect.orDie),
@@ -170,7 +176,7 @@ const toSourceTypeDeclarationsBackend = ( · 2 unmodified lines
170176 });
171177
172178 const toAuthBackend = (
173− input: ExecutorAuthBackend,
179+ input: ExecutorWorkspaceSourceAuthRepository,
174180 ): RuntimeAuthStorageServices => ({
175181 artifacts: {
176182 listByScopeId: (scopeId) => toEffect(input.artifacts.listByScopeId(scopeId)),
@@ -230,7 +236,7 @@ const toAuthBackend = ( · 53 unmodified lines
230236 });
231237
232238 const toSecretsBackend = (
233− input: ExecutorSecretsBackend,
239+ input: ExecutorSecretRepository,
234240 ): RuntimeSecretsStorageServices => ({
235241 getById: (id) => toOptionEffect(input.getById(id)),
236242 listAll: () => toEffect(input.listAll()),
@@ -244,7 +250,7 @@ const toSecretsBackend = ( · 7 unmodified lines
244250 });
245251
246252 const toExecutionsBackend = (
247− input: ExecutorExecutionsBackend,
253+ input: ExecutorExecutionRepository,
248254 ): RuntimeExecutionStorageServices => ({
249255 runs: {
250256 getById: (executionId) => toOptionEffect(input.runs.getById(executionId)),
@@ -285,33 +291,37 @@ const toExecutionsBackend = ( · 34 unmodified lines
285291 });
286292
287293 export const createExecutorBackend = (input: {
288− loadServices: (
294+ loadRepositories: (
289295 options: ExecutorRuntimeOptions,
290− ) => MaybeEffect<ExecutorBackendServices>;
296+ ) => MaybeEffect<ExecutorBackendRepositories>;
291297 }): ExecutorBackend => ({
292298 createRuntime: (options) =>
293− Effect.flatMap(toEffect(input.loadServices(options)), (services) =>
299+ Effect.flatMap(toEffect(input.loadRepositories(options)), (repositories) =>
294300 createExecutorRuntimeFromServices({
295301 ...options,
296302 services: {
297− scope: services.scope,
303+ scope: repositories.scope,
298304 storage: {
299− installation: toInstallationBackend(services.storage.installation),
300− scopeConfig: toScopeConfigBackend(services.storage.scopeConfig),
301− scopeState: toScopeStateBackend(services.storage.scopeState),
302− sourceArtifacts: toSourceArtifactBackend(services.storage.sourceArtifacts),
303− auth: toAuthBackend(services.storage.auth),
304− secrets: toSecretsBackend(services.storage.secrets),
305− executions: toExecutionsBackend(services.storage.executions),
306− close: services.storage.close,
305+ installation: toInstallationBackend(repositories.installation),
306+ scopeConfig: toScopeConfigBackend(repositories.workspace.config),
307+ scopeState: toScopeStateBackend(repositories.workspace.state),
308+ sourceArtifacts: toSourceArtifactBackend(
309+ repositories.workspace.sourceArtifacts,
310+ ),
311+ auth: toAuthBackend(repositories.workspace.sourceAuth),
312+ secrets: toSecretsBackend(repositories.secrets),
313+ executions: toExecutionsBackend(repositories.executions),
314+ close: repositories.close,
307315 } satisfies RuntimeStorageServices,
308− localToolRuntimeLoader: services.localTools
309− ? toLocalToolBackend(services.localTools)
316+ localToolRuntimeLoader: repositories.workspace.localTools
317+ ? toLocalToolBackend(repositories.workspace.localTools)
310318 : undefined,
311− sourceTypeDeclarationsRefresher: services.sourceTypeDeclarations
312− ? toSourceTypeDeclarationsBackend(services.sourceTypeDeclarations)
319+ sourceTypeDeclarationsRefresher: repositories.workspace.sourceTypeDeclarations
320+ ? toSourceTypeDeclarationsBackend(
321+ repositories.workspace.sourceTypeDeclarations,
322+ )
313323 : undefined,
314− instanceConfig: toInstanceConfigBackend(services.instanceConfig),
324+ instanceConfig: toInstanceConfigBackend(repositories.instanceConfig),
315325 },
316326 }),
317327 ),
packages/platform/sdk/src/executor.test.ts+14−25
@@ -40,7 +40,7 @@ import { · 39 unmodified lines
4040 ScopeIdSchema,
4141 } from "./schema";
4242 import {
43− createLocalExecutorServicesEffect,
43+ createLocalExecutorRepositoriesEffect,
4444 } from "../../sdk-file/src/effect";
4545 import {
4646 createExecutorApiLayer,
@@ -50,7 +50,7 @@ import { · 3 unmodified lines
5050 } from "../../api/src/index";
5151
5252 type LocalExecutorServices = Effect.Effect.Success<
53− ReturnType<typeof createLocalExecutorServicesEffect>
53+ ReturnType<typeof createLocalExecutorRepositoriesEffect>
5454 >;
5555
5656 const createClientLayer = (executor: Executor) =>
@@ -168,7 +168,7 @@ const createScopedExecutor = (input: { · 111 unmodified lines
168168 }) =>
169169 createExecutorEffect({
170170 backend: createExecutorBackend({
171− loadServices: () =>
171+ loadRepositories: () =>
172172 Effect.succeed({
173173 ...input.services,
174174 scope: {
@@ -176,12 +176,9 @@ const createScopedExecutor = (input: { · 1 unmodified line
176176 actorScopeId: input.installation.actorScopeId,
177177 resolutionScopeIds: input.installation.resolutionScopeIds,
178178 },
179− storage: {
180− ...input.services.storage,
181− installation: {
182− load: () => Effect.succeed(input.installation),
183− getOrProvision: () => Effect.succeed(input.installation),
184− },
179+ installation: {
180+ load: () => Effect.succeed(input.installation),
181+ getOrProvision: () => Effect.succeed(input.installation),
185182 },
186183 }),
187184 }),
@@ -200,21 +197,21 @@ const makeSharedScopeExecutors = Effect.acquireRelease( · 12 unmodified lines
200197 prefix: "executor-sdk-scope-b-",
201198 });
202199
203− const servicesA = yield* createLocalExecutorServicesEffect({
200+ const servicesA = yield* createLocalExecutorRepositoriesEffect({
204201 workspaceRoot: workspaceRootA,
205202 homeConfigPath: join(workspaceRootA, ".executor-home.jsonc"),
206203 homeStateDirectory: join(workspaceRootA, ".executor-home-state"),
207204 });
208− const servicesB = yield* createLocalExecutorServicesEffect({
205+ const servicesB = yield* createLocalExecutorRepositoriesEffect({
209206 workspaceRoot: workspaceRootB,
210207 homeConfigPath: join(workspaceRootB, ".executor-home.jsonc"),
211208 homeStateDirectory: join(workspaceRootB, ".executor-home-state"),
212209 });
213210
214211 const baseInstallationA =
215− yield* servicesA.storage.installation.getOrProvision();
212+ yield* servicesA.installation.getOrProvision();
216213 const baseInstallationB =
217− yield* servicesB.storage.installation.getOrProvision();
214+ yield* servicesB.installation.getOrProvision();
218215
219216 const sharedStorageClose = (() => {
220217 let closed = false;
@@ -223,14 +220,12 @@ const makeSharedScopeExecutors = Effect.acquireRelease( · 2 unmodified lines
223220 return;
224221 }
225222 closed = true;
226− await servicesA.storage.close?.();
227− await servicesB.storage.close?.();
223+ await servicesA.close?.();
224+ await servicesB.close?.();
228225 };
229226 })();
230227
231228 const sharedStorage = {
232− executorState: servicesA.storage.executorState,
233− secretMaterial: servicesA.storage.secretMaterial,
234229 close: sharedStorageClose,
235230 };
236231
@@ -256,20 +251,14 @@ const makeSharedScopeExecutors = Effect.acquireRelease( · 19 unmodified lines
256251 const executorA = yield* createScopedExecutor({
257252 services: {
258253 ...servicesA,
259− storage: {
260− ...servicesA.storage,
261− ...sharedStorage,
262− },
254+ ...sharedStorage,
263255 },
264256 installation: installationA,
265257 });
266258 const executorB = yield* createScopedExecutor({
267259 services: {
268260 ...servicesB,
269− storage: {
270− ...servicesB.storage,
271− ...sharedStorage,
272− },
261+ ...sharedStorage,
273262 },
274263 installation: installationB,
275264 });
packages/platform/sdk/src/runtime/execution/mcp-resume.test.ts+1−1
@@ -369,7 +369,7 @@ describe("execution-mcp-resume", () => { · 368 unmodified lines
369369 expect(created.pendingInteraction.payloadJson).toContain("Approve gated echo");
370370 }
371371
372− const pendingInteraction = yield* runtime.storage.executorState.executionInteractions.getPendingByExecutionId(
372+ const pendingInteraction = yield* runtime.storage.executions.interactions.getPendingByExecutionId(
373373 created.execution.id,
374374 );
375375
packages/platform/sdk/src/runtime/executor-runtime.test.ts+46−30
@@ -59,6 +59,7 @@ import { · 58 unmodified lines
5959
6060 import {
6161 type ExecutorRuntime,
62+ type ExecutorStateStoreShape,
6263 LiveExecutionManagerService,
6364 provideExecutorRuntime,
6465 RuntimeExecutionResolverService,
@@ -346,7 +347,7 @@ const upsertLocalSecret = ( · 281 unmodified lines
346347 value: string;
347348 },
348349 ) =>
349− runtime.storage.executorState.secretMaterials.upsert({
350+ executorStateForRuntime(runtime).secretMaterials.upsert({
350351 id: SecretMaterialIdSchema.make(input.id),
351352 providerId: "local",
352353 handle: input.id,
@@ -357,6 +358,21 @@ const upsertLocalSecret = ( · 4 unmodified lines
357358 updatedAt: Date.now(),
358359 });
359360
361+const executorStateForRuntime = (
362+ runtime: ExecutorRuntime,
363+): ExecutorStateStoreShape => ({
364+ authArtifacts: runtime.storage.auth.artifacts,
365+ authLeases: runtime.storage.auth.leases,
366+ sourceOauthClients: runtime.storage.auth.sourceOauthClients,
367+ scopeOauthClients: runtime.storage.auth.scopeOauthClients,
368+ providerAuthGrants: runtime.storage.auth.providerGrants,
369+ sourceAuthSessions: runtime.storage.auth.sourceSessions,
370+ secretMaterials: runtime.storage.secrets,
371+ executions: runtime.storage.executions.runs,
372+ executionInteractions: runtime.storage.executions.interactions,
373+ executionSteps: runtime.storage.executions.steps,
374+});
375+
360376 const createPersistedGoogleSource = (input: {
361377 runtime: ExecutorRuntime;
362378 name: string;
@@ -403,7 +419,7 @@ const createPersistedGoogleSource = (input: { · 40 unmodified lines
403419 now: Date.now(),
404420 });
405421
406− return yield* persistSource(input.runtime.storage.executorState, source, {
422+ return yield* persistSource(executorStateForRuntime(input.runtime), source, {
407423 actorScopeId: installation.actorScopeId,
408424 }).pipe((effect) => provideExecutorRuntime(effect, input.runtime));
409425 });
@@ -563,7 +579,7 @@ describe("executor-runtime", () => { · 153 unmodified lines
563579 expect(updated.name).toBe("GitHub PAT Updated");
564580
565581 const storedSecret =
566− yield* runtime.storage.executorState.secretMaterials.getById(
582+ yield* executorStateForRuntime(runtime).secretMaterials.getById(
567583 SecretMaterialIdSchema.make(created.id),
568584 );
569585 assertTrue(Option.isSome(storedSecret));
@@ -798,7 +814,7 @@ describe("executor-runtime", () => { · 228 unmodified lines
798814 );
799815 const now = Date.now();
800816
801− yield* runtime.storage.executorState.executions.insert({
817+ yield* executorStateForRuntime(runtime).executions.insert({
802818 id: executionId,
803819 scopeId: installation.scopeId,
804820 createdByScopeId: installation.actorScopeId,
@@ -833,7 +849,7 @@ describe("executor-runtime", () => { · 28 unmodified lines
833849 },
834850 now,
835851 }).pipe(Effect.orDie);
836− yield* persistSource(runtime.storage.executorState, localSource, {
852+ yield* persistSource(executorStateForRuntime(runtime), localSource, {
837853 actorScopeId: installation.actorScopeId,
838854 }).pipe(
839855 (effect) => provideExecutorRuntime(effect, runtime),
@@ -843,7 +859,7 @@ describe("executor-runtime", () => { · 3 unmodified lines
843859 const interactionFiber = yield* Effect.gen(function* () {
844860 const liveExecutionManager = yield* LiveExecutionManagerService;
845861 const onElicitation = liveExecutionManager.createOnElicitation({
846− executorState: runtime.storage.executorState,
862+ executorState: executorStateForRuntime(runtime),
847863 executionId,
848864 });
849865
@@ -874,7 +890,7 @@ describe("executor-runtime", () => { · 24 unmodified lines
874890 const waitForPendingInteraction = (
875891 remaining: number,
876892 ): Effect.Effect<Option.Option<ExecutionInteraction>, Error> =>
877− runtime.storage.executorState.executionInteractions
893+ executorStateForRuntime(runtime).executionInteractions
878894 .getPendingByExecutionId(executionId)
879895 .pipe(
880896 Effect.flatMap((pendingInteraction) =>
@@ -947,7 +963,7 @@ describe("executor-runtime", () => { · 66 unmodified lines
947963 : "",
948964 );
949965 const storedSecret =
950− yield* runtime.storage.executorState.secretMaterials.getById(
966+ yield* executorStateForRuntime(runtime).secretMaterials.getById(
951967 tokenSecretMaterialId,
952968 );
953969 assertTrue(Option.isSome(storedSecret));
@@ -963,7 +979,7 @@ describe("executor-runtime", () => { · 9 unmodified lines
963979 );
964980
965981 const storedInteraction =
966− yield* runtime.storage.executorState.executionInteractions.getById(
982+ yield* executorStateForRuntime(runtime).executionInteractions.getById(
967983 pendingInteraction.value.id,
968984 );
969985 assertTrue(Option.isSome(storedInteraction));
@@ -1315,7 +1331,7 @@ describe("executor-runtime", () => { · 345 unmodified lines
13151331 },
13161332 now,
13171333 }).pipe(Effect.orDie);
1318− yield* persistSource(runtime.storage.executorState, localSource, {
1334+ yield* persistSource(executorStateForRuntime(runtime), localSource, {
13191335 actorScopeId: installation.actorScopeId,
13201336 }).pipe(
13211337 (effect) => provideExecutorRuntime(effect, runtime),
@@ -1373,7 +1389,7 @@ describe("executor-runtime", () => { · 51 unmodified lines
13731389 },
13741390 now,
13751391 }).pipe(Effect.orDie);
1376− yield* persistSource(runtime.storage.executorState, localSource, {
1392+ yield* persistSource(executorStateForRuntime(runtime), localSource, {
13771393 actorScopeId: installation.actorScopeId,
13781394 }).pipe(
13791395 (effect) => provideExecutorRuntime(effect, runtime),
@@ -1415,7 +1431,7 @@ describe("executor-runtime", () => { · 35 unmodified lines
14151431 );
14161432 const now = Date.now();
14171433
1418− yield* runtime.storage.executorState.executions.insert({
1434+ yield* executorStateForRuntime(runtime).executions.insert({
14191435 id: executionId,
14201436 scopeId: installation.scopeId,
14211437 createdByScopeId: installation.actorScopeId,
@@ -1450,7 +1466,7 @@ describe("executor-runtime", () => { · 28 unmodified lines
14501466 },
14511467 now,
14521468 }).pipe(Effect.orDie);
1453− yield* persistSource(runtime.storage.executorState, localSource, {
1469+ yield* persistSource(executorStateForRuntime(runtime), localSource, {
14541470 actorScopeId: installation.actorScopeId,
14551471 }).pipe(
14561472 (effect) => provideExecutorRuntime(effect, runtime),
@@ -1460,7 +1476,7 @@ describe("executor-runtime", () => { · 3 unmodified lines
14601476 const interactionFiber = yield* Effect.gen(function* () {
14611477 const liveExecutionManager = yield* LiveExecutionManagerService;
14621478 const onElicitation = liveExecutionManager.createOnElicitation({
1463− executorState: runtime.storage.executorState,
1479+ executorState: executorStateForRuntime(runtime),
14641480 executionId,
14651481 });
14661482
@@ -1491,7 +1507,7 @@ describe("executor-runtime", () => { · 24 unmodified lines
14911507 const waitForPendingInteraction = (
14921508 remaining: number,
14931509 ): Effect.Effect<Option.Option<ExecutionInteraction>, Error> =>
1494− runtime.storage.executorState.executionInteractions
1510+ executorStateForRuntime(runtime).executionInteractions
14951511 .getPendingByExecutionId(executionId)
14961512 .pipe(
14971513 Effect.flatMap((pendingInteraction) =>
@@ -1522,7 +1538,7 @@ describe("executor-runtime", () => { · 24 unmodified lines
15221538 });
15231539
15241540 const storedInteraction =
1525− yield* runtime.storage.executorState.executionInteractions.getById(
1541+ yield* executorStateForRuntime(runtime).executionInteractions.getById(
15261542 pendingInteraction.value.id,
15271543 );
15281544 assertTrue(Option.isSome(storedInteraction));
@@ -1615,7 +1631,7 @@ describe("executor-runtime", () => { · 86 unmodified lines
16151631 const grantId = ProviderAuthGrantIdSchema.make(
16161632 `provider_grant_${crypto.randomUUID()}`,
16171633 );
1618− yield* runtime.storage.executorState.providerAuthGrants.upsert({
1634+ yield* executorStateForRuntime(runtime).providerAuthGrants.upsert({
16191635 id: grantId,
16201636 scopeId: installation.scopeId,
16211637 actorScopeId: installation.actorScopeId,
@@ -1677,7 +1693,7 @@ describe("executor-runtime", () => { · 55 unmodified lines
16771693 });
16781694
16791695 const storedGrant =
1680− yield* runtime.storage.executorState.providerAuthGrants.getById(grantId);
1696+ yield* executorStateForRuntime(runtime).providerAuthGrants.getById(grantId);
16811697 assertTrue(Option.isSome(storedGrant));
16821698 expect(storedGrant.value.orphanedAt).toBeNull();
16831699 expect(googleServer.discoveryAuthorizations).toContain(
@@ -1791,7 +1807,7 @@ describe("executor-runtime", () => { · 107 unmodified lines
17911807 const grantId = ProviderAuthGrantIdSchema.make(
17921808 `provider_grant_${crypto.randomUUID()}`,
17931809 );
1794− yield* runtime.storage.executorState.providerAuthGrants.upsert({
1810+ yield* executorStateForRuntime(runtime).providerAuthGrants.upsert({
17951811 id: grantId,
17961812 scopeId: installation.scopeId,
17971813 actorScopeId: installation.actorScopeId,
@@ -1870,7 +1886,7 @@ describe("executor-runtime", () => { · 72 unmodified lines
18701886 ).toBe(true);
18711887
18721888 const storedGrant =
1873− yield* runtime.storage.executorState.providerAuthGrants.getById(grantId);
1889+ yield* executorStateForRuntime(runtime).providerAuthGrants.getById(grantId);
18741890 assertTrue(Option.isSome(storedGrant));
18751891 expect(storedGrant.value.orphanedAt).toBeNull();
18761892 expect(googleServer.tokenRequests.length).toBeGreaterThanOrEqual(1);
@@ -2001,7 +2017,7 @@ describe("executor-runtime", () => { · 124 unmodified lines
20012017 const grantId = ProviderAuthGrantIdSchema.make(
20022018 `provider_grant_${crypto.randomUUID()}`,
20032019 );
2004− yield* runtime.storage.executorState.providerAuthGrants.upsert({
2020+ yield* executorStateForRuntime(runtime).providerAuthGrants.upsert({
20052021 id: grantId,
20062022 scopeId: installation.scopeId,
20072023 actorScopeId: installation.actorScopeId,
@@ -2026,7 +2042,7 @@ describe("executor-runtime", () => { · 18 unmodified lines
20262042 `src_auth_${crypto.randomUUID()}`,
20272043 );
20282044 const state = `provider-state-${crypto.randomUUID()}`;
2029− yield* runtime.storage.executorState.sourceAuthSessions.upsert({
2045+ yield* executorStateForRuntime(runtime).sourceAuthSessions.upsert({
20302046 id: sessionId,
20312047 scopeId: installation.scopeId,
20322048 sourceId: SourceIdSchema.make(
@@ -2097,7 +2113,7 @@ describe("executor-runtime", () => { · 64 unmodified lines
20972113 expect(callbackPage).toContain("Connected 1 source");
20982114
20992115 const storedGrant =
2100− yield* runtime.storage.executorState.providerAuthGrants.getById(grantId);
2116+ yield* executorStateForRuntime(runtime).providerAuthGrants.getById(grantId);
21012117 assertTrue(Option.isSome(storedGrant));
21022118 expect(storedGrant.value.refreshToken).toEqual({
21032119 providerId: "local",
@@ -2155,7 +2171,7 @@ describe("executor-runtime", () => { · 51 unmodified lines
21552171 );
21562172 const refreshSecretId = "sec_google_refresh_orphan";
21572173
2158− yield* runtime.storage.executorState.scopeOauthClients.upsert({
2174+ yield* executorStateForRuntime(runtime).scopeOauthClients.upsert({
21592175 id: oauthClientId,
21602176 scopeId: installation.scopeId,
21612177 providerKey: "google_workspace",
@@ -2177,7 +2193,7 @@ describe("executor-runtime", () => { · 15 unmodified lines
21772193 const grantId = ProviderAuthGrantIdSchema.make(
21782194 `provider_grant_${crypto.randomUUID()}`,
21792195 );
2180− yield* runtime.storage.executorState.providerAuthGrants.upsert({
2196+ yield* executorStateForRuntime(runtime).providerAuthGrants.upsert({
21812197 id: grantId,
21822198 scopeId: installation.scopeId,
21832199 actorScopeId: installation.actorScopeId,
@@ -2228,7 +2244,7 @@ describe("executor-runtime", () => { · 44 unmodified lines
22282244 expect(removed.removed).toBe(true);
22292245
22302246 const storedGrant =
2231− yield* runtime.storage.executorState.providerAuthGrants.getById(grantId);
2247+ yield* executorStateForRuntime(runtime).providerAuthGrants.getById(grantId);
22322248 assertTrue(Option.isSome(storedGrant));
22332249 expect(storedGrant.value.orphanedAt).not.toBeNull();
22342250 }),
@@ -2245,7 +2261,7 @@ describe("executor-runtime", () => { · 10 unmodified lines
22452261 );
22462262 const refreshSecretId = "sec_google_refresh_revoke";
22472263
2248− yield* runtime.storage.executorState.scopeOauthClients.upsert({
2264+ yield* executorStateForRuntime(runtime).scopeOauthClients.upsert({
22492265 id: oauthClientId,
22502266 scopeId: installation.scopeId,
22512267 providerKey: "google_workspace",
@@ -2267,7 +2283,7 @@ describe("executor-runtime", () => { · 15 unmodified lines
22672283 const grantId = ProviderAuthGrantIdSchema.make(
22682284 `provider_grant_${crypto.randomUUID()}`,
22692285 );
2270− yield* runtime.storage.executorState.providerAuthGrants.upsert({
2286+ yield* executorStateForRuntime(runtime).providerAuthGrants.upsert({
22712287 id: grantId,
22722288 scopeId: installation.scopeId,
22732289 actorScopeId: installation.actorScopeId,
@@ -2335,11 +2351,11 @@ describe("executor-runtime", () => { · 61 unmodified lines
23352351 expect(removed.removed).toBe(true);
23362352
23372353 const storedGrant =
2338− yield* runtime.storage.executorState.providerAuthGrants.getById(grantId);
2354+ yield* executorStateForRuntime(runtime).providerAuthGrants.getById(grantId);
23392355 assertTrue(Option.isNone(storedGrant));
23402356
23412357 const storedRefreshSecret =
2342− yield* runtime.storage.executorState.secretMaterials.getById(
2358+ yield* executorStateForRuntime(runtime).secretMaterials.getById(
23432359 SecretMaterialIdSchema.make(refreshSecretId),
23442360 );
23452361 assertTrue(Option.isNone(storedRefreshSecret));
packages/platform/sdk/src/runtime/sources/source-adapters/__fixtures__/mcp-stdio-server.mjs+26−0
@@ -0,0 +1,26 @@
1+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3+import { z } from "zod/v4";
4+
5+const server = new McpServer(
6+ { name: "mcp-stdio-test-server", version: "1.0.0" },
7+ { capabilities: { tools: { listChanged: true } } },
8+);
9+
10+server.registerTool(
11+ "echo_stdio",
12+ {
13+ title: "Echo Stdio",
14+ description: "Echo a value over stdio",
15+ inputSchema: {
16+ value: z.string(),
17+ },
18+ },
19+ async ({ value }) => ({
20+ content: [{ type: "text", text: "stdio:" + value }],
21+ }),
22+);
23+
24+const transport = new StdioServerTransport();
25+await server.connect(transport);
26+setInterval(() => {}, 1 << 30);
packages/platform/sdk/src/runtime/sources/source-adapters/mcp.test.ts+20−38
@@ -1,6 +1,9 @@
11 import {
22 randomUUID,
33 } from "node:crypto";
4+import {
5+ fileURLToPath,
6+} from "node:url";
47
58 import {
69 createMcpExpressApp,
@@ -70,6 +73,19 @@ type RealMcpServer = { · 63 unmodified lines
7073 close: () => Promise<void>;
7174 };
7275
76+const PACKAGE_ROOT = fileURLToPath(new URL("../../../../", import.meta.url));
77+const STDIO_SERVER_SCRIPT_PATH = fileURLToPath(
78+ new URL("./__fixtures__/mcp-stdio-server.mjs", import.meta.url),
79+);
80+const BUN_COMMAND =
81+ process.env.BUN_INSTALL && process.env.BUN_INSTALL.length > 0
82+ ? `${process.env.BUN_INSTALL}/bin/bun`
83+ : "bun";
84+const STDIO_TEST_ENV = {
85+ PATH: process.env.PATH ?? "",
86+ HOME: process.env.HOME ?? "",
87+};
88+
7389 const makeLoadedCatalog = (input: {
7490 source: Source;
7591 snapshot: CatalogSnapshotV1;
@@ -567,40 +583,6 @@ const makeStatefulMcpServer = Effect.acquireRelease( · 491 unmodified lines
567583 }).pipe(Effect.orDie),
568584 );
569585
570−const STDIO_TEST_SERVER_SCRIPT = `
571−import {
572− McpServer,
573−} from "@modelcontextprotocol/sdk/server/mcp.js";
574−import {
575− StdioServerTransport,
576−} from "@modelcontextprotocol/sdk/server/stdio.js";
577−import {
578− z,
579−} from "zod/v4";
580−
581−const server = new McpServer(
582− { name: "mcp-stdio-test-server", version: "1.0.0" },
583− { capabilities: { tools: { listChanged: true } } },
584−);
585−
586−server.registerTool(
587− "echo_stdio",
588− {
589− title: "Echo Stdio",
590− description: "Echo a value over stdio",
591− inputSchema: {
592− value: z.string(),
593− },
594− },
595− async ({ value }) => ({
596− content: [{ type: "text", text: "stdio:" + value }],
597− }),
598−);
599−
600−const transport = new StdioServerTransport();
601−await server.connect(transport);
602−`;
603−
604586 const makeStaticAuthProvider = (accessToken: string): OAuthClientProvider => ({
605587 get redirectUrl() {
606588 return "http://127.0.0.1/oauth/callback";
@@ -1020,10 +1002,10 @@ describe("mcp source adapter", () => { · 413 unmodified lines
10201002 transport: "stdio",
10211003 queryParams: null,
10221004 headers: null,
1023− command: "node",
1024− args: ["--input-type=module", "-e", STDIO_TEST_SERVER_SCRIPT],
1025− env: null,
1026− cwd: process.cwd(),
1005+ command: BUN_COMMAND,
1006+ args: [STDIO_SERVER_SCRIPT_PATH],
1007+ env: STDIO_TEST_ENV,
1008+ cwd: PACKAGE_ROOT,
10271009 },
10281010 importAuthPolicy: "reuse_runtime",
10291011 importAuth: { kind: "none" },
packages/platform/sdk/src/runtime/sources/source-discovery.test.ts+1−1
@@ -13,7 +13,7 @@ import * as Effect from "effect/Effect"; · 12 unmodified lines
1313
1414 import {
1515 discoverSource,
16−} from "./source-discovery";
16+} from "../../sources/discovery";
1717
1818 type TestServer = {
1919 url: string;
packages/platform/sdk/src/runtime/sources/source-inspection.test.ts+1−1
@@ -10,7 +10,7 @@ import type { · 9 unmodified lines
1010 } from "../catalog/source/runtime";
1111 import {
1212 inspectionToolDetailFromTool,
13−} from "./source-inspection";
13+} from "../../sources/inspection";
1414
1515 describe("source inspection", () => {
1616 it.effect("keeps the shared TypeScript contract first-class on tool detail", () =>
packages/sources/mcp/src/adapter.ts+3−1
@@ -462,7 +462,9 @@ export const mcpSourceAdapter = { · 461 unmodified lines
462462 Effect.mapError(
463463 (cause) =>
464464 new Error(
465− `Failed discovering MCP tools for ${source.id}: ${cause.message}`,
465+ `Failed discovering MCP tools for ${source.id}: ${cause.message}${
466+ cause.details ? ` (${cause.details})` : ""
467+ }`,
466468 ),
467469 ),
468470 );
packages/sources/mcp/src/connection.ts+5−27
@@ -1,6 +1,7 @@
11 import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
22 import { Client } from "@modelcontextprotocol/sdk/client/index.js";
33 import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
4+import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
45 import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
56 import * as Data from "effect/Data";
67 import * as Effect from "effect/Effect";
@@ -104,31 +105,6 @@ const closeClient = (client: Client): Effect.Effect<void, never, never> => · 97 unmodified lines
104105 }),
105106 }).pipe(Effect.ignore);
106107
107−type DynamicImport = <TModule>(specifier: string) => Promise<TModule>;
108−
109−const dynamicImport = new Function(
110− "specifier",
111− "return import(specifier)",
112−) as DynamicImport;
113−
114−const loadStdioClientTransport = (): Effect.Effect<
115− typeof import("@modelcontextprotocol/sdk/client/stdio.js").StdioClientTransport,
116− McpConnectionError,
117− never
118−> =>
119− Effect.tryPromise({
120− try: async () =>
121− (await dynamicImport<typeof import("@modelcontextprotocol/sdk/client/stdio.js")>(
122− "@modelcontextprotocol/sdk/client/stdio.js",
123− )).StdioClientTransport,
124− catch: (cause) =>
125− mcpConnectionError({
126− transport: "stdio",
127− message: "Failed loading MCP stdio transport",
128− cause,
129− }),
130− });
131−
132108 const connectClient = (input: {
133109 createClient: () => Client;
134110 transport: McpTransportPreference;
@@ -143,7 +119,10 @@ const connectClient = (input: { · 8 unmodified lines
143119 catch: (cause) =>
144120 mcpConnectionError({
145121 transport: input.transport,
146− message: `Failed connecting to MCP server via ${input.transport}`,
122+ message:
123+ `Failed connecting to MCP server via ${input.transport}: ${
124+ cause instanceof Error ? cause.message : String(cause)
125+ }`,
147126 cause,
148127 }),
149128 }).pipe(
@@ -183,7 +162,6 @@ export const createSdkMcpConnector = ( · 33 unmodified lines
183162 });
184163 }
185164
186− const StdioClientTransport = yield* loadStdioClientTransport();
187165 return yield* connectClient({
188166 createClient,
189167 transport: "stdio",