cast-code 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/modules/mcp/services/mcp-client.service.ts"],"sourcesContent":["import { Injectable } from '@nestjs/common';\nimport { EventEmitter } from 'events';\nimport { spawn, ChildProcess } from 'child_process';\nimport { McpConfig, McpTool, McpConnection } from '../types';\n\n@Injectable()\nexport class McpClientService extends EventEmitter {\n private connections: Map<string, McpConnection> = new Map();\n private stdioBuffers: Map<string, string> = new Map();\n private requestIdCounter = 0;\n\n async connect(name: string, config: McpConfig): Promise<boolean> {\n if (this.connections.has(name)) {\n const existing = this.connections.get(name)!;\n if (existing.status === 'connected') {\n return true;\n }\n }\n\n const connection: McpConnection = {\n config,\n tools: [],\n status: 'connecting',\n };\n\n this.connections.set(name, connection);\n\n try {\n if (config.type === 'stdio' && config.command) {\n await this.connectStdio(name, connection);\n } else if (config.type === 'sse' && config.endpoint) {\n throw new Error('SSE transport not yet supported. Use stdio or http instead.');\n } else if (config.type === 'http' && config.endpoint) {\n await this.connectHttp(name, connection);\n }\n\n connection.status = 'connected';\n return true;\n } catch (error) {\n connection.status = 'error';\n return false;\n }\n }\n\n private async connectStdio(name: string, connection: McpConnection): Promise<void> {\n const { command, args = [], env = {} } = connection.config;\n\n const proc = spawn(command!, args, {\n env: { ...process.env, ...env },\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n connection.process = proc;\n this.stdioBuffers.set(name, '');\n\n proc.stdout?.on('data', (data: Buffer) => {\n const buffer = (this.stdioBuffers.get(name) || '') + data.toString();\n const lines = buffer.split('\\n');\n\n this.stdioBuffers.set(name, lines.pop() || '');\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n const parsed = JSON.parse(trimmed);\n this.emit(`response:${name}`, parsed);\n } catch {\n }\n }\n });\n\n proc.stderr?.on('data', (data: Buffer) => {\n const msg = data.toString().trim();\n if (msg && !msg.startsWith('Debugger') && !msg.startsWith('Warning')) {\n console.error(`MCP ${name} stderr:`, msg);\n }\n });\n\n proc.on('close', (code) => {\n connection.status = 'disconnected';\n this.stdioBuffers.delete(name);\n this.emit('disconnected', name);\n\n if (code !== null && code !== 0) {\n setTimeout(() => {\n this.reconnect(name).catch(() => {});\n }, 3000);\n }\n });\n\n await this.sendRequest(name, 'initialize', {\n protocolVersion: '2024-11-05',\n capabilities: {},\n clientInfo: { name: 'cast-code', version: '1.0.0' },\n });\n\n const toolsResponse = await this.sendRequest(name, 'tools/list', {});\n connection.tools = toolsResponse?.tools || [];\n }\n\n private async connectHttp(name: string, connection: McpConnection): Promise<void> {\n const endpoint = connection.config.endpoint!;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n if (connection.config.env?.AUTH_TOKEN) {\n headers['Authorization'] = `Bearer ${connection.config.env.AUTH_TOKEN}`;\n }\n\n try {\n new URL(endpoint);\n } catch {\n throw new Error(`Invalid MCP HTTP endpoint: ${endpoint}`);\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 10000);\n\n const initResponse = await fetch(endpoint, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n jsonrpc: '2.0',\n id: this.nextId(),\n method: 'initialize',\n params: {\n protocolVersion: '2024-11-05',\n capabilities: {},\n clientInfo: { name: 'cast-code', version: '1.0.0' },\n },\n }),\n signal: controller.signal,\n });\n\n clearTimeout(timeout);\n\n if (!initResponse.ok) {\n throw new Error(`HTTP MCP init failed: ${initResponse.status} ${initResponse.statusText}`);\n }\n\n const toolsController = new AbortController();\n const toolsTimeout = setTimeout(() => toolsController.abort(), 10000);\n\n const toolsResponse = await fetch(endpoint, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n jsonrpc: '2.0',\n id: this.nextId(),\n method: 'tools/list',\n params: {},\n }),\n signal: toolsController.signal,\n });\n\n clearTimeout(toolsTimeout);\n\n if (toolsResponse.ok) {\n const data = await toolsResponse.json();\n connection.tools = data.result?.tools || data.tools || [];\n }\n }\n\n private sendRequest(\n name: string,\n method: string,\n params: Record<string, unknown>,\n ): Promise<any> {\n const connection = this.connections.get(name);\n\n if (!connection?.process) {\n return Promise.resolve(null);\n }\n\n return new Promise((resolve) => {\n const id = this.nextId();\n const request = JSON.stringify({ jsonrpc: '2.0', id, method, params });\n let resolved = false;\n\n const handler = (response: any) => {\n if (response.id === id && !resolved) {\n resolved = true;\n this.off(`response:${name}`, handler);\n resolve(response.result);\n }\n };\n\n this.on(`response:${name}`, handler);\n (connection.process as ChildProcess).stdin?.write(request + '\\n');\n\n setTimeout(() => {\n if (!resolved) {\n resolved = true;\n this.off(`response:${name}`, handler);\n resolve(null);\n }\n }, 15000);\n });\n }\n\n async callTool(\n mcpName: string,\n toolName: string,\n args: Record<string, unknown>,\n ): Promise<any> {\n const connection = this.connections.get(mcpName);\n\n if (!connection || connection.status !== 'connected') {\n throw new Error(`MCP ${mcpName} not connected`);\n }\n\n if (connection.config.type === 'stdio') {\n return this.sendRequest(mcpName, 'tools/call', { name: toolName, arguments: args });\n }\n\n if (connection.config.type === 'http') {\n const endpoint = connection.config.endpoint!;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (connection.config.env?.AUTH_TOKEN) {\n headers['Authorization'] = `Bearer ${connection.config.env.AUTH_TOKEN}`;\n }\n\n const response = await fetch(endpoint, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n jsonrpc: '2.0',\n id: this.nextId(),\n method: 'tools/call',\n params: { name: toolName, arguments: args },\n }),\n });\n\n const data = await response.json();\n return data.result || data;\n }\n\n return null;\n }\n\n getTools(name: string): McpTool[] {\n return this.connections.get(name)?.tools || [];\n }\n\n getStatus(name: string): string {\n return this.connections.get(name)?.status || 'unknown';\n }\n\n getAllStatuses(): Map<string, string> {\n const statuses = new Map<string, string>();\n for (const [name, conn] of this.connections) {\n statuses.set(name, conn.status);\n }\n return statuses;\n }\n\n private async reconnect(name: string): Promise<boolean> {\n const connection = this.connections.get(name);\n if (!connection) return false;\n\n if (connection.process) {\n try {\n (connection.process as ChildProcess).kill();\n } catch {}\n }\n\n connection.status = 'connecting';\n try {\n if (connection.config.type === 'stdio') {\n await this.connectStdio(name, connection);\n } else if (connection.config.type === 'http') {\n await this.connectHttp(name, connection);\n }\n connection.status = 'connected';\n this.emit('reconnected', name);\n return true;\n } catch {\n connection.status = 'error';\n return false;\n }\n }\n\n disconnect(name: string) {\n const connection = this.connections.get(name);\n\n if (connection?.process) {\n (connection.process as ChildProcess).kill();\n }\n\n this.stdioBuffers.delete(name);\n this.connections.delete(name);\n }\n\n disconnectAll() {\n for (const name of this.connections.keys()) {\n this.disconnect(name);\n }\n }\n\n private nextId(): string {\n return `${++this.requestIdCounter}`;\n }\n}\n"],"names":["McpClientService","EventEmitter","connect","name","config","connections","has","existing","get","status","connection","tools","set","type","command","connectStdio","endpoint","Error","connectHttp","error","args","env","proc","spawn","process","stdio","stdioBuffers","stdout","on","data","buffer","toString","lines","split","pop","line","trimmed","trim","parsed","JSON","parse","emit","stderr","msg","startsWith","console","code","delete","setTimeout","reconnect","catch","sendRequest","protocolVersion","capabilities","clientInfo","version","toolsResponse","headers","AUTH_TOKEN","URL","controller","AbortController","timeout","abort","initResponse","fetch","method","body","stringify","jsonrpc","id","nextId","params","signal","clearTimeout","ok","statusText","toolsController","toolsTimeout","json","result","Promise","resolve","request","resolved","handler","response","off","stdin","write","callTool","mcpName","toolName","arguments","getTools","getStatus","getAllStatuses","statuses","Map","conn","kill","disconnect","disconnectAll","keys","requestIdCounter"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../src/modules/mcp/services/mcp-client.service.ts"],"sourcesContent":["import { Injectable } from '@nestjs/common';\nimport { EventEmitter } from 'events';\nimport { spawn, ChildProcess } from 'child_process';\nimport { McpConfig, McpTool, McpConnection } from '../types';\nimport { auth } from '@modelcontextprotocol/sdk/client/auth.js';\nimport { CastOAuthProvider } from './cast-oauth-provider';\n\n@Injectable()\nexport class McpClientService extends EventEmitter {\n private connections: Map<string, McpConnection> = new Map();\n private stdioBuffers: Map<string, string> = new Map();\n private requestIdCounter = 0;\n\n async connect(name: string, config: McpConfig): Promise<boolean> {\n if (this.connections.has(name)) {\n const existing = this.connections.get(name)!;\n if (existing.status === 'connected') {\n return true;\n }\n }\n\n const connection: McpConnection = {\n config,\n tools: [],\n status: 'connecting',\n };\n\n this.connections.set(name, connection);\n\n try {\n if (config.type === 'stdio' && config.command) {\n await this.connectStdio(name, connection);\n } else if (config.type === 'sse' && config.endpoint) {\n throw new Error('SSE transport not yet supported. Use stdio or http instead.');\n } else if (config.type === 'http' && config.endpoint) {\n await this.connectHttp(name, connection);\n }\n\n connection.status = 'connected';\n return true;\n } catch (error) {\n connection.status = 'error';\n return false;\n }\n }\n\n private async connectStdio(name: string, connection: McpConnection): Promise<void> {\n const { command, args = [], env = {} } = connection.config;\n\n const proc = spawn(command!, args, {\n env: { ...process.env, ...env },\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n connection.process = proc;\n this.stdioBuffers.set(name, '');\n\n proc.stdout?.on('data', (data: Buffer) => {\n const buffer = (this.stdioBuffers.get(name) || '') + data.toString();\n const lines = buffer.split('\\n');\n\n this.stdioBuffers.set(name, lines.pop() || '');\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n const parsed = JSON.parse(trimmed);\n this.emit(`response:${name}`, parsed);\n } catch {\n }\n }\n });\n\n proc.stderr?.on('data', (data: Buffer) => {\n const msg = data.toString().trim();\n if (msg && !msg.startsWith('Debugger') && !msg.startsWith('Warning')) {\n console.error(`MCP ${name} stderr:`, msg);\n }\n });\n\n proc.on('close', (code) => {\n connection.status = 'disconnected';\n this.stdioBuffers.delete(name);\n this.emit('disconnected', name);\n\n if (code !== null && code !== 0) {\n setTimeout(() => {\n this.reconnect(name).catch(() => {});\n }, 3000);\n }\n });\n\n await this.sendRequest(name, 'initialize', {\n protocolVersion: '2024-11-05',\n capabilities: {},\n clientInfo: { name: 'cast-code', version: '1.0.0' },\n });\n\n const toolsResponse = await this.sendRequest(name, 'tools/list', {});\n connection.tools = toolsResponse?.tools || [];\n }\n\n private async connectHttp(name: string, connection: McpConnection): Promise<void> {\n const endpoint = connection.config.endpoint!;\n\n try {\n new URL(endpoint);\n } catch {\n throw new Error(`Invalid MCP HTTP endpoint: ${endpoint}`);\n }\n\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\n // Restore cached Bearer token if available (avoids re-running OAuth on reconnect)\n const provider = new CastOAuthProvider(name);\n const cachedTokens = provider.tokens();\n if (cachedTokens?.access_token) {\n headers['Authorization'] = `Bearer ${cachedTokens.access_token}`;\n }\n\n const doFetch = async (body: object, hdrs = headers, timeoutMs = 15000): Promise<Response> => {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n return await fetch(endpoint, {\n method: 'POST',\n headers: hdrs,\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n };\n\n const initBody = {\n jsonrpc: '2.0',\n id: this.nextId(),\n method: 'initialize',\n params: {\n protocolVersion: '2024-11-05',\n capabilities: {},\n clientInfo: { name: 'cast-code', version: '1.0.0' },\n },\n };\n\n let initResponse = await doFetch(initBody);\n\n // If 401: run full OAuth flow, then retry once with the new token\n if (initResponse.status === 401) {\n provider.invalidateCredentials('tokens');\n\n let authResult = await auth(provider, { serverUrl: endpoint });\n\n if (authResult === 'REDIRECT') {\n this.emit('oauth:browser-opened', name, provider.redirectUrl);\n const code = await provider.waitForCallback();\n authResult = await auth(provider, { serverUrl: endpoint, authorizationCode: code });\n }\n\n if (authResult !== 'AUTHORIZED') {\n throw new Error(`OAuth failed for ${name}`);\n }\n\n const tokens = provider.tokens();\n if (!tokens?.access_token) {\n throw new Error(`No access token for ${name} after OAuth`);\n }\n\n headers['Authorization'] = `Bearer ${tokens.access_token}`;\n initResponse = await doFetch(initBody);\n }\n\n if (!initResponse.ok) {\n throw new Error(`HTTP MCP init failed: ${initResponse.status} ${initResponse.statusText}`);\n }\n\n const sessionId = initResponse.headers.get('mcp-session-id');\n if (sessionId) headers['Mcp-Session-Id'] = sessionId;\n\n const toolsResponse = await doFetch({\n jsonrpc: '2.0',\n id: this.nextId(),\n method: 'tools/list',\n params: {},\n });\n\n if (toolsResponse.ok) {\n const data = await toolsResponse.json();\n connection.tools = data.result?.tools || data.tools || [];\n }\n\n (connection as any)._httpHeaders = headers;\n }\n\n private sendRequest(\n name: string,\n method: string,\n params: Record<string, unknown>,\n ): Promise<any> {\n const connection = this.connections.get(name);\n\n if (!connection?.process) {\n return Promise.resolve(null);\n }\n\n return new Promise((resolve) => {\n const id = this.nextId();\n const request = JSON.stringify({ jsonrpc: '2.0', id, method, params });\n let resolved = false;\n\n const handler = (response: any) => {\n if (response.id === id && !resolved) {\n resolved = true;\n this.off(`response:${name}`, handler);\n resolve(response.result);\n }\n };\n\n this.on(`response:${name}`, handler);\n (connection.process as ChildProcess).stdin?.write(request + '\\n');\n\n setTimeout(() => {\n if (!resolved) {\n resolved = true;\n this.off(`response:${name}`, handler);\n resolve(null);\n }\n }, 15000);\n });\n }\n\n async callTool(\n mcpName: string,\n toolName: string,\n args: Record<string, unknown>,\n ): Promise<any> {\n const connection = this.connections.get(mcpName);\n\n if (!connection || connection.status !== 'connected') {\n throw new Error(`MCP ${mcpName} not connected`);\n }\n\n if (connection.config.type === 'stdio') {\n return this.sendRequest(mcpName, 'tools/call', { name: toolName, arguments: args });\n }\n\n if (connection.config.type === 'http') {\n const endpoint = connection.config.endpoint!;\n // Use headers stored during connectHttp (includes Bearer token + session ID)\n const headers: Record<string, string> = (connection as any)._httpHeaders ?? {\n 'Content-Type': 'application/json',\n };\n\n const response = await fetch(endpoint, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n jsonrpc: '2.0',\n id: this.nextId(),\n method: 'tools/call',\n params: { name: toolName, arguments: args },\n }),\n });\n\n const data = await response.json();\n return data.result || data;\n }\n\n return null;\n }\n\n getTools(name: string): McpTool[] {\n return this.connections.get(name)?.tools || [];\n }\n\n getStatus(name: string): string {\n return this.connections.get(name)?.status || 'unknown';\n }\n\n getAuthUrl(name: string): string | undefined {\n return this.connections.get(name)?.authUrl;\n }\n\n getAllStatuses(): Map<string, string> {\n const statuses = new Map<string, string>();\n for (const [name, conn] of this.connections) {\n statuses.set(name, conn.status);\n }\n return statuses;\n }\n\n private async reconnect(name: string): Promise<boolean> {\n const connection = this.connections.get(name);\n if (!connection) return false;\n\n if (connection.process) {\n try {\n (connection.process as ChildProcess).kill();\n } catch {}\n }\n\n connection.status = 'connecting';\n try {\n if (connection.config.type === 'stdio') {\n await this.connectStdio(name, connection);\n } else if (connection.config.type === 'http') {\n await this.connectHttp(name, connection);\n }\n connection.status = 'connected';\n this.emit('reconnected', name);\n return true;\n } catch {\n connection.status = 'error';\n return false;\n }\n }\n\n disconnect(name: string) {\n const connection = this.connections.get(name);\n\n if (connection?.process) {\n (connection.process as ChildProcess).kill();\n }\n\n this.stdioBuffers.delete(name);\n this.connections.delete(name);\n }\n\n disconnectAll() {\n for (const name of this.connections.keys()) {\n this.disconnect(name);\n }\n }\n\n private nextId(): string {\n return `${++this.requestIdCounter}`;\n }\n}\n"],"names":["McpClientService","EventEmitter","connect","name","config","connections","has","existing","get","status","connection","tools","set","type","command","connectStdio","endpoint","Error","connectHttp","error","args","env","proc","spawn","process","stdio","stdioBuffers","stdout","on","data","buffer","toString","lines","split","pop","line","trimmed","trim","parsed","JSON","parse","emit","stderr","msg","startsWith","console","code","delete","setTimeout","reconnect","catch","sendRequest","protocolVersion","capabilities","clientInfo","version","toolsResponse","URL","headers","provider","CastOAuthProvider","cachedTokens","tokens","access_token","doFetch","body","hdrs","timeoutMs","controller","AbortController","timer","abort","fetch","method","stringify","signal","clearTimeout","initBody","jsonrpc","id","nextId","params","initResponse","invalidateCredentials","authResult","auth","serverUrl","redirectUrl","waitForCallback","authorizationCode","ok","statusText","sessionId","json","result","_httpHeaders","Promise","resolve","request","resolved","handler","response","off","stdin","write","callTool","mcpName","toolName","arguments","getTools","getStatus","getAuthUrl","authUrl","getAllStatuses","statuses","Map","conn","kill","disconnect","disconnectAll","keys","requestIdCounter"],"mappings":"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"}
@@ -28,6 +28,12 @@ let McpRegistryService = class McpRegistryService {
28
28
  registerMcp(name, config) {
29
29
  this.configs.set(name, config);
30
30
  }
31
+ getConfig(name) {
32
+ return this.configs.get(name);
33
+ }
34
+ getAuthUrl(name) {
35
+ return this.mcpClient.getAuthUrl(name);
36
+ }
31
37
  async connectMcp(name) {
32
38
  const config = this.configs.get(name);
33
39
  if (!config) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/modules/mcp/services/mcp-registry.service.ts"],"sourcesContent":["import { Injectable, OnModuleDestroy } from '@nestjs/common';\nimport { tool } from '@langchain/core/tools';\nimport { z } from 'zod';\nimport { StructuredTool } from '@langchain/core/tools';\nimport { McpClientService } from './mcp-client.service';\nimport { McpConfig, McpServerSummary } from '../types';\n\n@Injectable()\nexport class McpRegistryService implements OnModuleDestroy {\n private configs: Map<string, McpConfig> = new Map();\n\n constructor(private readonly mcpClient: McpClientService) {}\n\n onModuleDestroy() {\n this.mcpClient.disconnectAll();\n }\n\n registerMcp(name: string, config: McpConfig) {\n this.configs.set(name, config);\n }\n\n async connectMcp(name: string): Promise<boolean> {\n const config = this.configs.get(name);\n\n if (!config) {\n return false;\n }\n\n return this.mcpClient.connect(name, config);\n }\n\n async connectAll(): Promise<Map<string, boolean>> {\n const results = new Map<string, boolean>();\n\n for (const name of this.configs.keys()) {\n results.set(name, await this.connectMcp(name));\n }\n\n return results;\n }\n\n getMcpTools(name: string): StructuredTool[] {\n const mcpTools = this.mcpClient.getTools(name);\n\n return mcpTools.map((mcpTool) => {\n const schema = this.convertSchemaToZod(mcpTool.inputSchema);\n\n return tool(\n async (input) => {\n try {\n const result = await this.mcpClient.callTool(name, mcpTool.name, input);\n return JSON.stringify(result, null, 2);\n } catch (error) {\n return `Error calling ${mcpTool.name}: ${(error as Error).message}`;\n }\n },\n {\n name: `${name}_${mcpTool.name}`,\n description: mcpTool.description,\n schema,\n },\n );\n });\n }\n\n getAllMcpTools(): StructuredTool[] {\n const allTools: StructuredTool[] = [];\n\n for (const name of this.configs.keys()) {\n allTools.push(...this.getMcpTools(name));\n }\n\n return allTools;\n }\n\n private convertSchemaToZod(schema: Record<string, unknown>): z.ZodObject<any> {\n const properties = (schema.properties || {}) as Record<string, any>;\n const required = (schema.required || []) as string[];\n const zodShape: Record<string, z.ZodTypeAny> = {};\n\n for (const [key, prop] of Object.entries(properties)) {\n let zodType: z.ZodTypeAny;\n\n switch (prop.type) {\n case 'string':\n zodType = z.string();\n break;\n case 'number':\n zodType = z.number();\n break;\n case 'boolean':\n zodType = z.boolean();\n break;\n case 'array':\n zodType = z.array(z.any());\n break;\n case 'object':\n zodType = z.record(z.any());\n break;\n default:\n zodType = z.any();\n }\n\n if (prop.description) {\n zodType = zodType.describe(prop.description);\n }\n\n if (!required.includes(key)) {\n zodType = zodType.optional();\n }\n\n zodShape[key] = zodType;\n }\n\n return z.object(zodShape);\n }\n\n getServerSummaries(): McpServerSummary[] {\n const summaries: McpServerSummary[] = [];\n\n for (const [name, config] of this.configs) {\n const status = this.mcpClient.getStatus(name);\n const tools = this.mcpClient.getTools(name);\n\n summaries.push({\n name,\n transport: config.type,\n status,\n toolCount: tools.length,\n toolNames: tools.map(t => `${name}_${t.name}`),\n toolDescriptions: tools.map(t => ({\n name: `${name}_${t.name}`,\n description: t.description,\n })),\n });\n }\n\n return summaries;\n }\n\n getDiscoveryTools(): StructuredTool[] {\n return [\n tool(\n async () => {\n const summaries = this.getServerSummaries();\n if (summaries.length === 0) {\n return 'No MCP servers configured. Use the /mcp add command in the REPL to connect one.';\n }\n return summaries.map(s =>\n `${s.name} (${s.transport}) — ${s.status} — ${s.toolCount} tools`\n ).join('\\n');\n },\n {\n name: 'mcp_list_servers',\n description: 'List all connected MCP servers with their status, transport type, and tool count',\n schema: z.object({}),\n },\n ),\n tool(\n async (input) => {\n const summaries = this.getServerSummaries();\n\n if (input.server) {\n const server = summaries.find(s => s.name === input.server);\n if (!server) {\n return `Server \"${input.server}\" not found. Available: ${summaries.map(s => s.name).join(', ')}`;\n }\n if (server.toolDescriptions.length === 0) {\n return `Server \"${input.server}\" has no tools available (status: ${server.status})`;\n }\n return server.toolDescriptions.map(t =>\n `${t.name}: ${t.description}`\n ).join('\\n');\n }\n\n if (summaries.length === 0) {\n return 'No MCP servers configured.';\n }\n\n const sections: string[] = [];\n for (const s of summaries) {\n const header = `## ${s.name} (${s.transport}, ${s.status}) — ${s.toolCount} tools`;\n const toolList = s.toolDescriptions.map(t => `- ${t.name}: ${t.description}`).join('\\n');\n sections.push(`${header}\\n${toolList || '(no tools)'}`);\n }\n return sections.join('\\n\\n');\n },\n {\n name: 'mcp_list_tools',\n description: 'List tools from a specific MCP server or all servers. Optionally filter by server name.',\n schema: z.object({\n server: z.string().optional().describe('Server name to filter tools for. If omitted, lists all tools from all servers.'),\n }),\n },\n ),\n ];\n }\n\n loadConfigs(configs: Record<string, McpConfig>) {\n for (const [name, config] of Object.entries(configs)) {\n this.registerMcp(name, config);\n }\n }\n\n async addAndConnect(name: string, config: McpConfig): Promise<boolean> {\n this.registerMcp(name, config);\n return await this.connectMcp(name);\n }\n\n isConnected(name: string): boolean {\n return this.mcpClient.getStatus(name) === 'connected';\n }\n\n getConnectedServers(): string[] {\n const summaries = this.getServerSummaries();\n return summaries\n .filter(s => s.status === 'connected')\n .map(s => s.name);\n }\n}\n"],"names":["McpRegistryService","onModuleDestroy","mcpClient","disconnectAll","registerMcp","name","config","configs","set","connectMcp","get","connect","connectAll","results","Map","keys","getMcpTools","mcpTools","getTools","map","mcpTool","schema","convertSchemaToZod","inputSchema","tool","input","result","callTool","JSON","stringify","error","message","description","getAllMcpTools","allTools","push","properties","required","zodShape","key","prop","Object","entries","zodType","type","z","string","number","boolean","array","any","record","describe","includes","optional","object","getServerSummaries","summaries","status","getStatus","tools","transport","toolCount","length","toolNames","t","toolDescriptions","getDiscoveryTools","s","join","server","find","sections","header","toolList","loadConfigs","addAndConnect","isConnected","getConnectedServers","filter"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../src/modules/mcp/services/mcp-registry.service.ts"],"sourcesContent":["import { Injectable, OnModuleDestroy } from '@nestjs/common';\nimport { tool } from '@langchain/core/tools';\nimport { z } from 'zod';\nimport { StructuredTool } from '@langchain/core/tools';\nimport { McpClientService } from './mcp-client.service';\nimport { McpConfig, McpServerSummary } from '../types';\n\n@Injectable()\nexport class McpRegistryService implements OnModuleDestroy {\n private configs: Map<string, McpConfig> = new Map();\n\n constructor(private readonly mcpClient: McpClientService) {}\n\n onModuleDestroy() {\n this.mcpClient.disconnectAll();\n }\n\n registerMcp(name: string, config: McpConfig) {\n this.configs.set(name, config);\n }\n\n getConfig(name: string): McpConfig | undefined {\n return this.configs.get(name);\n }\n\n getAuthUrl(name: string): string | undefined {\n return this.mcpClient.getAuthUrl(name);\n }\n\n async connectMcp(name: string): Promise<boolean> {\n const config = this.configs.get(name);\n\n if (!config) {\n return false;\n }\n\n return this.mcpClient.connect(name, config);\n }\n\n async connectAll(): Promise<Map<string, boolean>> {\n const results = new Map<string, boolean>();\n\n for (const name of this.configs.keys()) {\n results.set(name, await this.connectMcp(name));\n }\n\n return results;\n }\n\n getMcpTools(name: string): StructuredTool[] {\n const mcpTools = this.mcpClient.getTools(name);\n\n return mcpTools.map((mcpTool) => {\n const schema = this.convertSchemaToZod(mcpTool.inputSchema);\n\n return tool(\n async (input) => {\n try {\n const result = await this.mcpClient.callTool(name, mcpTool.name, input);\n return JSON.stringify(result, null, 2);\n } catch (error) {\n return `Error calling ${mcpTool.name}: ${(error as Error).message}`;\n }\n },\n {\n name: `${name}_${mcpTool.name}`,\n description: mcpTool.description,\n schema,\n },\n );\n });\n }\n\n getAllMcpTools(): StructuredTool[] {\n const allTools: StructuredTool[] = [];\n\n for (const name of this.configs.keys()) {\n allTools.push(...this.getMcpTools(name));\n }\n\n return allTools;\n }\n\n private convertSchemaToZod(schema: Record<string, unknown>): z.ZodObject<any> {\n const properties = (schema.properties || {}) as Record<string, any>;\n const required = (schema.required || []) as string[];\n const zodShape: Record<string, z.ZodTypeAny> = {};\n\n for (const [key, prop] of Object.entries(properties)) {\n let zodType: z.ZodTypeAny;\n\n switch (prop.type) {\n case 'string':\n zodType = z.string();\n break;\n case 'number':\n zodType = z.number();\n break;\n case 'boolean':\n zodType = z.boolean();\n break;\n case 'array':\n zodType = z.array(z.any());\n break;\n case 'object':\n zodType = z.record(z.any());\n break;\n default:\n zodType = z.any();\n }\n\n if (prop.description) {\n zodType = zodType.describe(prop.description);\n }\n\n if (!required.includes(key)) {\n zodType = zodType.optional();\n }\n\n zodShape[key] = zodType;\n }\n\n return z.object(zodShape);\n }\n\n getServerSummaries(): McpServerSummary[] {\n const summaries: McpServerSummary[] = [];\n\n for (const [name, config] of this.configs) {\n const status = this.mcpClient.getStatus(name);\n const tools = this.mcpClient.getTools(name);\n\n summaries.push({\n name,\n transport: config.type,\n status,\n toolCount: tools.length,\n toolNames: tools.map(t => `${name}_${t.name}`),\n toolDescriptions: tools.map(t => ({\n name: `${name}_${t.name}`,\n description: t.description,\n })),\n });\n }\n\n return summaries;\n }\n\n getDiscoveryTools(): StructuredTool[] {\n return [\n tool(\n async () => {\n const summaries = this.getServerSummaries();\n if (summaries.length === 0) {\n return 'No MCP servers configured. Use the /mcp add command in the REPL to connect one.';\n }\n return summaries.map(s =>\n `${s.name} (${s.transport}) — ${s.status} — ${s.toolCount} tools`\n ).join('\\n');\n },\n {\n name: 'mcp_list_servers',\n description: 'List all connected MCP servers with their status, transport type, and tool count',\n schema: z.object({}),\n },\n ),\n tool(\n async (input) => {\n const summaries = this.getServerSummaries();\n\n if (input.server) {\n const server = summaries.find(s => s.name === input.server);\n if (!server) {\n return `Server \"${input.server}\" not found. Available: ${summaries.map(s => s.name).join(', ')}`;\n }\n if (server.toolDescriptions.length === 0) {\n return `Server \"${input.server}\" has no tools available (status: ${server.status})`;\n }\n return server.toolDescriptions.map(t =>\n `${t.name}: ${t.description}`\n ).join('\\n');\n }\n\n if (summaries.length === 0) {\n return 'No MCP servers configured.';\n }\n\n const sections: string[] = [];\n for (const s of summaries) {\n const header = `## ${s.name} (${s.transport}, ${s.status}) — ${s.toolCount} tools`;\n const toolList = s.toolDescriptions.map(t => `- ${t.name}: ${t.description}`).join('\\n');\n sections.push(`${header}\\n${toolList || '(no tools)'}`);\n }\n return sections.join('\\n\\n');\n },\n {\n name: 'mcp_list_tools',\n description: 'List tools from a specific MCP server or all servers. Optionally filter by server name.',\n schema: z.object({\n server: z.string().optional().describe('Server name to filter tools for. If omitted, lists all tools from all servers.'),\n }),\n },\n ),\n ];\n }\n\n loadConfigs(configs: Record<string, McpConfig>) {\n for (const [name, config] of Object.entries(configs)) {\n this.registerMcp(name, config);\n }\n }\n\n async addAndConnect(name: string, config: McpConfig): Promise<boolean> {\n this.registerMcp(name, config);\n return await this.connectMcp(name);\n }\n\n isConnected(name: string): boolean {\n return this.mcpClient.getStatus(name) === 'connected';\n }\n\n getConnectedServers(): string[] {\n const summaries = this.getServerSummaries();\n return summaries\n .filter(s => s.status === 'connected')\n .map(s => s.name);\n }\n}\n"],"names":["McpRegistryService","onModuleDestroy","mcpClient","disconnectAll","registerMcp","name","config","configs","set","getConfig","get","getAuthUrl","connectMcp","connect","connectAll","results","Map","keys","getMcpTools","mcpTools","getTools","map","mcpTool","schema","convertSchemaToZod","inputSchema","tool","input","result","callTool","JSON","stringify","error","message","description","getAllMcpTools","allTools","push","properties","required","zodShape","key","prop","Object","entries","zodType","type","z","string","number","boolean","array","any","record","describe","includes","optional","object","getServerSummaries","summaries","status","getStatus","tools","transport","toolCount","length","toolNames","t","toolDescriptions","getDiscoveryTools","s","join","server","find","sections","header","toolList","loadConfigs","addAndConnect","isConnected","getConnectedServers","filter"],"mappings":"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"}
