// Large catalogs stay server-side and remain searchable within the connect timeout.import { expect } from "@effect/vitest";import { Effect } from "effect"; import { decodeToolSearch } from "../scenarios/support/search-invoke"; import { scenario } from "../src/scenario";import { Api, Mcp, Target } from "../src/services";import { catalogApi, seedLargeCatalog } from "../scenarios/support/large-catalog"; // Catalog loading and the two-tool handshake must complete within 20 seconds.const MAX_PASSTHROUGH_CONNECT_MS = 20_000; scenario( "Passthrough · a production-shaped catalog is served completely, in bounded time", { timeout: 300_000 }, Effect.scoped( Effect.gen(function* () { const target = yield* Target; const mcp = yield* Mcp; const { client: makeClient } = yield* Api; const identity = yield* target.newIdentity(); const client = yield* makeClient(catalogApi, identity); const seeded = yield* seedLargeCatalog(client); yield* Effect.ensuring( Effect.gen(function* () { // What the caller can see through the typed API is the ground truth // search results must cover completely — minus the plugins' // static configuration tools (`executor.*`, `openapi.addSpec`, …), // which are codemode affordances and deliberately not served here. const visible = (yield* client.tools.list({ query: {} })).filter( (tool) => tool.static !== true, ); expect(visible.length, "the seeded catalog is large").toBeGreaterThan(3000); const session = mcp.session(identity, { mode: "passthrough", artifacts: false }); const startedAt = Date.now(); const served = yield* session.describeTools(); const elapsedMs = Date.now() - startedAt; expect( elapsedMs, `a ${visible.length}-tool passthrough connect stays bounded (took ${elapsedMs}ms)`, ).toBeLessThan(MAX_PASSTHROUGH_CONNECT_MS); expect(served.map((tool) => tool.name).sort()).toEqual([ "integrations", "invoke", "search", "skills", ]); const first = decodeToolSearch( (yield* session.call("search", { query: "org", limit: 20 })).raw, ).structuredContent; expect(first.total).toBe(visible.length); expect(first.items).toHaveLength(20); expect(first.nextOffset).toBe(20); const last = decodeToolSearch( (yield* session.call("search", { query: "org", offset: visible.length - 1 })).raw, ).structuredContent; expect(last.items).toHaveLength(1); expect(last.hasMore).toBe(false); expect(last.nextOffset).toBeNull(); for (const slug of seeded.integrationSlugs) { const found = decodeToolSearch( (yield* session.call("search", { query: slug })).raw, ).structuredContent; expect( found.items.some((tool) => tool.integration === slug), `${slug} is discoverable`, ).toBe(true); } }), seeded.cleanup, ); }), ),);
// Large catalogs stay server-side and remain searchable within the connect timeout.
import { expect } from "@effect/vitest";
import { Effect } from "effect";
import { decodeToolSearch } from "../scenarios/support/search-invoke";
import { scenario } from "../src/scenario";
import { Api, Mcp, Target } from "../src/services";
import { catalogApi, seedLargeCatalog } from "../scenarios/support/large-catalog";
// Catalog loading and the two-tool handshake must complete within 20 seconds.
const MAX_PASSTHROUGH_CONNECT_MS = 20_000;
scenario(
"Passthrough · a production-shaped catalog is served completely, in bounded time",
{ timeout: 300_000 },
Effect.scoped(
Effect.gen(function* () {
const target = yield* Target;
const mcp = yield* Mcp;
const { client: makeClient } = yield* Api;
const identity = yield* target.newIdentity();
const client = yield* makeClient(catalogApi, identity);
const seeded = yield* seedLargeCatalog(client);
yield* Effect.ensuring(
Effect.gen(function* () {
// What the caller can see through the typed API is the ground truth
// search results must cover completely — minus the plugins'
// static configuration tools (`executor.*`, `openapi.addSpec`, …),
// which are codemode affordances and deliberately not served here.
const visible = (yield* client.tools.list({ query: {} })).filter(
(tool) => tool.static !== true,
);
expect(visible.length, "the seeded catalog is large").toBeGreaterThan(3000);
const session = mcp.session(identity, { mode: "passthrough", artifacts: false });
const startedAt = Date.now();
const served = yield* session.describeTools();
const elapsedMs = Date.now() - startedAt;
expect(
elapsedMs,
`a ${visible.length}-tool passthrough connect stays bounded (took ${elapsedMs}ms)`,
).toBeLessThan(MAX_PASSTHROUGH_CONNECT_MS);
expect(served.map((tool) => tool.name).sort()).toEqual([
"integrations",
"invoke",
"search",
"skills",
]);
const first = decodeToolSearch(
(yield* session.call("search", { query: "org", limit: 20 })).raw,
).structuredContent;
expect(first.total).toBe(visible.length);
expect(first.items).toHaveLength(20);
expect(first.nextOffset).toBe(20);
const last = decodeToolSearch(
(yield* session.call("search", { query: "org", offset: visible.length - 1 })).raw,
).structuredContent;
expect(last.items).toHaveLength(1);
expect(last.hasMore).toBe(false);
expect(last.nextOffset).toBeNull();
for (const slug of seeded.integrationSlugs) {
const found = decodeToolSearch(
(yield* session.call("search", { query: slug })).raw,
).structuredContent;
expect(
found.items.some((tool) => tool.integration === slug),
`${slug} is discoverable`,
).toBe(true);
}
}),
seeded.cleanup,
);
}),
),
);