/** * Persistent store for user-added remote MCP servers. * * Servers added through the settings UI live in the framework's `settings` * table, keyed by scope: * - User scope: `u::mcp-servers-remote` * - Org scope: `o::mcp-servers-remote` * * Both scopes store the same shape — a list of `StoredRemoteMcpServer` * records. The running MCP manager merges this list with the file-based * `mcp.config.json` on startup and after every mutation. * * SECURITY: HTTP MCP servers commonly require a bearer token in the * `Authorization` header. Those values are written to the encrypted * `app_secrets` table (AES-256-GCM via writeAppSecret). The settings row * stores only the secret-key reference (`headerSecretKey`), not the raw * value. Callers retrieving headers must call `materializeHeaders` to * fetch the cleartext at request time. Legacy rows that wrote headers * cleartext into `headers` continue to work read-only — they should be * re-saved to migrate. */ import type { McpHttpServerConfig } from "./config.js"; import { type McpOAuthCredentialBundle } from "./oauth-client.js"; export { validateRemoteUrl } from "./remote-url.js"; export type RemoteMcpScope = "user" | "org"; export declare class InvalidMcpHeaderConfigurationError extends Error { readonly reason: "invalid-json" | "invalid-shape" | "missing-secret"; readonly code = "invalid_mcp_header_configuration"; constructor(reason: "invalid-json" | "invalid-shape" | "missing-secret"); } export declare class McpHeaderSecretUnavailableError extends Error { readonly code = "mcp_header_secret_unavailable"; constructor(cause: unknown); } export interface StoredRemoteMcpServer { id: string; name: string; url: string; /** * Optional non-secret headers to pass to the MCP server. SECURITY: secret * material (Authorization, X-Api-Key, …) is moved out of this field at * write time and stored encrypted in `app_secrets`; see * `headerSecretKey`. Legacy rows may still contain cleartext headers and * are honored read-only. */ headers?: Record; headerSecretKey?: string; oauthSecretKey?: string; firstParty?: boolean; firstPartyAppId?: string; description?: string; createdAt: number; } export declare function normalizeServerName(input: string): string; export declare function hashEmail(email: string): string; export declare function listRemoteServers(scope: RemoteMcpScope, scopeId: string): Promise; export declare function addRemoteServer(scope: RemoteMcpScope, scopeId: string, input: { name: string; url: string; headers?: Record; description?: string; firstParty?: boolean; }): Promise<{ ok: true; server: StoredRemoteMcpServer; } | { ok: false; error: string; }>; /** * Persist a remote MCP server whose authorization is managed by the MCP * server's OAuth 2.1 metadata and token endpoints. OAuth credentials are * stored separately from the settings row and never returned to clients. */ export declare function addOAuthRemoteServer(scope: RemoteMcpScope, scopeId: string, input: { name: string; url: string; description?: string; credentials: McpOAuthCredentialBundle; }): Promise<{ ok: true; server: StoredRemoteMcpServer; } | { ok: false; error: string; }>; export declare function replaceOAuthRemoteServer(scope: RemoteMcpScope, scopeId: string, serverId: string, credentials: McpOAuthCredentialBundle): Promise<{ ok: true; server: StoredRemoteMcpServer; } | { ok: false; error: string; }>; export declare function addFirstPartyRemoteServer(orgId: string, input: { appId: string; name: string; url: string; description?: string; }): Promise<{ ok: true; server: StoredRemoteMcpServer; } | { ok: false; error: string; }>; export declare function isFirstPartyRemoteEndpointTrusted(orgId: string, appId: string, endpoint: string): Promise<{ ok: true; } | { ok: false; error: string; }>; export declare function removeRemoteServer(scope: RemoteMcpScope, scopeId: string, id: string): Promise; export declare function materializeHeaders(scope: RemoteMcpScope, scopeId: string, stored: StoredRemoteMcpServer): Promise | undefined>; /** * Project a stored server into the runtime `McpHttpServerConfig` shape that * `McpClientManager` consumes. The merged-config key is the scope + name * so a user-scope and org-scope server can both share a readable name * without clobbering each other. * * SECURITY: when the stored row references encrypted headers * (`headerSecretKey`), callers should use `toHttpServerConfigAsync` * instead — this synchronous variant returns ONLY the cleartext headers * already present on the row. Returning the row's literal headers without * the secret material means the runtime client would call the MCP server * without auth (request will fail), but never leaks the encrypted secret. */ export declare function toHttpServerConfig(stored: StoredRemoteMcpServer): McpHttpServerConfig; export declare function toHttpServerConfigAsync(scope: RemoteMcpScope, scopeId: string, stored: StoredRemoteMcpServer): Promise; /** * Build the merged-config key for a stored server. * * The key encodes the owning scope + owner identity so two users adding a * server called `zapier` produce distinct ids (`user_ab12cd34ef_zapier` vs * `user_99aa88bb77_zapier`) and Alice's tool calls never route through Bob's * credentials in a shared-process deployment. * * - User scope: `user__` * - Org scope: `org__` * * `ownerId` is the raw email for user scope, and the org id for org scope. */ export declare function mergedConfigKey(scope: RemoteMcpScope, stored: StoredRemoteMcpServer, ownerId: string): string; export declare function parseMergedKey(keyOrToolName: string): { scope: RemoteMcpScope; owner: string; name: string; } | null;