Skip to content
Merged
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
9 changes: 9 additions & 0 deletions apps/server/src/mcp/OrchestratorMcpService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2512,6 +2512,10 @@ describe("OrchestratorMcpService provider resolution", () => {
{ slug: "claude-opus-4-1", isLegacy: true },
...Array.from({ length: 9 }, (_, index) => ({ slug: `claude-opus-5-${index + 1}` })),
].map((model) => ({ ...model, name: model.slug, isCustom: false, capabilities: null })),
version: "2.1.302",
updateRequiredModels: [
{ slug: "claude-fable-5-2", name: "Claude Fable 5.2", minVersion: "2.1.321" },
],
},
]),
adapterRegistryLayer([codexInstanceId]),
Expand Down Expand Up @@ -2549,6 +2553,11 @@ describe("OrchestratorMcpService provider resolution", () => {
"claude-opus-5-6, claude-opus-5-7, claude-opus-5-8 and 2 more; " +
"orchestrator_capabilities with search finds them.",
);

const updateRequired = yield* refuse("claude-fable-5-2");
assert.equal(updateRequired.code, "model_unavailable");
assert.include(updateRequired.message, "v2.1.302");
assert.include(updateRequired.message, "2.1.321");
}).pipe(Effect.provide(layerOrchestratorService.pipe(Layer.provide(layerDependencies))));
}),
);
Expand Down
28 changes: 19 additions & 9 deletions apps/server/src/mcp/OrchestratorMcpService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,16 +395,26 @@ const advertisedModel = (provider: ServerProvider, model: string) =>
candidate.aliases?.some((alias) => alias.toLowerCase() === model.toLowerCase()),
);

const withV = (version: string) => (version.startsWith("v") ? version : `v${version}`);

/** A provider that lists no models accepts any slug. */
export const requireAdvertisedModel = (provider: ServerProvider, model: string) =>
provider.models.length > 0 && advertisedModel(provider, model) === undefined
? Effect.fail(
failure(
"model_unavailable",
`Model ${model} is not advertised by provider ${provider.instanceId}. ${advertisedModelsHint(provider.models, model)}`,
),
)
: Effect.void;
export const requireAdvertisedModel = (provider: ServerProvider, model: string) => {
if (provider.models.length === 0 || advertisedModel(provider, model) !== undefined) {
return Effect.void;
}
const updateRequired = provider.updateRequiredModels?.find(
(candidate) => candidate.slug === model,
);
const detectedVersion = provider.installedVersion ?? provider.version;
return Effect.fail(
failure(
"model_unavailable",
updateRequired === undefined
? `Model ${model} is not advertised by provider ${provider.instanceId}. ${advertisedModelsHint(provider.models, model)}`
: `Model ${model} needs provider ${provider.instanceId} at ${withV(updateRequired.minVersion)} or newer, but the detected CLI is ${detectedVersion === null ? "an unknown version" : withV(detectedVersion)}. Ask the user to update it or point the provider's binary path at a newer install.`,
),
);
};

function providerConstraints(
provider: ServerProvider | undefined,
Expand Down
Loading