diff --git a/package-lock.json b/package-lock.json index de07d47..c29f108 100644 --- a/package-lock.json +++ b/package-lock.json @@ -539,6 +539,16 @@ "w3c-keyname": "^2.2.4" } }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/@ctrl/tinycolor": { "version": "3.6.1", "license": "MIT", @@ -1759,7 +1769,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -2085,6 +2094,21 @@ "node": ">=6.0" } }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -2456,7 +2480,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/emojis-list": { @@ -3519,7 +3542,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5603,7 +5625,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -5618,7 +5639,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -6820,6 +6840,7 @@ "@modelcontextprotocol/sdk": "^1.12.1", "@seald-io/nedb": "^4.1.1", "axios": "^1.9.0", + "cli-table3": "^0.6.5", "https-proxy-agent": "^7.0.6", "open": "^10.1.2", "openai": "^5.0.1", diff --git a/renderer/src/components/main-panel/chat/core/task-loop.ts b/renderer/src/components/main-panel/chat/core/task-loop.ts index 3d9beb2..d58f5cd 100644 --- a/renderer/src/components/main-panel/chat/core/task-loop.ts +++ b/renderer/src/components/main-panel/chat/core/task-loop.ts @@ -63,8 +63,6 @@ export class TaskLoop { // 根据当前环境决定是否要开启 messageBridge const platform = getPlatform(); - console.log('current platform is', platform); - if (platform === 'nodejs') { const adapter = taskOptions.adapter; @@ -73,7 +71,6 @@ export class TaskLoop { } createMessageBridge(adapter.emitter); - console.log('mcpClientAdapter launch'); this.nodejsStatus.connectionFut = mcpClientAdapter.launch(); } @@ -143,8 +140,6 @@ export class TaskLoop { // data.code 一定为 200,否则不会走这个 route const { chunk } = data.msg as { chunk: ChatCompletionChunk }; - console.log(chunk); - // 处理增量的 content 和 tool_calls this.handleChunkDeltaContent(chunk); this.handleChunkDeltaToolCalls(chunk, toolcallIndexAdapter); @@ -185,6 +180,10 @@ export class TaskLoop { }); } + public setProxyServer(proxyServer: string) { + mcpSetting.proxyServer = proxyServer; + } + public makeChatData(tabStorage: ChatStorage): ChatCompletionCreateParamsBase | undefined { const baseURL = this.getLlmConfig().baseUrl; const apiKey = this.getLlmConfig().userToken || ''; @@ -197,7 +196,7 @@ export class TaskLoop { } return undefined; } - + const model = this.getLlmConfig().userModel; const temperature = tabStorage.settings.temperature; const tools = getToolSchema(tabStorage.settings.enableTools); @@ -332,15 +331,19 @@ export class TaskLoop { await this.nodejsStatus.connectionFut; } - const allTools = {} as Record; + const allTools = [] as ToolItem[]; for (const client of mcpClientAdapter.clients) { if (!client.connected) { continue; } const tools = await client.getTools(); - const clientName = client.connectionResult.name as string; - allTools[clientName] = Array.from(tools.values()); + allTools.push(...Array.from(tools.values()).map( + item => ({ + ...item, + enabled: true + }) + )); } return allTools; @@ -395,9 +398,6 @@ export class TaskLoop { // 发送请求 const doConverationResult = await this.doConversation(chatData, toolcallIndexAdapter); - console.log('[doConverationResult] Response'); - console.log(doConverationResult); - // 如果存在需要调度的工具 if (this.streamingToolCalls.value.length > 0) { diff --git a/renderer/src/views/connect/core.ts b/renderer/src/views/connect/core.ts index 5704c23..b9dff1c 100644 --- a/renderer/src/views/connect/core.ts +++ b/renderer/src/views/connect/core.ts @@ -200,7 +200,6 @@ export class McpClient { this.tools = new Map(); msg.tools.forEach(tool => { const standardSchema = _processSchemaNode(tool.inputSchema, tool.inputSchema.$defs || {}); - console.log(standardSchema); tool.inputSchema = standardSchema; @@ -530,7 +529,6 @@ class McpClientAdapter { } const launchSignature = await this.getLaunchSignature(); - console.log('launchSignature', launchSignature); let allOk = true; diff --git a/resources/openmcp-sdk-release/package.json b/resources/openmcp-sdk-release/package.json index 75b108b..e42d5fd 100644 --- a/resources/openmcp-sdk-release/package.json +++ b/resources/openmcp-sdk-release/package.json @@ -35,6 +35,7 @@ "uuid": "^11.1.0", "open": "^10.1.2", "ws": "^8.18.1", + "cli-table3": "^0.6.5", "https-proxy-agent": "^7.0.6" } } \ No newline at end of file diff --git a/resources/openmcp-sdk-release/task-loop.d.ts b/resources/openmcp-sdk-release/task-loop.d.ts index 8914f55..3b9eb7a 100644 --- a/resources/openmcp-sdk-release/task-loop.d.ts +++ b/resources/openmcp-sdk-release/task-loop.d.ts @@ -29,6 +29,7 @@ export interface ToolItem { name: string; description: string; inputSchema: InputSchema; + enabled: boolean; anyOf?: any; } @@ -160,10 +161,16 @@ export class TaskLoop { bindStreaming(content: Ref, toolCalls: Ref): void; connectToService(): Promise; + /** + * @description 设置代理服务器 + * @param proxyServer + */ + setProxyServer(proxyServer: string): void; + /** * @description 获取所有可用的工具列表 */ - listTools(): Promise>; + listTools(): Promise; /** * @description 开启循环,异步更新 DOM diff --git a/service/package.json b/service/package.json index 9560d0d..9309020 100644 --- a/service/package.json +++ b/service/package.json @@ -39,6 +39,7 @@ "@modelcontextprotocol/sdk": "^1.12.1", "@seald-io/nedb": "^4.1.1", "axios": "^1.9.0", + "cli-table3": "^0.6.5", "https-proxy-agent": "^7.0.6", "open": "^10.1.2", "openai": "^5.0.1", diff --git a/service/src/common/router.ts b/service/src/common/router.ts index 54da9b5..c9d53fb 100644 --- a/service/src/common/router.ts +++ b/service/src/common/router.ts @@ -30,7 +30,7 @@ export async function routeMessage(command: string, data: any, webview: PostMess webview.postMessage({ command, data: res }); } } catch (error) { - console.error(error); + // console.error(error); webview.postMessage({ command, data: { code: 500, diff --git a/service/src/hook/adapter.ts b/service/src/hook/adapter.ts index 42c9acd..964cce1 100644 --- a/service/src/hook/adapter.ts +++ b/service/src/hook/adapter.ts @@ -1,7 +1,7 @@ import { WebSocket } from 'ws'; import { EventEmitter } from 'events'; import { routeMessage } from '../common/router.js'; -import { McpOptions } from '../mcp/client.dto.js'; +import { ConnectionType, McpOptions } from '../mcp/client.dto.js'; import { clientMap, connectService } from '../mcp/connect.service.js'; // WebSocket 消息格式 @@ -21,6 +21,14 @@ export interface PostMessageble { postMessage(message: any): void; } +export interface IConnectionArgs { + connectionType: ConnectionType; + commandString?: string; + cwd?: string; + url?: string; + oauth?: string; +} + // 监听器回调类型 export type MessageHandler = (message: any) => void; @@ -72,7 +80,7 @@ export class VSCodeWebViewLike { export class TaskLoopAdapter { public emitter: EventEmitter; private messageHandlers: Set; - private connectionOptions: McpOptions[] = []; + private connectionOptions: IConnectionArgs[] = []; constructor(option?: any) { this.emitter = new EventEmitter(option); @@ -133,7 +141,7 @@ export class TaskLoopAdapter { * @description 连接到 mcp 服务端 * @param mcpOption */ - public addMcp(mcpOption: McpOptions) { + public addMcp(mcpOption: IConnectionArgs) { // 0.1.4 新版本开始,此处修改为懒加载连接 // 实际的连接移交给前端 mcpAdapter 中进行统一的调度 diff --git a/service/src/llm/llm.service.ts b/service/src/llm/llm.service.ts index d2366d9..4e9c43c 100644 --- a/service/src/llm/llm.service.ts +++ b/service/src/llm/llm.service.ts @@ -6,6 +6,7 @@ import { ocrDB } from "../hook/db.js"; import type { ToolCallContent } from "../mcp/client.dto.js"; import { ocrWorkerStorage } from "../mcp/ocr.service.js"; import { axiosFetch } from "../hook/axios-fetch.js"; +import Table from 'cli-table3'; export let currentStream: AsyncIterable | null = null; @@ -24,12 +25,21 @@ export async function streamingChatCompletion( proxyServer = '' } = data; + // 创建请求参数表格 + const requestTable = new Table({ + head: ['Parameter', 'Value'], + colWidths: [20, 40], + style: { + head: ['cyan'], + border: ['grey'] + } + }); + + const client = new OpenAI({ baseURL, apiKey, fetch: async (input: string | URL | Request, init?: RequestInit) => { - - console.log('openai fetch begin, proxyServer:', proxyServer); if (model.startsWith('gemini') && init) { // 该死的 google @@ -42,15 +52,25 @@ export async function streamingChatCompletion( return await axiosFetch(input, init, { proxyServer }); } }); - + const seriableTools = (tools.length === 0) ? undefined: tools; const seriableParallelToolCalls = (tools.length === 0)? undefined: model.startsWith('gemini') ? undefined : parallelToolCalls; await postProcessMessages(messages); - console.log('seriableTools', seriableTools); - console.log('seriableParallelToolCalls', seriableParallelToolCalls); + // 使用表格渲染请求参数 + requestTable.push( + ['Model', model], + ['Base URL', baseURL || 'Default'], + ['Temperature', temperature], + ['Tools Count', tools.length], + ['Parallel Tool Calls', parallelToolCalls], + ['Proxy Server', proxyServer || 'No Proxy'] + ); + + console.log('\nOpenAI Request Parameters:'); + console.log(requestTable.toString()); const stream = await client.chat.completions.create({ model, diff --git a/service/src/mcp/client.service.ts b/service/src/mcp/client.service.ts index 7e9c736..1cf175c 100644 --- a/service/src/mcp/client.service.ts +++ b/service/src/mcp/client.service.ts @@ -87,9 +87,9 @@ export class McpClient { // 建立连接 if (this.transport) { try { - console.log(`🔌 Connecting to MCP server via ${this.options.connectionType}...`); + // console.log(`🔌 Connecting to MCP server via ${this.options.connectionType}...`); await this.client.connect(this.transport); - console.log(`Connected to MCP server via ${this.options.connectionType}`); + // console.log(`✅ Connected to MCP server via ${this.options.connectionType}`); } catch (error) { if (error instanceof UnauthorizedError) { if (!(this.transport instanceof StreamableHTTPClientTransport) && !(this.transport instanceof SSEClientTransport)) { @@ -125,7 +125,6 @@ export class McpClient { // 断开连接 public async disconnect(): Promise { await this.client.close(); - console.log('Disconnected from MCP server'); } diff --git a/service/src/mcp/connect.service.ts b/service/src/mcp/connect.service.ts index 372b76c..908c441 100644 --- a/service/src/mcp/connect.service.ts +++ b/service/src/mcp/connect.service.ts @@ -8,6 +8,7 @@ import path from 'node:path'; import fs from 'node:fs'; import * as os from 'os'; import { PostMessageble } from '../hook/adapter.js'; +import Table from 'cli-table3'; export const clientMap: Map = new Map(); export function getClient(clientId?: string): RequestClientType | undefined { @@ -103,6 +104,10 @@ async function preprocessCommand(option: McpOptions, webview?: PostMessageble) { }); } + if (option.cwd && option.cwd.startsWith('~/')) { + option.cwd = option.cwd.replace('~', process.env.HOME || ''); + } + if (option.connectionType === 'SSE' || option.connectionType === 'STREAMABLE_HTTP') { return; } @@ -239,12 +244,32 @@ export async function connectService( option: McpOptions, webview?: PostMessageble ): Promise { - try { - const { env, ...others } = option; - console.log('ready to connect', others); + try { + // 使用cli-table3创建美观的表格 + const table = new Table({ + head: ['Property', 'Value'], + colWidths: [20, 60], + style: { + head: ['green'], + border: ['grey'] + } + }); + table.push( + ['Connection Type', option.connectionType], + ['Command', option.command || 'N/A'], + ['Arguments', option.args?.join(' ') || 'N/A'], + ['Working Directory', option.cwd || 'N/A'], + ['URL', option.url || 'N/A'] + ); + + console.log(table.toString()); + + + + // 预处理字符串 await preprocessCommand(option, webview); - + // 通过 option 字符串进行 hash,得到唯一的 uuid const uuid = await deterministicUUID(JSON.stringify(option));