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.
- package/dist/common/services/config.service.js +0 -3
- package/dist/common/services/config.service.js.map +1 -1
- package/dist/main.js +3 -1
- package/dist/main.js.map +1 -1
- package/dist/modules/mcp/catalog/mcp-templates.js +13 -2
- package/dist/modules/mcp/catalog/mcp-templates.js.map +1 -1
- package/dist/modules/mcp/services/cast-oauth-provider.js +187 -0
- package/dist/modules/mcp/services/cast-oauth-provider.js.map +1 -0
- package/dist/modules/mcp/services/mcp-client.service.js +75 -43
- package/dist/modules/mcp/services/mcp-client.service.js.map +1 -1
- package/dist/modules/mcp/services/mcp-registry.service.js +6 -0
- package/dist/modules/mcp/services/mcp-registry.service.js.map +1 -1
- package/dist/modules/repl/services/commands/mcp-commands.service.js +186 -51
- package/dist/modules/repl/services/commands/mcp-commands.service.js.map +1 -1
- package/package.json +2 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
39
|
-
break;
|
|
40
|
-
}
|
|
90
|
+
await this.listServers();
|
|
91
|
+
break;
|
|
41
92
|
case 'tools':
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
break;
|
|
45
|
-
}
|
|
93
|
+
await this.listTools();
|
|
94
|
+
break;
|
|
46
95
|
case 'add':
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
break;
|
|
50
|
-
}
|
|
96
|
+
await this.addMcpWizard(smartInput);
|
|
97
|
+
break;
|
|
51
98
|
case 'remove':
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
break;
|
|
55
|
-
}
|
|
99
|
+
await this.removeMcpWizard(smartInput);
|
|
100
|
+
break;
|
|
56
101
|
case 'test':
|
|
57
|
-
|
|
58
|
-
|
|
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: '
|
|
155
|
+
key: '5',
|
|
108
156
|
label: 'Remover servidor',
|
|
109
157
|
description: 'Desconectar MCP'
|
|
110
158
|
},
|
|
111
159
|
{
|
|
112
|
-
key: '
|
|
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.
|
|
183
|
+
await this.connectServers(smartInput);
|
|
140
184
|
break;
|
|
141
185
|
case '4':
|
|
142
|
-
await this.
|
|
186
|
+
await this.addMcpWizard(smartInput);
|
|
187
|
+
pause = false;
|
|
143
188
|
break;
|
|
144
189
|
case '5':
|
|
145
|
-
this.
|
|
190
|
+
await this.removeMcpWizard(smartInput);
|
|
191
|
+
pause = false;
|
|
146
192
|
break;
|
|
147
193
|
case '6':
|
|
148
|
-
this.
|
|
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
|
|
216
|
-
|
|
217
|
-
|
|
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 =
|
|
325
|
-
|
|
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 (
|
|
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 =
|
|
414
|
-
|
|
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
|
|
422
|
-
|
|
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 =
|
|
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
|
-
|
|
584
|
+
_fs.unlinkSync(_path.join(mcpDir, toRemove));
|
|
450
585
|
console.log((0, _theme.colorize)(`\n✓ Servidor removido\n`, 'success'));
|
|
451
586
|
}
|
|
452
587
|
}
|