import type { Client } from '@modelcontextprotocol/sdk/client/index.js' import type { Resource, ServerCapabilities, } from '@modelcontextprotocol/sdk/types.js' import { z } from 'zod/v4' import { lazySchema } from '../../utils/lazySchema.js' // Configuration schemas and types export const ConfigScopeSchema = lazySchema(() => z.enum([ 'local', 'user', 'project', 'dynamic', 'enterprise', 'claudeai', 'managed', ]), ) export type ConfigScope = z.infer> export const TransportSchema = lazySchema(() => z.enum(['stdio', 'sse', 'sse-ide', 'http', 'ws', 'sdk']), ) export type Transport = z.infer> export const McpStdioServerConfigSchema = lazySchema(() => z.object({ type: z.literal('stdio').optional(), // Optional for backwards compatibility command: z.string().min(1, 'Command cannot be empty'), args: z.array(z.string()).default([]), env: z.record(z.string(), z.string()).optional(), }), ) // Cross-App Access (XAA / SEP-990): just a per-server flag. IdP connection // details (issuer, clientId, callbackPort) come from settings.xaaIdp — configured // once, shared across all XAA-enabled servers. clientId/clientSecret (parent // oauth config + keychain slot) are for the MCP server's AS. const McpXaaConfigSchema = lazySchema(() => z.boolean()) const McpOAuthConfigSchema = lazySchema(() => z.object({ clientId: z.string().optional(), callbackPort: z.number().int().positive().optional(), authServerMetadataUrl: z .string() .url() .startsWith('https://', { message: 'authServerMetadataUrl must use https://', }) .optional(), xaa: McpXaaConfigSchema().optional(), }), ) export const McpSSEServerConfigSchema = lazySchema(() => z.object({ type: z.literal('sse'), url: z.string(), headers: z.record(z.string(), z.string()).optional(), headersHelper: z.string().optional(), oauth: McpOAuthConfigSchema().optional(), }), ) // Internal-only server type for IDE extensions export const McpSSEIDEServerConfigSchema = lazySchema(() => z.object({ type: z.literal('sse-ide'), url: z.string(), ideName: z.string(), ideRunningInWindows: z.boolean().optional(), }), ) // Internal-only server type for IDE extensions export const McpWebSocketIDEServerConfigSchema = lazySchema(() => z.object({ type: z.literal('ws-ide'), url: z.string(), ideName: z.string(), authToken: z.string().optional(), ideRunningInWindows: z.boolean().optional(), }), ) export const McpHTTPServerConfigSchema = lazySchema(() => z.object({ type: z.literal('http'), url: z.string(), headers: z.record(z.string(), z.string()).optional(), headersHelper: z.string().optional(), oauth: McpOAuthConfigSchema().optional(), }), ) export const McpWebSocketServerConfigSchema = lazySchema(() => z.object({ type: z.literal('ws'), url: z.string(), headers: z.record(z.string(), z.string()).optional(), headersHelper: z.string().optional(), }), ) export const McpSdkServerConfigSchema = lazySchema(() => z.object({ type: z.literal('sdk'), name: z.string(), }), ) // Config type for Claude.ai proxy servers export const McpClaudeAIProxyServerConfigSchema = lazySchema(() => z.object({ type: z.literal('claudeai-proxy'), url: z.string(), id: z.string(), }), ) export const McpServerConfigSchema = lazySchema(() => z.union([ McpStdioServerConfigSchema(), McpSSEServerConfigSchema(), McpSSEIDEServerConfigSchema(), McpWebSocketIDEServerConfigSchema(), McpHTTPServerConfigSchema(), McpWebSocketServerConfigSchema(), McpSdkServerConfigSchema(), McpClaudeAIProxyServerConfigSchema(), ]), ) export type McpStdioServerConfig = z.infer< ReturnType > export type McpSSEServerConfig = z.infer< ReturnType > export type McpSSEIDEServerConfig = z.infer< ReturnType > export type McpWebSocketIDEServerConfig = z.infer< ReturnType > export type McpHTTPServerConfig = z.infer< ReturnType > export type McpWebSocketServerConfig = z.infer< ReturnType > export type McpSdkServerConfig = z.infer< ReturnType > export type McpClaudeAIProxyServerConfig = z.infer< ReturnType > export type McpServerConfig = z.infer> export type ScopedMcpServerConfig = McpServerConfig & { scope: ConfigScope // For plugin-provided servers: the providing plugin's LoadedPlugin.source // (e.g. 'slack@anthropic'). Stashed at config-build time so the channel // gate doesn't have to race AppState.plugins.enabled hydration. pluginSource?: string } export const McpJsonConfigSchema = lazySchema(() => z.object({ mcpServers: z.record(z.string(), McpServerConfigSchema()), }), ) export type McpJsonConfig = z.infer> // Server connection types export type ConnectedMCPServer = { client: Client name: string type: 'connected' capabilities: ServerCapabilities serverInfo?: { name: string version: string } instructions?: string config: ScopedMcpServerConfig cleanup: () => Promise } export type FailedMCPServer = { name: string type: 'failed' config: ScopedMcpServerConfig error?: string } export type NeedsAuthMCPServer = { name: string type: 'needs-auth' config: ScopedMcpServerConfig } export type PendingMCPServer = { name: string type: 'pending' config: ScopedMcpServerConfig reconnectAttempt?: number maxReconnectAttempts?: number } export type DisabledMCPServer = { name: string type: 'disabled' config: ScopedMcpServerConfig } export type MCPServerConnection = | ConnectedMCPServer | FailedMCPServer | NeedsAuthMCPServer | PendingMCPServer | DisabledMCPServer // Resource types export type ServerResource = Resource & { server: string } // MCP CLI State types export interface SerializedTool { name: string description: string inputJSONSchema?: { [x: string]: unknown type: 'object' properties?: { [x: string]: unknown } } isMcp?: boolean originalToolName?: string // Original unnormalized tool name from MCP server } export interface SerializedClient { name: string type: 'connected' | 'failed' | 'needs-auth' | 'pending' | 'disabled' capabilities?: ServerCapabilities } export interface MCPCliState { clients: SerializedClient[] configs: Record tools: SerializedTool[] resources: Record normalizedNames?: Record // Maps normalized names to original names }