Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions apps/server/src/provider/Layers/ClaudeAdapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,74 @@ describe("ClaudeAdapterLive", () => {
);
});

it.effect("forwards mcpServers from providerOptions to query options", () => {
const harness = makeHarness();
return Effect.gen(function* () {
const adapter = yield* ClaudeAdapter;
const mcpServers = {
filesystem: {
command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
},
};
yield* adapter.startSession({
threadId: THREAD_ID,
provider: "claudeAgent",
runtimeMode: "full-access",
providerOptions: {
claudeAgent: { mcpServers },
},
});

const createInput = harness.getLastCreateQueryInput();
assert.deepStrictEqual(createInput?.options.mcpServers, mcpServers);
}).pipe(
Effect.provideService(Random.Random, makeDeterministicRandomService()),
Effect.provide(harness.layer),
);
});

it.effect("forwards plugins from providerOptions to query options", () => {
const harness = makeHarness();
return Effect.gen(function* () {
const adapter = yield* ClaudeAdapter;
const plugins = [{ type: "local" as const, path: "./my-plugin" }];
yield* adapter.startSession({
threadId: THREAD_ID,
provider: "claudeAgent",
runtimeMode: "full-access",
providerOptions: {
claudeAgent: { plugins },
},
});

const createInput = harness.getLastCreateQueryInput();
assert.deepStrictEqual(createInput?.options.plugins, plugins);
}).pipe(
Effect.provideService(Random.Random, makeDeterministicRandomService()),
Effect.provide(harness.layer),
);
});

it.effect("omits mcpServers and plugins when not provided", () => {
const harness = makeHarness();
return Effect.gen(function* () {
const adapter = yield* ClaudeAdapter;
yield* adapter.startSession({
threadId: THREAD_ID,
provider: "claudeAgent",
runtimeMode: "full-access",
});

const createInput = harness.getLastCreateQueryInput();
assert.equal(createInput?.options.mcpServers, undefined);
assert.equal(createInput?.options.plugins, undefined);
}).pipe(
Effect.provideService(Random.Random, makeDeterministicRandomService()),
Effect.provide(harness.layer),
);
});

it.effect("treats ultrathink as a prompt keyword instead of a session effort", () => {
const harness = makeHarness();
return Effect.gen(function* () {
Expand Down
6 changes: 6 additions & 0 deletions apps/server/src/provider/Layers/ClaudeAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2585,6 +2585,12 @@ function makeClaudeAdapter(options?: ClaudeAdapterLiveOptions) {
env: process.env,
...(input.cwd ? { additionalDirectories: [input.cwd] } : {}),
};
if (providerOptions?.mcpServers) {
(queryOptions as Record<string, unknown>).mcpServers = providerOptions.mcpServers;
}
if (providerOptions?.plugins) {
(queryOptions as Record<string, unknown>).plugins = providerOptions.plugins;
}

const queryRuntime = yield* Effect.try({
try: () =>
Expand Down
2 changes: 2 additions & 0 deletions packages/contracts/src/orchestration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ export const ClaudeProviderStartOptions = Schema.Struct({
binaryPath: Schema.optional(TrimmedNonEmptyString),
permissionMode: Schema.optional(TrimmedNonEmptyString),
maxThinkingTokens: Schema.optional(NonNegativeInt),
mcpServers: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
plugins: Schema.optional(Schema.Array(Schema.Unknown)),
});

export const ProviderStartOptions = Schema.Struct({
Expand Down