@@ -9,11 +9,54 @@ Object.defineProperty(exports, "McpCommandsService", {
9
9
  }
10
10
  });
11
11
  const _common = require("@nestjs/common");
12
+ const _fs = /*#__PURE__*/ _interop_require_wildcard(require("fs"));
13
+ const _path = /*#__PURE__*/ _interop_require_wildcard(require("path"));
12
14
  const _theme = require("../../utils/theme");
13
15
  const _mcpregistryservice = require("../../../mcp/services/mcp-registry.service");
14
16
  const _mcpclientservice = require("../../../mcp/services/mcp-client.service");
15
17
  const _promptswithesc = require("../../utils/prompts-with-esc");
16
18
  const _mcptemplates = require("../../../mcp/catalog/mcp-templates");
19
+ function _getRequireWildcardCache(nodeInterop) {
20
+ if (typeof WeakMap !== "function") return null;
21
+ var cacheBabelInterop = new WeakMap();
22
+ var cacheNodeInterop = new WeakMap();
23
+ return (_getRequireWildcardCache = function(nodeInterop) {
24
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
25
+ })(nodeInterop);
26
+ }
27
+ function _interop_require_wildcard(obj, nodeInterop) {
28
+ if (!nodeInterop && obj && obj.__esModule) {
29
+ return obj;
30
+ }
31
+ if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
32
+ return {
33
+ default: obj
34
+ };
35
+ }
36
+ var cache = _getRequireWildcardCache(nodeInterop);
37
+ if (cache && cache.has(obj)) {
38
+ return cache.get(obj);
39
+ }
40
+ var newObj = {
41
+ __proto__: null
42
+ };
43
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
44
+ for(var key in obj){
45
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
46
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
47
+ if (desc && (desc.get || desc.set)) {
48
+ Object.defineProperty(newObj, key, desc);
49
+ } else {
50
+ newObj[key] = obj[key];
51
+ }
52
+ }
53
+ }
54
+ newObj.default = obj;
55
+ if (cache) {
56
+ cache.set(obj, newObj);
57
+ }
58
+ return newObj;
59
+ }
17
60
  function _ts_decorate(decorators, target, key, desc) {
18
61
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
19
62
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -26,38 +69,38 @@ function _ts_metadata(k, v) {
26
69
  let McpCommandsService = class McpCommandsService {
27
70
  async cmdMcp(args, smartInput) {
28
71
  const sub = args[0] || 'menu';
29
- const w = (s)=>process.stdout.write(s);
72
+ // 'menu' uses SmartInput directly — must NOT pause before it
73
+ if (sub === 'menu') {
74
+ try {
75
+ await this.showMcpMenu(smartInput);
76
+ } catch (error) {
77
+ if (error instanceof _promptswithesc.CancelledPromptError || error?.name === 'CancelledPromptError') {
78
+ console.log((0, _theme.colorize)('\n❌ Cancelado. Voltando ao chat...\n', 'warning'));
79
+ } else {
80
+ throw error;
81
+ }
82
+ }
83
+ return;
84
+ }
85
+ // Other sub-commands use inquirer and need pause/resume
30
86
  smartInput.pause();
31
87
  try {
32
88
  switch(sub){
33
- case 'menu':
34
- await this.showMcpMenu(smartInput);
35
- break;
36
89
  case 'list':
37
- {
38
- await this.listServers();
39
- break;
40
- }
90
+ await this.listServers();
91
+ break;
41
92
  case 'tools':
42
- {
43
- await this.listTools();
44
- break;
45
- }
93
+ await this.listTools();
94
+ break;
46
95
  case 'add':
47
- {
48
- await this.addMcpWizard(smartInput);
49
- break;
50
- }
96
+ await this.addMcpWizard(smartInput);
97
+ break;
51
98
  case 'remove':
52
- {
53
- await this.removeMcpWizard(smartInput);
54
- break;
55
- }
99
+ await this.removeMcpWizard(smartInput);
100
+ break;
56
101
  case 'test':
57
- {
58
- await this.testMcpTool(smartInput);
59
- break;
60
- }
102
+ await this.testMcpTool(smartInput);
103
+ break;
61
104
  case 'what':
62
105
  case 'about':
63
106
  this.printWhatIsMcp();
@@ -100,24 +143,24 @@ let McpCommandsService = class McpCommandsService {
100
143
  },
101
144
  {
102
145
  key: '3',
146
+ label: 'Conectar servidores',
147
+ description: 'Conectar/reconectar MCPs configurados'
148
+ },
149
+ {
150
+ key: '4',
103
151
  label: 'Adicionar servidor',
104
152
  description: 'Configurar novo MCP'
105
153
  },
106
154
  {
107
- key: '4',
155
+ key: '5',
108
156
  label: 'Remover servidor',
109
157
  description: 'Desconectar MCP'
110
158
  },
111
159
  {
112
- key: '5',
160
+ key: '6',
113
161
  label: 'O que é MCP?',
114
162
  description: 'Entenda o protocolo'
115
163
  },
116
- {
117
- key: '6',
118
- label: 'Como criar um MCP',
119
- description: 'Guia de desenvolvimento'
120
- },
121
164
  {
122
165
  key: 'q',
123
166
  label: 'Voltar',
@@ -128,6 +171,7 @@ let McpCommandsService = class McpCommandsService {
128
171
  console.log((0, _theme.colorize)('\nSaindo do MCP Hub...\n', 'muted'));
129
172
  return;
130
173
  }
174
+ let pause = true;
131
175
  switch(action){
132
176
  case '1':
133
177
  await this.listServers();
@@ -136,20 +180,25 @@ let McpCommandsService = class McpCommandsService {
136
180
  await this.listTools();
137
181
  break;
138
182
  case '3':
139
- await this.addMcpWizard(smartInput);
183
+ await this.connectServers(smartInput);
140
184
  break;
141
185
  case '4':
142
- await this.removeMcpWizard(smartInput);
186
+ await this.addMcpWizard(smartInput);
187
+ pause = false;
143
188
  break;
144
189
  case '5':
145
- this.printWhatIsMcp();
190
+ await this.removeMcpWizard(smartInput);
191
+ pause = false;
146
192
  break;
147
193
  case '6':
148
- this.printHowToCreate();
194
+ this.printWhatIsMcp();
149
195
  break;
150
196
  case 'q':
151
197
  return;
152
198
  }
199
+ if (pause) {
200
+ await smartInput.question((0, _theme.colorize)('\nEnter para continuar...', 'muted'));
201
+ }
153
202
  }
154
203
  }
155
204
  async withEsc(fn) {
@@ -162,6 +211,73 @@ let McpCommandsService = class McpCommandsService {
162
211
  throw error;
163
212
  }
164
213
  }
214
+ async connectServers(smartInput) {
215
+ const w = (s)=>process.stdout.write(s);
216
+ const summaries = this.mcpRegistry.getServerSummaries();
217
+ w('\r\n');
218
+ w((0, _theme.colorize)(_theme.Icons.cloud + ' ', 'accent') + (0, _theme.colorize)('Conectar Servidores MCP', 'bold') + '\r\n');
219
+ w((0, _theme.colorize)(_theme.Box.horizontal.repeat(40), 'subtle') + '\r\n\n');
220
+ if (summaries.length === 0) {
221
+ w(` ${(0, _theme.colorize)('Nenhum servidor configurado. Use a opção Adicionar servidor.', 'muted')}\r\n\n`);
222
+ return;
223
+ }
224
+ w(` ${(0, _theme.colorize)('Conectando ' + summaries.length + ' servidor(es)...', 'muted')}\r\n\n`);
225
+ const results = await this.mcpRegistry.connectAll();
226
+ for (const [name, ok] of results.entries()){
227
+ const icon = ok ? (0, _theme.colorize)('●', 'success') : (0, _theme.colorize)('○', 'error');
228
+ const status = ok ? (0, _theme.colorize)('conectado', 'success') : (0, _theme.colorize)('falhou', 'error');
229
+ w(` ${icon} ${(0, _theme.colorize)(name, 'cyan')} — ${status}\r\n`);
230
+ if (!ok) {
231
+ const config = this.mcpRegistry.getConfig(name);
232
+ // OAuth/HTTP: the SDK already ran the full flow. Failure = server blocked DCR
233
+ if (config?.type === 'http') {
234
+ w(`\r\n ${(0, _theme.colorize)('⚠️ ' + name + ': OAuth bloqueado pelo servidor', 'warning')}\r\n`);
235
+ w(` Este servidor só aceita clientes pré-aprovados (ex: VS Code, Cursor).\r\n`);
236
+ w(` Alternativa: use o proxy mcp-remote para redirecionar via cliente aprovado.\r\n\r\n`);
237
+ // stdio server — check for missing credentials
238
+ } else if (config?.type === 'stdio' && smartInput) {
239
+ const template = (0, _mcptemplates.getTemplate)(name);
240
+ if (template?.credentials?.length) {
241
+ const missing = template.credentials.filter((cred)=>{
242
+ if (cred.isArg) return false; // args are set at add time
243
+ return !config.env?.[cred.envVar];
244
+ });
245
+ if (missing.length > 0) {
246
+ w(`\r\n ${(0, _theme.colorize)('🔑 Credenciais necessárias para ' + name, 'warning')}\r\n\r\n`);
247
+ const mcpDir = _path.join(process.cwd(), '.cast', 'mcp');
248
+ const filePath = _path.join(mcpDir, `${name}.json`);
249
+ const updatedConfig = JSON.parse(JSON.stringify(config));
250
+ if (!updatedConfig.env) updatedConfig.env = {};
251
+ for (const cred of missing){
252
+ const value = await smartInput.question((0, _theme.colorize)(` ${cred.name} (${cred.placeholder}): `, 'cyan'));
253
+ if (value.trim()) {
254
+ updatedConfig.env[cred.envVar] = value.trim();
255
+ }
256
+ }
257
+ // Save updated config
258
+ _fs.writeFileSync(filePath, JSON.stringify({
259
+ [name]: updatedConfig
260
+ }, null, 2));
261
+ this.mcpRegistry.registerMcp(name, updatedConfig);
262
+ // Retry connection
263
+ w(`\r\n ${(0, _theme.colorize)('Reconectando ' + name + '...', 'muted')}\r\n`);
264
+ const retryOk = await this.mcpRegistry.connectMcp(name);
265
+ const retryIcon = retryOk ? (0, _theme.colorize)('●', 'success') : (0, _theme.colorize)('○', 'error');
266
+ const retryStatus = retryOk ? (0, _theme.colorize)('conectado', 'success') : (0, _theme.colorize)('falhou — verifique a credencial', 'error');
267
+ w(` ${retryIcon} ${(0, _theme.colorize)(name, 'cyan')} — ${retryStatus}\r\n\r\n`);
268
+ } else {
269
+ w(` ${(0, _theme.colorize)('Verifique se o servidor está disponível e tente novamente.', 'muted')}\r\n\r\n`);
270
+ }
271
+ }
272
+ }
273
+ }
274
+ }
275
+ const finalResults = await this.mcpRegistry.connectAll();
276
+ const connected = [
277
+ ...finalResults.values()
278
+ ].filter(Boolean).length;
279
+ w(` ${(0, _theme.colorize)(`${connected}/${finalResults.size} conectado(s)`, connected === finalResults.size ? 'success' : 'warning')}\r\n\r\n`);
280
+ }
165
281
  async listServers() {
166
282
  const w = (s)=>process.stdout.write(s);
167
283
  const summaries = this.mcpRegistry.getServerSummaries();
@@ -212,11 +328,9 @@ let McpCommandsService = class McpCommandsService {
212
328
  w('\r\n');
213
329
  }
214
330
  async addMcpWizard(smartInput) {
215
- const fs = require('fs');
216
- const path = require('path');
217
- const mcpDir = path.join(process.cwd(), '.cast', 'mcp');
218
- if (!fs.existsSync(mcpDir)) {
219
- fs.mkdirSync(mcpDir, {
331
+ const mcpDir = _path.join(process.cwd(), '.cast', 'mcp');
332
+ if (!_fs.existsSync(mcpDir)) {
333
+ _fs.mkdirSync(mcpDir, {
220
334
  recursive: true
221
335
  });
222
336
  }
@@ -321,12 +435,35 @@ let McpCommandsService = class McpCommandsService {
321
435
  }
322
436
  }
323
437
  }
324
- const filePath = path.join(mcpDir, `${name}.json`);
325
- fs.writeFileSync(filePath, JSON.stringify({
438
+ const filePath = _path.join(mcpDir, `${name}.json`);
439
+ _fs.writeFileSync(filePath, JSON.stringify({
326
440
  [name]: config
327
441
  }, null, 2));
328
442
  w(`\r\n${(0, _theme.colorize)('✓', 'success')} MCP configurado: ${(0, _theme.colorize)(filePath, 'accent')}\r\n`);
329
- if (template.config.type === 'http') {
443
+ if (name === 'figma') {
444
+ w('\r\n');
445
+ w((0, _theme.colorize)(' ─────────────────────────────────────────\r\n', 'subtle'));
446
+ w((0, _theme.colorize)(' Tutorial — Figma Desktop MCP\r\n', 'bold'));
447
+ w((0, _theme.colorize)(' ─────────────────────────────────────────\r\n', 'subtle'));
448
+ w('\r\n');
449
+ w((0, _theme.colorize)(' Passo 1: ', 'accent') + 'Instale o Figma Desktop\r\n');
450
+ w((0, _theme.colorize)(' https://www.figma.com/downloads/\r\n', 'muted'));
451
+ w('\r\n');
452
+ w((0, _theme.colorize)(' Passo 2: ', 'accent') + 'Abra qualquer arquivo de Design no Figma\r\n');
453
+ w('\r\n');
454
+ w((0, _theme.colorize)(' Passo 3: ', 'accent') + 'Ative o Dev Mode\r\n');
455
+ w((0, _theme.colorize)(' Clique no botão "<>" no canto superior direito\r\n', 'muted'));
456
+ w('\r\n');
457
+ w((0, _theme.colorize)(' Passo 4: ', 'accent') + 'Habilite o servidor MCP\r\n');
458
+ w((0, _theme.colorize)(' Painel Inspect → seção MCP\r\n', 'muted'));
459
+ w((0, _theme.colorize)(' Ative "Enable desktop MCP server"\r\n', 'muted'));
460
+ w('\r\n');
461
+ w((0, _theme.colorize)(' Passo 5: ', 'accent') + 'Conecte via Cast\r\n');
462
+ w((0, _theme.colorize)(' Reinicie o Cast e use /mcp → Conectar servidores\r\n', 'muted'));
463
+ w('\r\n');
464
+ w((0, _theme.colorize)(' ─────────────────────────────────────────\r\n', 'subtle'));
465
+ w('\r\n');
466
+ } else if (template.config.type === 'http') {
330
467
  w((0, _theme.colorize)('\r\n ⚠️ Servidor HTTP/OAuth detectado!\r\n', 'warning'));
331
468
  w((0, _theme.colorize)(' Autenticação pode ser necessária após conectar.\r\n\r\n', 'muted'));
332
469
  }
@@ -410,22 +547,20 @@ let McpCommandsService = class McpCommandsService {
410
547
  }
411
548
  config.endpoint = endpoint.trim();
412
549
  }
413
- const filePath = path.join(mcpDir, `${name.trim().toLowerCase()}.json`);
414
- fs.writeFileSync(filePath, JSON.stringify({
550
+ const filePath = _path.join(mcpDir, `${name.trim().toLowerCase()}.json`);
551
+ _fs.writeFileSync(filePath, JSON.stringify({
415
552
  [name.trim()]: config
416
553
  }, null, 2));
417
554
  w(`\r\n${(0, _theme.colorize)('✓', 'success')} Config salva: ${(0, _theme.colorize)(filePath, 'accent')}\r\n`);
418
555
  w((0, _theme.colorize)(' Reinicie o Cast para conectar\r\n\r\n', 'muted'));
419
556
  }
420
557
  async removeMcpWizard(smartInput) {
421
- const fs = require('fs');
422
- const path = require('path');
423
- const mcpDir = path.join(process.cwd(), '.cast', 'mcp');
424
- if (!fs.existsSync(mcpDir)) {
558
+ const mcpDir = _path.join(process.cwd(), '.cast', 'mcp');
559
+ if (!_fs.existsSync(mcpDir)) {
425
560
  console.log((0, _theme.colorize)('\nNenhum MCP configurado\n', 'muted'));
426
561
  return;
427
562
  }
428
- const files = fs.readdirSync(mcpDir).filter((f)=>f.endsWith('.json'));
563
+ const files = _fs.readdirSync(mcpDir).filter((f)=>f.endsWith('.json'));
429
564
  if (files.length === 0) {
430
565
  console.log((0, _theme.colorize)('\nNenhum MCP configurado\n', 'muted'));
431
566
  return;
@@ -446,7 +581,7 @@ let McpCommandsService = class McpCommandsService {
446
581
  default: false
447
582
  });
448
583
  if (confirmRemove) {
449
- fs.unlinkSync(path.join(mcpDir, toRemove));
584
+ _fs.unlinkSync(_path.join(mcpDir, toRemove));
450
585
  console.log((0, _theme.colorize)(`\n✓ Servidor removido\n`, 'success'));
451
586
  }
452
587
  }