// Cloud: `connections.list` over the MCP surface returns a LEAN projection. A // single OAuth connection's `oauthScope` grant string runs to thousands of // characters for a real provider, and an agent listing connections to pick one // pays that cost on every row. So the core tool summarizes scope to an // `oauthScopeCount` by default and only echoes the full grant string when the // caller asks for `verbose: true`. // // Proven end to end: a real authorization-code flow (start → consent → // complete) mints a connection carrying a recorded `read` scope, then the core // tool is driven through `execute` exactly as an MCP client (Claude, Cursor, …) // would call it. The HTTP API projection is deliberately untouched — the web // console reads the full `oauthScope` to flag reconnect-for-new-scope — so this // only pins the agent-facing tool. import { randomBytes } from "node:crypto"; import { expect } from "@effect/vitest"; import { Effect } from "effect"; import { composePluginApi } from "@executor-js/api/server"; import { openApiHttpPlugin } from "@executor-js/plugin-openapi/api"; import { AuthTemplateSlug, ConnectionName, IntegrationSlug, OAuthClientSlug, } from "@executor-js/sdk/shared"; import { serveOAuthTestServer } from "@executor-js/sdk/testing"; import { scenario } from "../src/scenario"; import { Api, Mcp, Target } from "../src/services"; import type { McpSession } from "../src/surfaces/mcp"; const api = composePluginApi([openApiHttpPlugin()] as const); const unique = (prefix: string) => `${prefix}_${randomBytes(4).toString("hex")}`; /** Run `execute` and parse the sandbox's JSON return value. */ const executeJson = (session: McpSession, code: string) => Effect.gen(function* () { const result = yield* session.call("execute", { code }); expect(result.ok, `execute completed (got: ${result.text.slice(0, 400)})`).toBe(true); return JSON.parse(result.text) as Record; }); /** The `main` connection from a `connections.list` sandbox return value. */ const mainConnection = (listed: Record): Record | undefined => { const connections = (listed as { connections?: ReadonlyArray> }) .connections; return connections?.find((c) => c.name === "main"); }; const listCode = (integration: string, verbose: boolean) => ` const res = await tools.executor.coreTools.connections.list(${JSON.stringify( verbose ? { integration, verbose: true } : { integration }, )}); return res.ok ? res.data : res; `; scenario( "Connections · list summarizes OAuth scope to a count by default and returns the full grant under verbose", { timeout: 240_000 }, Effect.scoped( Effect.gen(function* () { const target = yield* Target; const mcp = yield* Mcp; const { client: makeClient } = yield* Api; const oauth = yield* serveOAuthTestServer({ scopes: ["read"] }); const identity = yield* target.newIdentity(); const client = yield* makeClient(api, identity); const session = mcp.session(identity); // An integration declaring an oauth template — the minted connection // attaches to it and records the granted `read` scope. const integration = IntegrationSlug.make(unique("connscopeint")); yield* client.openapi.addSpec({ payload: { spec: { kind: "blob", value: JSON.stringify({ openapi: "3.0.3", info: { title: "OAuth-protected API", version: "1.0.0" }, paths: { "/me": { get: { operationId: "getMe", tags: ["default"], responses: { "200": { description: "the caller" } }, }, }, }, }), }, slug: integration, baseUrl: "http://127.0.0.1:59999", authenticationTemplate: [ { slug: "oauth", kind: "oauth2", authorizationUrl: oauth.authorizationEndpoint, tokenUrl: oauth.tokenEndpoint, scopes: ["read"], }, ], }, }); const clientSlug = OAuthClientSlug.make(unique("connscopec")); yield* client.oauth.createClient({ payload: { owner: "org", slug: clientSlug, authorizationUrl: oauth.authorizationEndpoint, tokenUrl: oauth.tokenEndpoint, grant: "authorization_code", clientId: "test-client", clientSecret: "test-secret", }, }); const started = yield* client.oauth.start({ payload: { client: clientSlug, clientOwner: "org", owner: "org", name: ConnectionName.make("main"), integration, template: AuthTemplateSlug.make("oauth"), }, }); if (started.status !== "redirect") { throw new Error(`oauth.start did not redirect: ${JSON.stringify(started)}`); } // Headless consent: the authorize page bounces to login, and submitting // credentials redirects back to the product callback with a code. const authorize = yield* Effect.promise(() => fetch(started.authorizationUrl, { redirect: "manual" }), ); const consent = yield* Effect.promise(() => fetch(authorize.headers.get("location") ?? "", { method: "POST", redirect: "manual", headers: { authorization: `Basic ${Buffer.from("alice:password").toString("base64")}`, }, }), ); const callback = new URL(consent.headers.get("location") ?? ""); const code = callback.searchParams.get("code") ?? ""; yield* client.oauth.complete({ payload: { state: started.state, code } }); // Default (lean): the full scope string is omitted; a grant count stands // in so a scanning agent still knows scope exists and how much. const lean = yield* executeJson(session, listCode(String(integration), false)); const leanMain = mainConnection(lean); expect(leanMain, "the minted connection is listed").toBeDefined(); expect(leanMain?.oauthScopeCount, "scope is summarized to its grant count").toBe(1); expect( "oauthScope" in (leanMain ?? {}), "the full scope grant string is omitted by default", ).toBe(false); // verbose: the full grant string is included alongside the count. const verbose = yield* executeJson(session, listCode(String(integration), true)); const verboseMain = mainConnection(verbose); expect(verboseMain?.oauthScope, "verbose returns the full grant string").toBe("read"); expect(verboseMain?.oauthScopeCount, "the count still accompanies the full scope").toBe(1); }), ), );