import { OAuthClientInformationFull, OAuthTokens, } from "@modelcontextprotocol/sdk/shared/auth.js"; import { Tool } from "ai"; import { tag } from "lib/tag"; import { z } from "zod"; export const MCPRemoteConfigZodSchema = z.object({ url: z.string().url().describe("The URL of the SSE endpoint"), headers: z.record(z.string(), z.string()).optional(), }); export const MCPStdioConfigZodSchema = z.object({ command: z.string().min(1).describe("The command to run"), args: z.array(z.string()).optional(), env: z.record(z.string(), z.string()).optional(), }); export const AllowedMCPServerZodSchema = z.object({ tools: z.array(z.string()), // resources: z.array(z.string()).optional(), }); export type AllowedMCPServer = z.infer; export type MCPRemoteConfig = z.infer; export type MCPStdioConfig = z.infer; export type MCPServerConfig = MCPRemoteConfig | MCPStdioConfig; export type MCPToolInfo = { name: string; description: string; inputSchema?: { type?: any; properties?: Record; required?: string[]; }; }; export type MCPServerInfo = { id: string; name: string; config?: MCPServerConfig; // Optional - hidden from non-owners for security visibility: "public" | "private"; error?: unknown; enabled: boolean; userId: string; status: "connected" | "disconnected" | "loading" | "authorizing"; lastConnectionStatus?: MCPConnectionStatus | null; toolInfo: MCPToolInfo[]; createdAt?: Date | string; updatedAt?: Date | string; userName?: string | null; userAvatar?: string | null; description?: string; // For ShareableCard compatibility icon?: { value?: string; style?: { backgroundColor?: string; }; }; }; export type McpServerInsert = { name: string; config: MCPServerConfig; id?: string; userId: string; visibility?: "public" | "private"; }; export type MCPConnectionStatus = "connected" | "error"; export type McpServerSelect = { name: string; config: MCPServerConfig; id: string; userId: string; visibility: "public" | "private"; toolInfo?: MCPToolInfo[] | null; toolInfoUpdatedAt?: Date | null; lastConnectionStatus?: MCPConnectionStatus | null; }; export type VercelAIMcpTool = Tool & { _mcpServerName: string; _mcpServerId: string; _originToolName: string; }; export const VercelAIMcpToolTag = tag("mcp"); export interface MCPRepository { save(server: McpServerInsert): Promise; selectById(id: string): Promise; selectByServerName(name: string): Promise; selectAll(): Promise; selectAllForUser(userId: string): Promise; deleteById(id: string): Promise; existsByServerName(name: string): Promise; updateVisibility(id: string, visibility: "public" | "private"): Promise; updateToolInfo(id: string, toolInfo: MCPToolInfo[]): Promise; updateConnectionStatus( id: string, status: MCPConnectionStatus, ): Promise; } export const McpToolCustomizationZodSchema = z.object({ toolName: z.string().min(1), mcpServerId: z.string().min(1), prompt: z.string().max(1000).optional().nullable(), }); export type McpToolCustomization = { id: string; userId: string; toolName: string; mcpServerId: string; prompt?: string | null; }; export type McpToolCustomizationRepository = { select(key: { userId: string; mcpServerId: string; toolName: string; }): Promise; selectByUserIdAndMcpServerId: (key: { userId: string; mcpServerId: string; }) => Promise; selectByUserId: ( userId: string, ) => Promise<(McpToolCustomization & { serverName: string })[]>; upsertToolCustomization: ( data: PartialBy, ) => Promise; deleteToolCustomization: (key: { userId: string; mcpServerId: string; toolName: string; }) => Promise; }; export const McpServerCustomizationZodSchema = z.object({ mcpServerId: z.string().min(1), prompt: z.string().max(3000).optional().nullable(), }); export type McpServerCustomization = { id: string; userId: string; mcpServerId: string; prompt?: string | null; }; export type McpServerCustomizationRepository = { selectByUserIdAndMcpServerId: (key: { userId: string; mcpServerId: string; }) => Promise<(McpServerCustomization & { serverName: string }) | null>; selectByUserId: ( userId: string, ) => Promise<(McpServerCustomization & { serverName: string })[]>; upsertMcpServerCustomization: ( data: PartialBy, ) => Promise; deleteMcpServerCustomizationByMcpServerIdAndUserId: (key: { mcpServerId: string; userId: string; }) => Promise; }; export type McpServerCustomizationsPrompt = { name: string; id: string; prompt?: string; tools?: { [toolName: string]: string; }; }; const TextContent = z.object({ type: z.literal("text"), text: z.string(), _meta: z.object({}).passthrough().optional(), }); const ImageContent = z.object({ type: z.literal("image"), data: z.string(), mimeType: z.string(), _meta: z.object({}).passthrough().optional(), }); const AudioContent = z.object({ type: z.literal("audio"), data: z.string(), mimeType: z.string(), _meta: z.object({}).passthrough().optional(), }); const ResourceLinkContent = z.object({ type: z.literal("resource_link"), name: z.string(), title: z.string().optional(), uri: z.string(), description: z.string().optional(), mimeType: z.string().optional(), _meta: z.object({}).passthrough().optional(), }); const ResourceText = z.object({ uri: z.string(), mimeType: z.string().optional(), _meta: z.object({}).passthrough().optional(), text: z.string(), }); const ResourceBlob = z.object({ uri: z.string(), mimeType: z.string().optional(), _meta: z.object({}).passthrough().optional(), blob: z.string(), }); const ResourceContent = z.object({ type: z.literal("resource"), resource: z.union([ResourceText, ResourceBlob]), _meta: z.object({}).passthrough().optional(), }); const ContentUnion = z.union([ TextContent, ImageContent, AudioContent, ResourceLinkContent, ResourceContent, ]); export const CallToolResultSchema = z.object({ _meta: z.object({}).passthrough().optional(), content: z.array(ContentUnion).default([]), structuredContent: z.object({}).passthrough().optional(), isError: z.boolean().optional(), }); export type CallToolResult = z.infer; export type McpOAuthSession = { id: string; mcpServerId: string; serverUrl: string; clientInfo?: OAuthClientInformationFull; tokens?: OAuthTokens; codeVerifier?: string; state?: string; createdAt: Date; updatedAt: Date; }; export type McpOAuthRepository = { // 1. Query methods // Get session with valid tokens (authenticated) getAuthenticatedSession( mcpServerId: string, ): Promise; // Get session by OAuth state (for callback handling) getSessionByState(state: string): Promise; // 2. Create/Update methods // Create new OAuth session createSession( mcpServerId: string, data: Partial, ): Promise; // Update existing session by state updateSessionByState( state: string, data: Partial, ): Promise; // Save tokens and cleanup incomplete sessions saveTokensAndCleanup( state: string, mcpServerId: string, data: Partial, ): Promise; // Delete a session by its OAuth state deleteByState(state: string): Promise; };