// ---------------------------------------------------------------------------// core-tools plugin//// Built-in plugin that contributes agent-facing static tools for configuring// executor-level primitives over the v2 surface. Agent-facing connection setup// should hand users to the web UI for pasted credentials; low-level creation// through this plugin only accepts provider refs.// --------------------------------------------------------------------------- import { Effect, Schema } from "effect"; import type { Connection, ConnectionInputOrigin, CreateConnectionInput } from "./connection";import { AuthTemplateSlug, ConnectionName, IntegrationSlug, NO_AUTH_TEMPLATE, OAuthClientSlug, OAuthState, ProviderItemId, ProviderKey, type Owner,} from "./ids";import { definePlugin, tool, type StaticToolSchema } from "./plugin";import { ToolPolicyActionSchema } from "./policies";import type { Tool } from "./tool"; const schemaToStandard = <A, I>(schema: Schema.Decoder<A, I>): StaticToolSchema<A, I> => Schema.toStandardSchemaV1(Schema.toStandardJSONSchemaV1(schema) as never) as StaticToolSchema< A, I >; const OwnerSchema = Schema.Literals(["org", "user"]);const OAuthGrantSchema = Schema.Literals(["authorization_code", "client_credentials"]); // ---------------------------------------------------------------------------// Schemas// --------------------------------------------------------------------------- const IntegrationOutput = Schema.Struct({ slug: Schema.String, description: Schema.String, kind: Schema.String, canRemove: Schema.Boolean, canRefresh: Schema.Boolean,}); const IntegrationsListOutput = Schema.Struct({ integrations: Schema.Array(IntegrationOutput),}); const DetectInput = Schema.Struct({ url: Schema.String });const DetectOutput = Schema.Struct({ results: Schema.Array( Schema.Struct({ kind: Schema.String, confidence: Schema.Literals(["high", "medium", "low"]), endpoint: Schema.String, name: Schema.String, slug: Schema.String, }), ),}); const ConnectionOutput = Schema.Struct({ owner: OwnerSchema, name: Schema.String, integration: Schema.String, template: Schema.String, provider: Schema.String, address: Schema.String, identityLabel: Schema.optional(Schema.NullOr(Schema.String)), description: Schema.optional(Schema.NullOr(Schema.String)), expiresAt: Schema.NullOr(Schema.Number), oauthClient: Schema.NullOr(Schema.String), oauthClientOwner: Schema.NullOr(OwnerSchema), oauthScope: Schema.NullOr(Schema.String),}); const ConnectionsListInput = Schema.Struct({ integration: Schema.optional(Schema.String), owner: Schema.optional(OwnerSchema), verbose: Schema.optional(Schema.Boolean),}); /** Lean per-connection shape for list scans. Omits the full `oauthScope` * grant string (a single connection's scope list can run to thousands of * characters and dominates the payload) in favor of `oauthScopeCount`. The * full scope is included only when the caller passes `verbose: true`. */const ConnectionListItem = Schema.Struct({ owner: OwnerSchema, name: Schema.String, integration: Schema.String, template: Schema.String, provider: Schema.String, address: Schema.String, identityLabel: Schema.optional(Schema.NullOr(Schema.String)), description: Schema.optional(Schema.NullOr(Schema.String)), expiresAt: Schema.NullOr(Schema.Number), oauthClient: Schema.NullOr(Schema.String), oauthClientOwner: Schema.NullOr(OwnerSchema), oauthScopeCount: Schema.NullOr(Schema.Number), oauthScope: Schema.optional(Schema.NullOr(Schema.String)),});const ConnectionsListOutput = Schema.Struct({ connections: Schema.Array(ConnectionListItem),}); const ConnectionCreateHandoffInput = Schema.Struct({ integration: Schema.String, owner: Schema.optional(OwnerSchema), template: Schema.optional(Schema.String), label: Schema.optional(Schema.String),});const ConnectionCreateHandoffOutput = Schema.Struct({ url: Schema.String, instructions: Schema.String,}); const ConnectionFromInput = Schema.Struct({ provider: Schema.String, id: Schema.String,});const ConnectionInputOriginInput = Schema.Struct({ from: ConnectionFromInput });const ConnectionCreateInput = Schema.Struct({ owner: OwnerSchema, name: Schema.String, integration: Schema.String, template: Schema.String, identityLabel: Schema.optional(Schema.NullOr(Schema.String)), from: Schema.optional(ConnectionFromInput), inputs: Schema.optional(Schema.Record(Schema.String, ConnectionInputOriginInput)),}).check( Schema.makeFilter((payload) => { const originCount = (payload.from === undefined ? 0 : 1) + (payload.inputs === undefined ? 0 : 1); // The no-auth template ("none") binds zero credentials — both `from` and // `inputs` are legitimately absent (public MCP servers, public REST APIs). // Mirror the engine, which accepts an empty input set only for this // template; a stray origin would wire a credential the connection can't // hold, so reject any. Every other template needs exactly one origin. const isNoAuth = String(payload.template) === String(NO_AUTH_TEMPLATE); if (isNoAuth) { if (originCount > 0) { return 'A no-auth connection (template "none") takes no provider credential origin'; } } else if (originCount !== 1) { return "Expected exactly one provider credential origin"; } if (payload.inputs !== undefined && Object.keys(payload.inputs).length === 0) { return "Expected at least one provider credential input"; } return undefined; }),);const ConnectionRefInput = Schema.Struct({ owner: OwnerSchema, name: Schema.String, integration: Schema.String,}); const ToolOutput = Schema.Struct({ address: Schema.String, owner: OwnerSchema, integration: Schema.String, connection: Schema.String, name: Schema.String, pluginId: Schema.String, description: Schema.String,});const ConnectionsRefreshOutput = Schema.Struct({ tools: Schema.Array(ToolOutput),}); const RemovedOutput = Schema.Struct({ removed: Schema.Boolean });const CancelledOutput = Schema.Struct({ cancelled: Schema.Boolean }); const ProvidersOutput = Schema.Struct({ providers: Schema.Array(Schema.String),}); const ProviderItemsInput = Schema.Struct({ provider: Schema.String });const ProviderItemsOutput = Schema.Struct({ items: Schema.Array(Schema.Struct({ id: Schema.String, name: Schema.String })),}); const PolicyOutput = Schema.Struct({ id: Schema.String, owner: OwnerSchema, pattern: Schema.String, action: Schema.String, position: Schema.String,});const PoliciesListOutput = Schema.Struct({ policies: Schema.Array(PolicyOutput),}); const PolicyCreateInput = Schema.Struct({ owner: OwnerSchema, pattern: Schema.String, action: ToolPolicyActionSchema,});const PolicyUpdateInput = Schema.Struct({ id: Schema.String, owner: OwnerSchema, pattern: Schema.optional(Schema.String), action: Schema.optional(ToolPolicyActionSchema),});const PolicyRemoveInput = Schema.Struct({ id: Schema.String, owner: OwnerSchema,}); const OAuthClientOutput = Schema.Struct({ owner: OwnerSchema, slug: Schema.String, grant: OAuthGrantSchema, authorizationUrl: Schema.String, tokenUrl: Schema.String, resource: Schema.optional(Schema.NullOr(Schema.String)), clientId: Schema.String, origin: Schema.Union([ Schema.Struct({ kind: Schema.Literal("manual") }), Schema.Struct({ kind: Schema.Literal("dynamic_client_registration"), integration: Schema.optional(Schema.NullOr(Schema.String)), }), ]),});const OAuthClientsListOutput = Schema.Struct({ clients: Schema.Array(OAuthClientOutput),});// No `clientSecret`: a confidential client's secret must never cross the agent// boundary (it would land in the LLM context window). This tool registers a// PUBLIC client only; a secret-bearing app is registered by the human through// `oauth.clients.createHandoff`, which deep-links them to the web form.const OAuthCreateClientInput = Schema.Struct({ owner: OwnerSchema, slug: Schema.String, authorizationUrl: Schema.String, tokenUrl: Schema.String, grant: OAuthGrantSchema, clientId: Schema.String, resource: Schema.optional(Schema.NullOr(Schema.String)), /** Integration whose connect dialog registered this manual app. Recorded so * the picker can match it by intent (exact) instead of by root domain. */ originIntegration: Schema.optional(Schema.NullOr(Schema.String)),});// Browser-handoff for a CONFIDENTIAL OAuth app: carries only the NON-secret// fields the form pre-fills. The client secret is typed by the human in the web// UI, exactly like a pasted connection credential, so it never reaches the// agent. Mirrors `ConnectionCreateHandoffInput`.const OAuthCreateClientHandoffInput = Schema.Struct({ integration: Schema.String, owner: Schema.optional(OwnerSchema), slug: Schema.optional(Schema.String), grant: Schema.optional(OAuthGrantSchema), clientId: Schema.optional(Schema.String), authorizationUrl: Schema.optional(Schema.String), tokenUrl: Schema.optional(Schema.String), resource: Schema.optional(Schema.NullOr(Schema.String)), label: Schema.optional(Schema.String),});const OAuthCreateClientHandoffOutput = Schema.Struct({ url: Schema.String, instructions: Schema.String,});const OAuthClientOutputRef = Schema.Struct({ client: Schema.String,});const OAuthRegisterDynamicInput = Schema.Struct({ owner: OwnerSchema, slug: Schema.String, issuer: Schema.optional(Schema.NullOr(Schema.String)), registrationEndpoint: Schema.String, authorizationUrl: Schema.String, tokenUrl: Schema.String, resource: Schema.optional(Schema.NullOr(Schema.String)), scopes: Schema.Array(Schema.String), tokenEndpointAuthMethodsSupported: Schema.optional(Schema.Array(Schema.String)), clientName: Schema.optional(Schema.String), redirectUri: Schema.optional(Schema.NullOr(Schema.String)), originIntegration: Schema.optional(Schema.NullOr(Schema.String)),});const OAuthRemoveClientInput = Schema.Struct({ owner: OwnerSchema, slug: Schema.String,});const OAuthProbeInput = Schema.Struct({ url: Schema.String,});const OAuthProbeOutput = Schema.Struct({ issuer: Schema.optional(Schema.NullOr(Schema.String)), authorizationUrl: Schema.String, tokenUrl: Schema.String, resource: Schema.optional(Schema.NullOr(Schema.String)), scopesSupported: Schema.optional(Schema.Array(Schema.String)), registrationEndpoint: Schema.optional(Schema.NullOr(Schema.String)), tokenEndpointAuthMethodsSupported: Schema.optional(Schema.Array(Schema.String)), clientIdMetadataDocumentSupported: Schema.optional(Schema.Boolean),});const OAuthStartInput = Schema.Struct({ client: Schema.String, clientOwner: OwnerSchema, owner: OwnerSchema, name: Schema.String, integration: Schema.String, template: Schema.String, identityLabel: Schema.optional(Schema.NullOr(Schema.String)), redirectUri: Schema.optional(Schema.NullOr(Schema.String)),});const OAuthStartOutput = Schema.Union([ Schema.Struct({ status: Schema.Literal("connected"), connection: ConnectionOutput, }), Schema.Struct({ status: Schema.Literal("redirect"), authorizationUrl: Schema.String, state: Schema.String, }),]);const OAuthCancelInput = Schema.Struct({ state: Schema.String,}); // Standard-schema versions for the tool() builder.const IntegrationsListOutputStd = schemaToStandard(IntegrationsListOutput);const DetectInputStd = schemaToStandard(DetectInput);const DetectOutputStd = schemaToStandard(DetectOutput);const ConnectionsListInputStd = schemaToStandard(ConnectionsListInput);const ConnectionsListOutputStd = schemaToStandard(ConnectionsListOutput);const ConnectionCreateHandoffInputStd = schemaToStandard(ConnectionCreateHandoffInput);const ConnectionCreateHandoffOutputStd = schemaToStandard(ConnectionCreateHandoffOutput);const ConnectionCreateInputStd = schemaToStandard(ConnectionCreateInput);const ConnectionOutputStd = schemaToStandard(ConnectionOutput);const ConnectionRefInputStd = schemaToStandard(ConnectionRefInput);const ConnectionsRefreshOutputStd = schemaToStandard(ConnectionsRefreshOutput);const RemovedOutputStd = schemaToStandard(RemovedOutput);const CancelledOutputStd = schemaToStandard(CancelledOutput);const ProvidersOutputStd = schemaToStandard(ProvidersOutput);const ProviderItemsInputStd = schemaToStandard(ProviderItemsInput);const ProviderItemsOutputStd = schemaToStandard(ProviderItemsOutput);const PoliciesListOutputStd = schemaToStandard(PoliciesListOutput);const PolicyOutputStd = schemaToStandard(PolicyOutput);const PolicyCreateInputStd = schemaToStandard(PolicyCreateInput);const PolicyUpdateInputStd = schemaToStandard(PolicyUpdateInput);const PolicyRemoveInputStd = schemaToStandard(PolicyRemoveInput);const OAuthClientsListOutputStd = schemaToStandard(OAuthClientsListOutput);const OAuthCreateClientInputStd = schemaToStandard(OAuthCreateClientInput);const OAuthCreateClientHandoffInputStd = schemaToStandard(OAuthCreateClientHandoffInput);const OAuthCreateClientHandoffOutputStd = schemaToStandard(OAuthCreateClientHandoffOutput);const OAuthClientOutputRefStd = schemaToStandard(OAuthClientOutputRef);const OAuthRegisterDynamicInputStd = schemaToStandard(OAuthRegisterDynamicInput);const OAuthRemoveClientInputStd = schemaToStandard(OAuthRemoveClientInput);const OAuthProbeInputStd = schemaToStandard(OAuthProbeInput);const OAuthProbeOutputStd = schemaToStandard(OAuthProbeOutput);const OAuthStartInputStd = schemaToStandard(OAuthStartInput);const OAuthStartOutputStd = schemaToStandard(OAuthStartOutput);const OAuthCancelInputStd = schemaToStandard(OAuthCancelInput); const connectionToOutput = (connection: Connection) => ({ owner: connection.owner, name: String(connection.name), integration: String(connection.integration), template: String(connection.template), provider: String(connection.provider), address: String(connection.address), identityLabel: connection.identityLabel ?? null, description: connection.description ?? null, expiresAt: connection.expiresAt ?? null, oauthClient: connection.oauthClient == null ? null : String(connection.oauthClient), oauthClientOwner: connection.oauthClientOwner ?? null, oauthScope: connection.oauthScope ?? null,}); /** Number of space-separated grants in an `oauthScope` string, or null when * the connection carries no scope (static credentials, or an OAuth AS that * omitted scope). */const oauthScopeCount = (scope: string | null | undefined): number | null => scope == null ? null : scope.split(/\s+/).filter(Boolean).length; /** Lean projection for `connections.list`. Summarizes `oauthScope` to a count * unless `verbose`, where the full grant string is included too. */const connectionToListItem = (connection: Connection, verbose: boolean) => ({ owner: connection.owner, name: String(connection.name), integration: String(connection.integration), template: String(connection.template), provider: String(connection.provider), address: String(connection.address), identityLabel: connection.identityLabel ?? null, description: connection.description ?? null, expiresAt: connection.expiresAt ?? null, oauthClient: connection.oauthClient == null ? null : String(connection.oauthClient), oauthClientOwner: connection.oauthClientOwner ?? null, oauthScopeCount: oauthScopeCount(connection.oauthScope), ...(verbose ? { oauthScope: connection.oauthScope ?? null } : {}),}); const toolToOutput = (toolRow: Tool) => ({ address: String(toolRow.address), owner: toolRow.owner, integration: String(toolRow.integration), connection: String(toolRow.connection), name: String(toolRow.name), pluginId: toolRow.pluginId, description: toolRow.description,}); const connectionRefFromInput = (input: typeof ConnectionRefInput.Type) => ({ owner: input.owner as Owner, integration: IntegrationSlug.make(input.integration), name: ConnectionName.make(input.name),}); const originFromInput = ( origin: typeof ConnectionInputOriginInput.Type,): ConnectionInputOrigin => ({ from: { provider: ProviderKey.make(origin.from.provider), id: ProviderItemId.make(origin.from.id), },}); const createConnectionInputFromTool = ( input: typeof ConnectionCreateInput.Type,): CreateConnectionInput => { const base = { owner: input.owner as Owner, name: ConnectionName.make(input.name), integration: IntegrationSlug.make(input.integration), template: AuthTemplateSlug.make(input.template), identityLabel: input.identityLabel ?? null, }; if (input.from !== undefined) { return { ...base, from: { provider: ProviderKey.make(input.from.provider), id: ProviderItemId.make(input.from.id), }, }; } return { ...base, inputs: Object.fromEntries( Object.entries(input.inputs ?? {}).map(([variable, origin]) => [ variable, originFromInput(origin), ]), ), };}; const connectionCreateHandoffUrl = ( webBaseUrl: string | undefined, orgSlug: string | undefined, input: typeof ConnectionCreateHandoffInput.Type,): string => { const search = new URLSearchParams({ addAccount: "1" }); if (input.owner !== undefined) search.set("owner", input.owner); if (input.template !== undefined) search.set("template", input.template); if (input.label !== undefined) search.set("label", input.label); // Org-scoped hosts (cloud, self-host, cloudflare) serve the console under an // optional `/<org-slug>` segment. Pin the URL to the executor's bound org so // it opens that org directly instead of relying on the browser's last-active // org (which `OrgSlugGate` would otherwise canonicalize a bare URL to). When // no slug is known (CLI, local, non-request callers) we emit the bare path. const orgPrefix = orgSlug !== undefined && orgSlug.length > 0 ? `/${orgSlug}` : ""; const path = `${orgPrefix}/integrations/${encodeURIComponent(input.integration)}?${search.toString()}`; if (webBaseUrl === undefined || webBaseUrl.length === 0) return path; return new URL(path, webBaseUrl.endsWith("/") ? webBaseUrl : `${webBaseUrl}/`).toString();}; const oauthClientCreateHandoffUrl = ( webBaseUrl: string | undefined, orgSlug: string | undefined, input: typeof OAuthCreateClientHandoffInput.Type,): string => { // `oauthClient=1` flips the integration's Add-account flow straight into the // Register-OAuth-app form; the rest pre-fill its NON-secret fields. The client // secret is deliberately absent: the human types it in the browser, so it is // never placed in this URL (nor in the agent's context). Same builder shape as // `connectionCreateHandoffUrl`. const search = new URLSearchParams({ addAccount: "1", oauthClient: "1" }); if (input.owner !== undefined) search.set("owner", input.owner); if (input.slug !== undefined) search.set("clientSlug", input.slug); if (input.grant !== undefined) search.set("grant", input.grant); if (input.clientId !== undefined) search.set("clientId", input.clientId); if (input.authorizationUrl !== undefined) search.set("authorizationUrl", input.authorizationUrl); if (input.tokenUrl !== undefined) search.set("tokenUrl", input.tokenUrl); if (input.resource != null && input.resource.length > 0) search.set("resource", input.resource); if (input.label !== undefined) search.set("label", input.label); const orgPrefix = orgSlug !== undefined && orgSlug.length > 0 ? `/${orgSlug}` : ""; const path = `${orgPrefix}/integrations/${encodeURIComponent(input.integration)}?${search.toString()}`; if (webBaseUrl === undefined || webBaseUrl.length === 0) return path; return new URL(path, webBaseUrl.endsWith("/") ? webBaseUrl : `${webBaseUrl}/`).toString();Lines 1–500 of 953. Load more
// --------------------------------------------------------------------------- // core-tools plugin // // Built-in plugin that contributes agent-facing static tools for configuring // executor-level primitives over the v2 surface. Agent-facing connection setup // should hand users to the web UI for pasted credentials; low-level creation // through this plugin only accepts provider refs. // --------------------------------------------------------------------------- import { Effect, Schema } from "effect"; import type { Connection, ConnectionInputOrigin, CreateConnectionInput } from "./connection"; import { AuthTemplateSlug, ConnectionName, IntegrationSlug, NO_AUTH_TEMPLATE, OAuthClientSlug, OAuthState, ProviderItemId, ProviderKey, type Owner, } from "./ids"; import { definePlugin, tool, type StaticToolSchema } from "./plugin"; import { ToolPolicyActionSchema } from "./policies"; import type { Tool } from "./tool"; const schemaToStandard = <A, I>(schema: Schema.Decoder<A, I>): StaticToolSchema<A, I> => Schema.toStandardSchemaV1(Schema.toStandardJSONSchemaV1(schema) as never) as StaticToolSchema< A, I >; const OwnerSchema = Schema.Literals(["org", "user"]); const OAuthGrantSchema = Schema.Literals(["authorization_code", "client_credentials"]); // --------------------------------------------------------------------------- // Schemas // --------------------------------------------------------------------------- const IntegrationOutput = Schema.Struct({ slug: Schema.String, description: Schema.String, kind: Schema.String, canRemove: Schema.Boolean, canRefresh: Schema.Boolean, }); const IntegrationsListOutput = Schema.Struct({ integrations: Schema.Array(IntegrationOutput), }); const DetectInput = Schema.Struct({ url: Schema.String }); const DetectOutput = Schema.Struct({ results: Schema.Array( Schema.Struct({ kind: Schema.String, confidence: Schema.Literals(["high", "medium", "low"]), endpoint: Schema.String, name: Schema.String, slug: Schema.String, }), ), }); const ConnectionOutput = Schema.Struct({ owner: OwnerSchema, name: Schema.String, integration: Schema.String, template: Schema.String, provider: Schema.String, address: Schema.String, identityLabel: Schema.optional(Schema.NullOr(Schema.String)), description: Schema.optional(Schema.NullOr(Schema.String)), expiresAt: Schema.NullOr(Schema.Number), oauthClient: Schema.NullOr(Schema.String), oauthClientOwner: Schema.NullOr(OwnerSchema), oauthScope: Schema.NullOr(Schema.String), }); const ConnectionsListInput = Schema.Struct({ integration: Schema.optional(Schema.String), owner: Schema.optional(OwnerSchema), verbose: Schema.optional(Schema.Boolean), }); /** Lean per-connection shape for list scans. Omits the full `oauthScope` * grant string (a single connection's scope list can run to thousands of * characters and dominates the payload) in favor of `oauthScopeCount`. The * full scope is included only when the caller passes `verbose: true`. */ const ConnectionListItem = Schema.Struct({ owner: OwnerSchema, name: Schema.String, integration: Schema.String, template: Schema.String, provider: Schema.String, address: Schema.String, identityLabel: Schema.optional(Schema.NullOr(Schema.String)), description: Schema.optional(Schema.NullOr(Schema.String)), expiresAt: Schema.NullOr(Schema.Number), oauthClient: Schema.NullOr(Schema.String), oauthClientOwner: Schema.NullOr(OwnerSchema), oauthScopeCount: Schema.NullOr(Schema.Number), oauthScope: Schema.optional(Schema.NullOr(Schema.String)), }); const ConnectionsListOutput = Schema.Struct({ connections: Schema.Array(ConnectionListItem), }); const ConnectionCreateHandoffInput = Schema.Struct({ integration: Schema.String, owner: Schema.optional(OwnerSchema), template: Schema.optional(Schema.String), label: Schema.optional(Schema.String), }); const ConnectionCreateHandoffOutput = Schema.Struct({ url: Schema.String, instructions: Schema.String, }); const ConnectionFromInput = Schema.Struct({ provider: Schema.String, id: Schema.String, }); const ConnectionInputOriginInput = Schema.Struct({ from: ConnectionFromInput }); const ConnectionCreateInput = Schema.Struct({ owner: OwnerSchema, name: Schema.String, integration: Schema.String, template: Schema.String, identityLabel: Schema.optional(Schema.NullOr(Schema.String)), from: Schema.optional(ConnectionFromInput), inputs: Schema.optional(Schema.Record(Schema.String, ConnectionInputOriginInput)), }).check( Schema.makeFilter((payload) => { const originCount = (payload.from === undefined ? 0 : 1) + (payload.inputs === undefined ? 0 : 1); // The no-auth template ("none") binds zero credentials — both `from` and // `inputs` are legitimately absent (public MCP servers, public REST APIs). // Mirror the engine, which accepts an empty input set only for this // template; a stray origin would wire a credential the connection can't // hold, so reject any. Every other template needs exactly one origin. const isNoAuth = String(payload.template) === String(NO_AUTH_TEMPLATE); if (isNoAuth) { if (originCount > 0) { return 'A no-auth connection (template "none") takes no provider credential origin'; } } else if (originCount !== 1) { return "Expected exactly one provider credential origin"; } if (payload.inputs !== undefined && Object.keys(payload.inputs).length === 0) { return "Expected at least one provider credential input"; } return undefined; }), ); const ConnectionRefInput = Schema.Struct({ owner: OwnerSchema, name: Schema.String, integration: Schema.String, }); const ToolOutput = Schema.Struct({ address: Schema.String, owner: OwnerSchema, integration: Schema.String, connection: Schema.String, name: Schema.String, pluginId: Schema.String, description: Schema.String, }); const ConnectionsRefreshOutput = Schema.Struct({ tools: Schema.Array(ToolOutput), }); const RemovedOutput = Schema.Struct({ removed: Schema.Boolean }); const CancelledOutput = Schema.Struct({ cancelled: Schema.Boolean }); const ProvidersOutput = Schema.Struct({ providers: Schema.Array(Schema.String), }); const ProviderItemsInput = Schema.Struct({ provider: Schema.String }); const ProviderItemsOutput = Schema.Struct({ items: Schema.Array(Schema.Struct({ id: Schema.String, name: Schema.String })), }); const PolicyOutput = Schema.Struct({ id: Schema.String, owner: OwnerSchema, pattern: Schema.String, action: Schema.String, position: Schema.String, }); const PoliciesListOutput = Schema.Struct({ policies: Schema.Array(PolicyOutput), }); const PolicyCreateInput = Schema.Struct({ owner: OwnerSchema, pattern: Schema.String, action: ToolPolicyActionSchema, }); const PolicyUpdateInput = Schema.Struct({ id: Schema.String, owner: OwnerSchema, pattern: Schema.optional(Schema.String), action: Schema.optional(ToolPolicyActionSchema), }); const PolicyRemoveInput = Schema.Struct({ id: Schema.String, owner: OwnerSchema, }); const OAuthClientOutput = Schema.Struct({ owner: OwnerSchema, slug: Schema.String, grant: OAuthGrantSchema, authorizationUrl: Schema.String, tokenUrl: Schema.String, resource: Schema.optional(Schema.NullOr(Schema.String)), clientId: Schema.String, origin: Schema.Union([ Schema.Struct({ kind: Schema.Literal("manual") }), Schema.Struct({ kind: Schema.Literal("dynamic_client_registration"), integration: Schema.optional(Schema.NullOr(Schema.String)), }), ]), }); const OAuthClientsListOutput = Schema.Struct({ clients: Schema.Array(OAuthClientOutput), }); // No `clientSecret`: a confidential client's secret must never cross the agent // boundary (it would land in the LLM context window). This tool registers a // PUBLIC client only; a secret-bearing app is registered by the human through // `oauth.clients.createHandoff`, which deep-links them to the web form. const OAuthCreateClientInput = Schema.Struct({ owner: OwnerSchema, slug: Schema.String, authorizationUrl: Schema.String, tokenUrl: Schema.String, grant: OAuthGrantSchema, clientId: Schema.String, resource: Schema.optional(Schema.NullOr(Schema.String)), /** Integration whose connect dialog registered this manual app. Recorded so * the picker can match it by intent (exact) instead of by root domain. */ originIntegration: Schema.optional(Schema.NullOr(Schema.String)), }); // Browser-handoff for a CONFIDENTIAL OAuth app: carries only the NON-secret // fields the form pre-fills. The client secret is typed by the human in the web // UI, exactly like a pasted connection credential, so it never reaches the // agent. Mirrors `ConnectionCreateHandoffInput`. const OAuthCreateClientHandoffInput = Schema.Struct({ integration: Schema.String, owner: Schema.optional(OwnerSchema), slug: Schema.optional(Schema.String), grant: Schema.optional(OAuthGrantSchema), clientId: Schema.optional(Schema.String), authorizationUrl: Schema.optional(Schema.String), tokenUrl: Schema.optional(Schema.String), resource: Schema.optional(Schema.NullOr(Schema.String)), label: Schema.optional(Schema.String), }); const OAuthCreateClientHandoffOutput = Schema.Struct({ url: Schema.String, instructions: Schema.String, }); const OAuthClientOutputRef = Schema.Struct({ client: Schema.String, }); const OAuthRegisterDynamicInput = Schema.Struct({ owner: OwnerSchema, slug: Schema.String, issuer: Schema.optional(Schema.NullOr(Schema.String)), registrationEndpoint: Schema.String, authorizationUrl: Schema.String, tokenUrl: Schema.String, resource: Schema.optional(Schema.NullOr(Schema.String)), scopes: Schema.Array(Schema.String), tokenEndpointAuthMethodsSupported: Schema.optional(Schema.Array(Schema.String)), clientName: Schema.optional(Schema.String), redirectUri: Schema.optional(Schema.NullOr(Schema.String)), originIntegration: Schema.optional(Schema.NullOr(Schema.String)), }); const OAuthRemoveClientInput = Schema.Struct({ owner: OwnerSchema, slug: Schema.String, }); const OAuthProbeInput = Schema.Struct({ url: Schema.String, }); const OAuthProbeOutput = Schema.Struct({ issuer: Schema.optional(Schema.NullOr(Schema.String)), authorizationUrl: Schema.String, tokenUrl: Schema.String, resource: Schema.optional(Schema.NullOr(Schema.String)), scopesSupported: Schema.optional(Schema.Array(Schema.String)), registrationEndpoint: Schema.optional(Schema.NullOr(Schema.String)), tokenEndpointAuthMethodsSupported: Schema.optional(Schema.Array(Schema.String)), clientIdMetadataDocumentSupported: Schema.optional(Schema.Boolean), }); const OAuthStartInput = Schema.Struct({ client: Schema.String, clientOwner: OwnerSchema, owner: OwnerSchema, name: Schema.String, integration: Schema.String, template: Schema.String, identityLabel: Schema.optional(Schema.NullOr(Schema.String)), redirectUri: Schema.optional(Schema.NullOr(Schema.String)), }); const OAuthStartOutput = Schema.Union([ Schema.Struct({ status: Schema.Literal("connected"), connection: ConnectionOutput, }), Schema.Struct({ status: Schema.Literal("redirect"), authorizationUrl: Schema.String, state: Schema.String, }), ]); const OAuthCancelInput = Schema.Struct({ state: Schema.String, }); // Standard-schema versions for the tool() builder. const IntegrationsListOutputStd = schemaToStandard(IntegrationsListOutput); const DetectInputStd = schemaToStandard(DetectInput); const DetectOutputStd = schemaToStandard(DetectOutput); const ConnectionsListInputStd = schemaToStandard(ConnectionsListInput); const ConnectionsListOutputStd = schemaToStandard(ConnectionsListOutput); const ConnectionCreateHandoffInputStd = schemaToStandard(ConnectionCreateHandoffInput); const ConnectionCreateHandoffOutputStd = schemaToStandard(ConnectionCreateHandoffOutput); const ConnectionCreateInputStd = schemaToStandard(ConnectionCreateInput); const ConnectionOutputStd = schemaToStandard(ConnectionOutput); const ConnectionRefInputStd = schemaToStandard(ConnectionRefInput); const ConnectionsRefreshOutputStd = schemaToStandard(ConnectionsRefreshOutput); const RemovedOutputStd = schemaToStandard(RemovedOutput); const CancelledOutputStd = schemaToStandard(CancelledOutput); const ProvidersOutputStd = schemaToStandard(ProvidersOutput); const ProviderItemsInputStd = schemaToStandard(ProviderItemsInput); const ProviderItemsOutputStd = schemaToStandard(ProviderItemsOutput); const PoliciesListOutputStd = schemaToStandard(PoliciesListOutput); const PolicyOutputStd = schemaToStandard(PolicyOutput); const PolicyCreateInputStd = schemaToStandard(PolicyCreateInput); const PolicyUpdateInputStd = schemaToStandard(PolicyUpdateInput); const PolicyRemoveInputStd = schemaToStandard(PolicyRemoveInput); const OAuthClientsListOutputStd = schemaToStandard(OAuthClientsListOutput); const OAuthCreateClientInputStd = schemaToStandard(OAuthCreateClientInput); const OAuthCreateClientHandoffInputStd = schemaToStandard(OAuthCreateClientHandoffInput); const OAuthCreateClientHandoffOutputStd = schemaToStandard(OAuthCreateClientHandoffOutput); const OAuthClientOutputRefStd = schemaToStandard(OAuthClientOutputRef); const OAuthRegisterDynamicInputStd = schemaToStandard(OAuthRegisterDynamicInput); const OAuthRemoveClientInputStd = schemaToStandard(OAuthRemoveClientInput); const OAuthProbeInputStd = schemaToStandard(OAuthProbeInput); const OAuthProbeOutputStd = schemaToStandard(OAuthProbeOutput); const OAuthStartInputStd = schemaToStandard(OAuthStartInput); const OAuthStartOutputStd = schemaToStandard(OAuthStartOutput); const OAuthCancelInputStd = schemaToStandard(OAuthCancelInput); const connectionToOutput = (connection: Connection) => ({ owner: connection.owner, name: String(connection.name), integration: String(connection.integration), template: String(connection.template), provider: String(connection.provider), address: String(connection.address), identityLabel: connection.identityLabel ?? null, description: connection.description ?? null, expiresAt: connection.expiresAt ?? null, oauthClient: connection.oauthClient == null ? null : String(connection.oauthClient), oauthClientOwner: connection.oauthClientOwner ?? null, oauthScope: connection.oauthScope ?? null, }); /** Number of space-separated grants in an `oauthScope` string, or null when * the connection carries no scope (static credentials, or an OAuth AS that * omitted scope). */ const oauthScopeCount = (scope: string | null | undefined): number | null => scope == null ? null : scope.split(/\s+/).filter(Boolean).length; /** Lean projection for `connections.list`. Summarizes `oauthScope` to a count * unless `verbose`, where the full grant string is included too. */ const connectionToListItem = (connection: Connection, verbose: boolean) => ({ owner: connection.owner, name: String(connection.name), integration: String(connection.integration), template: String(connection.template), provider: String(connection.provider), address: String(connection.address), identityLabel: connection.identityLabel ?? null, description: connection.description ?? null, expiresAt: connection.expiresAt ?? null, oauthClient: connection.oauthClient == null ? null : String(connection.oauthClient), oauthClientOwner: connection.oauthClientOwner ?? null, oauthScopeCount: oauthScopeCount(connection.oauthScope), ...(verbose ? { oauthScope: connection.oauthScope ?? null } : {}), }); const toolToOutput = (toolRow: Tool) => ({ address: String(toolRow.address), owner: toolRow.owner, integration: String(toolRow.integration), connection: String(toolRow.connection), name: String(toolRow.name), pluginId: toolRow.pluginId, description: toolRow.description, }); const connectionRefFromInput = (input: typeof ConnectionRefInput.Type) => ({ owner: input.owner as Owner, integration: IntegrationSlug.make(input.integration), name: ConnectionName.make(input.name), }); const originFromInput = ( origin: typeof ConnectionInputOriginInput.Type, ): ConnectionInputOrigin => ({ from: { provider: ProviderKey.make(origin.from.provider), id: ProviderItemId.make(origin.from.id), }, }); const createConnectionInputFromTool = ( input: typeof ConnectionCreateInput.Type, ): CreateConnectionInput => { const base = { owner: input.owner as Owner, name: ConnectionName.make(input.name), integration: IntegrationSlug.make(input.integration), template: AuthTemplateSlug.make(input.template), identityLabel: input.identityLabel ?? null, }; if (input.from !== undefined) { return { ...base, from: { provider: ProviderKey.make(input.from.provider), id: ProviderItemId.make(input.from.id), }, }; } return { ...base, inputs: Object.fromEntries( Object.entries(input.inputs ?? {}).map(([variable, origin]) => [ variable, originFromInput(origin), ]), ), }; }; const connectionCreateHandoffUrl = ( webBaseUrl: string | undefined, orgSlug: string | undefined, input: typeof ConnectionCreateHandoffInput.Type, ): string => { const search = new URLSearchParams({ addAccount: "1" }); if (input.owner !== undefined) search.set("owner", input.owner); if (input.template !== undefined) search.set("template", input.template); if (input.label !== undefined) search.set("label", input.label); // Org-scoped hosts (cloud, self-host, cloudflare) serve the console under an // optional `/<org-slug>` segment. Pin the URL to the executor's bound org so // it opens that org directly instead of relying on the browser's last-active // org (which `OrgSlugGate` would otherwise canonicalize a bare URL to). When // no slug is known (CLI, local, non-request callers) we emit the bare path. const orgPrefix = orgSlug !== undefined && orgSlug.length > 0 ? `/${orgSlug}` : ""; const path = `${orgPrefix}/integrations/${encodeURIComponent(input.integration)}?${search.toString()}`; if (webBaseUrl === undefined || webBaseUrl.length === 0) return path; return new URL(path, webBaseUrl.endsWith("/") ? webBaseUrl : `${webBaseUrl}/`).toString(); }; const oauthClientCreateHandoffUrl = ( webBaseUrl: string | undefined, orgSlug: string | undefined, input: typeof OAuthCreateClientHandoffInput.Type, ): string => { // `oauthClient=1` flips the integration's Add-account flow straight into the // Register-OAuth-app form; the rest pre-fill its NON-secret fields. The client // secret is deliberately absent: the human types it in the browser, so it is // never placed in this URL (nor in the agent's context). Same builder shape as // `connectionCreateHandoffUrl`. const search = new URLSearchParams({ addAccount: "1", oauthClient: "1" }); if (input.owner !== undefined) search.set("owner", input.owner); if (input.slug !== undefined) search.set("clientSlug", input.slug); if (input.grant !== undefined) search.set("grant", input.grant); if (input.clientId !== undefined) search.set("clientId", input.clientId); if (input.authorizationUrl !== undefined) search.set("authorizationUrl", input.authorizationUrl); if (input.tokenUrl !== undefined) search.set("tokenUrl", input.tokenUrl); if (input.resource != null && input.resource.length > 0) search.set("resource", input.resource); if (input.label !== undefined) search.set("label", input.label); const orgPrefix = orgSlug !== undefined && orgSlug.length > 0 ? `/${orgSlug}` : ""; const path = `${orgPrefix}/integrations/${encodeURIComponent(input.integration)}?${search.toString()}`; if (webBaseUrl === undefined || webBaseUrl.length === 0) return path; return new URL(path, webBaseUrl.endsWith("/") ? webBaseUrl : `${webBaseUrl}/`).toString();