types.ts
1 import type { Client } from '@modelcontextprotocol/sdk/client/index.js' 2 import type { 3 Resource, 4 ServerCapabilities, 5 } from '@modelcontextprotocol/sdk/types.js' 6 import { z } from 'zod/v4' 7 import { lazySchema } from '../../utils/lazySchema.js' 8 9 // Configuration schemas and types 10 export const ConfigScopeSchema = lazySchema(() => 11 z.enum([ 12 'local', 13 'user', 14 'project', 15 'dynamic', 16 'enterprise', 17 'claudeai', 18 'managed', 19 ]), 20 ) 21 export type ConfigScope = z.infer<ReturnType<typeof ConfigScopeSchema>> 22 23 export const TransportSchema = lazySchema(() => 24 z.enum(['stdio', 'sse', 'sse-ide', 'http', 'ws', 'sdk']), 25 ) 26 export type Transport = z.infer<ReturnType<typeof TransportSchema>> 27 28 export const McpStdioServerConfigSchema = lazySchema(() => 29 z.object({ 30 type: z.literal('stdio').optional(), // Optional for backwards compatibility 31 command: z.string().min(1, 'Command cannot be empty'), 32 args: z.array(z.string()).default([]), 33 env: z.record(z.string(), z.string()).optional(), 34 }), 35 ) 36 37 // Cross-App Access (XAA / SEP-990): just a per-server flag. IdP connection 38 // details (issuer, clientId, callbackPort) come from settings.xaaIdp — configured 39 // once, shared across all XAA-enabled servers. clientId/clientSecret (parent 40 // oauth config + keychain slot) are for the MCP server's AS. 41 const McpXaaConfigSchema = lazySchema(() => z.boolean()) 42 43 const McpOAuthConfigSchema = lazySchema(() => 44 z.object({ 45 clientId: z.string().optional(), 46 callbackPort: z.number().int().positive().optional(), 47 authServerMetadataUrl: z 48 .string() 49 .url() 50 .startsWith('https://', { 51 message: 'authServerMetadataUrl must use https://', 52 }) 53 .optional(), 54 xaa: McpXaaConfigSchema().optional(), 55 }), 56 ) 57 58 export const McpSSEServerConfigSchema = lazySchema(() => 59 z.object({ 60 type: z.literal('sse'), 61 url: z.string(), 62 headers: z.record(z.string(), z.string()).optional(), 63 headersHelper: z.string().optional(), 64 oauth: McpOAuthConfigSchema().optional(), 65 }), 66 ) 67 68 // Internal-only server type for IDE extensions 69 export const McpSSEIDEServerConfigSchema = lazySchema(() => 70 z.object({ 71 type: z.literal('sse-ide'), 72 url: z.string(), 73 ideName: z.string(), 74 ideRunningInWindows: z.boolean().optional(), 75 }), 76 ) 77 78 // Internal-only server type for IDE extensions 79 export const McpWebSocketIDEServerConfigSchema = lazySchema(() => 80 z.object({ 81 type: z.literal('ws-ide'), 82 url: z.string(), 83 ideName: z.string(), 84 authToken: z.string().optional(), 85 ideRunningInWindows: z.boolean().optional(), 86 }), 87 ) 88 89 export const McpHTTPServerConfigSchema = lazySchema(() => 90 z.object({ 91 type: z.literal('http'), 92 url: z.string(), 93 headers: z.record(z.string(), z.string()).optional(), 94 headersHelper: z.string().optional(), 95 oauth: McpOAuthConfigSchema().optional(), 96 }), 97 ) 98 99 export const McpWebSocketServerConfigSchema = lazySchema(() => 100 z.object({ 101 type: z.literal('ws'), 102 url: z.string(), 103 headers: z.record(z.string(), z.string()).optional(), 104 headersHelper: z.string().optional(), 105 }), 106 ) 107 108 export const McpSdkServerConfigSchema = lazySchema(() => 109 z.object({ 110 type: z.literal('sdk'), 111 name: z.string(), 112 }), 113 ) 114 115 // Config type for Claude.ai proxy servers 116 export const McpClaudeAIProxyServerConfigSchema = lazySchema(() => 117 z.object({ 118 type: z.literal('claudeai-proxy'), 119 url: z.string(), 120 id: z.string(), 121 }), 122 ) 123 124 export const McpServerConfigSchema = lazySchema(() => 125 z.union([ 126 McpStdioServerConfigSchema(), 127 McpSSEServerConfigSchema(), 128 McpSSEIDEServerConfigSchema(), 129 McpWebSocketIDEServerConfigSchema(), 130 McpHTTPServerConfigSchema(), 131 McpWebSocketServerConfigSchema(), 132 McpSdkServerConfigSchema(), 133 McpClaudeAIProxyServerConfigSchema(), 134 ]), 135 ) 136 137 export type McpStdioServerConfig = z.infer< 138 ReturnType<typeof McpStdioServerConfigSchema> 139 > 140 export type McpSSEServerConfig = z.infer< 141 ReturnType<typeof McpSSEServerConfigSchema> 142 > 143 export type McpSSEIDEServerConfig = z.infer< 144 ReturnType<typeof McpSSEIDEServerConfigSchema> 145 > 146 export type McpWebSocketIDEServerConfig = z.infer< 147 ReturnType<typeof McpWebSocketIDEServerConfigSchema> 148 > 149 export type McpHTTPServerConfig = z.infer< 150 ReturnType<typeof McpHTTPServerConfigSchema> 151 > 152 export type McpWebSocketServerConfig = z.infer< 153 ReturnType<typeof McpWebSocketServerConfigSchema> 154 > 155 export type McpSdkServerConfig = z.infer< 156 ReturnType<typeof McpSdkServerConfigSchema> 157 > 158 export type McpClaudeAIProxyServerConfig = z.infer< 159 ReturnType<typeof McpClaudeAIProxyServerConfigSchema> 160 > 161 export type McpServerConfig = z.infer<ReturnType<typeof McpServerConfigSchema>> 162 163 export type ScopedMcpServerConfig = McpServerConfig & { 164 scope: ConfigScope 165 // For plugin-provided servers: the providing plugin's LoadedPlugin.source 166 // (e.g. 'slack@anthropic'). Stashed at config-build time so the channel 167 // gate doesn't have to race AppState.plugins.enabled hydration. 168 pluginSource?: string 169 } 170 171 export const McpJsonConfigSchema = lazySchema(() => 172 z.object({ 173 mcpServers: z.record(z.string(), McpServerConfigSchema()), 174 }), 175 ) 176 177 export type McpJsonConfig = z.infer<ReturnType<typeof McpJsonConfigSchema>> 178 179 // Server connection types 180 export type ConnectedMCPServer = { 181 client: Client 182 name: string 183 type: 'connected' 184 capabilities: ServerCapabilities 185 serverInfo?: { 186 name: string 187 version: string 188 } 189 instructions?: string 190 config: ScopedMcpServerConfig 191 cleanup: () => Promise<void> 192 } 193 194 export type FailedMCPServer = { 195 name: string 196 type: 'failed' 197 config: ScopedMcpServerConfig 198 error?: string 199 } 200 201 export type NeedsAuthMCPServer = { 202 name: string 203 type: 'needs-auth' 204 config: ScopedMcpServerConfig 205 } 206 207 export type PendingMCPServer = { 208 name: string 209 type: 'pending' 210 config: ScopedMcpServerConfig 211 reconnectAttempt?: number 212 maxReconnectAttempts?: number 213 } 214 215 export type DisabledMCPServer = { 216 name: string 217 type: 'disabled' 218 config: ScopedMcpServerConfig 219 } 220 221 export type MCPServerConnection = 222 | ConnectedMCPServer 223 | FailedMCPServer 224 | NeedsAuthMCPServer 225 | PendingMCPServer 226 | DisabledMCPServer 227 228 // Resource types 229 export type ServerResource = Resource & { server: string } 230 231 // MCP CLI State types 232 export interface SerializedTool { 233 name: string 234 description: string 235 inputJSONSchema?: { 236 [x: string]: unknown 237 type: 'object' 238 properties?: { 239 [x: string]: unknown 240 } 241 } 242 isMcp?: boolean 243 originalToolName?: string // Original unnormalized tool name from MCP server 244 } 245 246 export interface SerializedClient { 247 name: string 248 type: 'connected' | 'failed' | 'needs-auth' | 'pending' | 'disabled' 249 capabilities?: ServerCapabilities 250 } 251 252 export interface MCPCliState { 253 clients: SerializedClient[] 254 configs: Record<string, ScopedMcpServerConfig> 255 tools: SerializedTool[] 256 resources: Record<string, ServerResource[]> 257 normalizedNames?: Record<string, string> // Maps normalized names to original names 258 }