Connections (MCP)

createMcpConnections, a connection's options, and signing in with OAuth, from @intentface/latch-mcp.

@intentface/latch-mcp gives agents the tools of MCP servers, signed in as each user. What it's for: Tools and connections. Setting it up: Connect other services.

ts
import { createVault } from "@intentface/latch-core";
import { createPostgresSecretStore } from "@intentface/latch-drizzle";
import { createMcpConnections, oauth, bearer } from "@intentface/latch-mcp";

const vault = createVault<Principal>({
  store: createPostgresSecretStore(db, { owner: (p) => [p.orgId, p.userId] }),
  masterKey: process.env.LATCH_MASTER_KEY!, // base64 of 32 random bytes
});

createRuntime({
  …,
  connections: createMcpConnections({
    vault,
    secrets: vault.resolver,
    allowedHosts: ["mcp.linear.app", "mcp.internal.example.com"],
    callbackBaseUrl: "https://app.example.com/api/latch/connections",
    connections: {
      linear: { url: "https://mcp.linear.app/mcp", auth: oauth() },
      crm: { url: "https://mcp.internal.example.com/crm", auth: bearer({ getToken: (p) => vault.resolver(p, "crm_token") }) },
    },
  }),
});

createMcpConnections

OptionTypeDefaultDetails
vaultVault<P>—
secrets?SecretResolver<P>—
allowedHostsstring[] | ((principal: P) => string[] | Promise<string[]>)—
connectionsRecord<string, McpConnectionDef<P>>—
callbackBaseUrl?string—
onConnectionError?((name: string, error: unknown) => void)—

A connection

OptionTypeDefaultDetails
urlstring—
description?string—
auth?McpConnectionAuth<P>—
tools?object—
approval?McpApprovalPolicy—
approvalFor?(principal: P) => McpApprovalPolicy | Promise<McpApprovalPolicy>—
visibleTools?((tool: McpToolInfo) => boolean)—
visibleToolsFor?function—
allowPrivateHost?boolean—

Signing in: oauth()

oauth() with no options works for servers that let apps register themselves. For the rest, pass your own app's clientId and clientSecret.

OptionTypeDefaultDetails
scopes?string[]—
clientId?string—
clientSecret?string—
clientName?string—
tokenEndpointAuthMethod?string"none" (a public client)
normalize?object—
omitResource?boolean—
authorizationParams?Record<string, string>—
clientMetadata?Partial<object>—
allowedAuthorizationServers?string[]—

bearer({ getToken }) sends a token you supply, and headers({ … }) sends fixed headers.