diff --git a/apps/server/src/mcp/OrchestratorMcpService.test.ts b/apps/server/src/mcp/OrchestratorMcpService.test.ts index 8df18bb356cf6..53ab5093da7cf 100644 --- a/apps/server/src/mcp/OrchestratorMcpService.test.ts +++ b/apps/server/src/mcp/OrchestratorMcpService.test.ts @@ -1461,6 +1461,65 @@ describe("OrchestratorMcpService provider resolution", () => { skills: [], }); + it.effect.each([ + { + case: "Pi before discovery", + driver: "pi", + auth: "unknown", + discovered: false, + accepted: true, + }, + { + case: "Pi after discovery", + driver: "pi", + auth: "unknown", + discovered: true, + accepted: false, + }, + { + case: "unauthenticated Pi", + driver: "pi", + auth: "unauthenticated", + discovered: false, + accepted: false, + }, + { + case: "another provider", + driver: "muse", + auth: "unknown", + discovered: false, + accepted: false, + }, + ] as const)("validates an explicit model for $case", (row) => + Effect.gen(function* () { + const provider = providerSnapshot({ + instanceId: ProviderInstanceId.make("test"), + driver: ProviderDriverKind.make(row.driver), + model: "default", + }); + const result = yield* OrchestratorMcpService.requireAdvertisedModel( + { + ...provider, + auth: { status: row.auth }, + models: [ + ...provider.models, + row.discovered + ? { + slug: "anthropic/claude-sonnet", + name: "Claude Sonnet", + subProvider: "anthropic", + isCustom: false, + capabilities: null, + } + : { slug: "custom/model", name: "custom/model", isCustom: true, capabilities: null }, + ], + }, + "anthropic/claude-opus", + ).pipe(Effect.result); + assert.equal(Result.isSuccess(result), row.accepted); + }), + ); + const providerRegistryLayer = (providers: ReadonlyArray) => Layer.mock(ProviderRegistry.ProviderRegistry)({ getProviders: Effect.succeed(providers), diff --git a/apps/server/src/mcp/OrchestratorMcpService.ts b/apps/server/src/mcp/OrchestratorMcpService.ts index 0bbef0d92a606..c6238a20c4b2c 100644 --- a/apps/server/src/mcp/OrchestratorMcpService.ts +++ b/apps/server/src/mcp/OrchestratorMcpService.ts @@ -395,9 +395,15 @@ const advertisedModel = (provider: ServerProvider, model: string) => candidate.aliases?.some((alias) => alias.toLowerCase() === model.toLowerCase()), ); -/** A provider that lists no models accepts any slug. */ +/** Empty inventories and Pi's undiscovered placeholder inventory accept any slug. */ export const requireAdvertisedModel = (provider: ServerProvider, model: string) => - provider.models.length > 0 && advertisedModel(provider, model) === undefined + provider.models.length > 0 && + (provider.driver !== "pi" || + provider.auth.status !== "unknown" || + provider.models.some( + (candidate) => !candidate.isCustom && candidate.subProvider !== undefined, + )) && + advertisedModel(provider, model) === undefined ? Effect.fail( failure( "model_unavailable",