diff --git a/CHANGELOG.md b/CHANGELOG.md index bb22fca..a4484d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Change Log +## [main] 0.1.8 +- 增加 STDIO 下的热更新,现在用户修改 mcp 代码,openmcp 会自动完成一切相关功能的热更新,无需用户手动重启。 +- 完成 mcpconfig.json 的导出功能,导出的 配置文件 可以通过 openmcp-sdk 框架完成低代码 agent 部署;也可以直接载入 Claude Desktop 等等 MCP 客户端中,实现 MCP 的快速部署和使用。 +- 修复若干 vscode 插件端 bug + +## [main] 0.1.7 +- 新的构建系统 +- 修复无法在 trae & cursor 中使用的 bug + ## [main] 0.1.6 - 针对 0.1.5 无法在 Windows 启动的紧急修复。 - 修复环境变量中添加 token 失效的问题。 diff --git a/package.json b/package.json index 440ecb6..7ed4c54 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "openmcp", "displayName": "OpenMCP", "description": "An all in one MCP Client/TestTool", - "version": "0.1.7", + "version": "0.1.8", "publisher": "kirigaya", "author": { "name": "kirigaya", diff --git a/renderer/public/iconfont.css b/renderer/public/iconfont.css index 6df823e..6b9f716 100644 --- a/renderer/public/iconfont.css +++ b/renderer/public/iconfont.css @@ -1,8 +1,8 @@ @font-face { font-family: "iconfont"; /* Project id 4870215 */ - src: url('iconfont.woff2?t=1750172161574') format('woff2'), - url('iconfont.woff?t=1750172161574') format('woff'), - url('iconfont.ttf?t=1750172161574') format('truetype'); + src: url('iconfont.woff2?t=1750532923458') format('woff2'), + url('iconfont.woff?t=1750532923458') format('woff'), + url('iconfont.ttf?t=1750532923458') format('truetype'); } .iconfont { @@ -13,6 +13,10 @@ -moz-osx-font-smoothing: grayscale; } +.icon-deploy:before { + content: "\e614"; +} + .icon-suffix-xml:before { content: "\e653"; } diff --git a/renderer/public/iconfont.woff2 b/renderer/public/iconfont.woff2 index be144e9..c6056df 100644 Binary files a/renderer/public/iconfont.woff2 and b/renderer/public/iconfont.woff2 differ diff --git a/renderer/src/components/main-panel/chat/chat-box/options/export.vue b/renderer/src/components/main-panel/chat/chat-box/options/export.vue new file mode 100644 index 0000000..a797b98 --- /dev/null +++ b/renderer/src/components/main-panel/chat/chat-box/options/export.vue @@ -0,0 +1,208 @@ + + + + + diff --git a/renderer/src/components/main-panel/chat/chat-box/options/setting.vue b/renderer/src/components/main-panel/chat/chat-box/options/setting.vue index cdd97f2..f7d03d6 100644 --- a/renderer/src/components/main-panel/chat/chat-box/options/setting.vue +++ b/renderer/src/components/main-panel/chat/chat-box/options/setting.vue @@ -9,6 +9,7 @@ + @@ -27,6 +28,7 @@ import ParallelToolCalls from './parallel-tool-calls.vue'; import Temperature from './temperature.vue'; import ContextLength from './context-length.vue'; import XmlWrapper from './xml-wrapper.vue'; +import Export from './export.vue'; const props = defineProps({ modelValue: { diff --git a/renderer/src/components/main-panel/chat/chat-box/prompt-chat-item.vue b/renderer/src/components/main-panel/chat/chat-box/prompt-chat-item.vue index 0453fc8..3e48ec1 100644 --- a/renderer/src/components/main-panel/chat/chat-box/prompt-chat-item.vue +++ b/renderer/src/components/main-panel/chat/chat-box/prompt-chat-item.vue @@ -22,9 +22,6 @@ const props = defineProps({ \ No newline at end of file diff --git a/renderer/src/components/main-panel/tool/tool-list.vue b/renderer/src/components/main-panel/tool/tool-list.vue index f8f4c05..9eed698 100644 --- a/renderer/src/components/main-panel/tool/tool-list.vue +++ b/renderer/src/components/main-panel/tool/tool-list.vue @@ -18,7 +18,7 @@
- +
@@ -148,10 +148,9 @@ onMounted(async () => { margin-left: 10px; background-color: var(--main-color); padding: 2px 5px; - border-radius: .5em; + border-radius: .3em; height: fit-content; - font-size: 10px; - + font-size: 13px; color: black; } diff --git a/renderer/src/i18n/ar.json b/renderer/src/i18n/ar.json index 73f5338..1506e7a 100644 --- a/renderer/src/i18n/ar.json +++ b/renderer/src/i18n/ar.json @@ -176,5 +176,11 @@ "tool-manage": "إدارة الأدوات", "enable-all-tools": "تفعيل جميع الأدوات", "disable-all-tools": "تعطيل جميع الأدوات", - "using-tool": "جاري استخدام الأداة" + "using-tool": "جاري استخدام الأداة", + "copy-success": "تم النسخ بنجاح", + "copy-fail": "فشل النسخ", + "copy": "نسخ", + "export": "تصدير", + "export-filename": "اسم ملف التصدير", + "how-to-use": "كيفية الاستخدام؟" } \ No newline at end of file diff --git a/renderer/src/i18n/de.json b/renderer/src/i18n/de.json index 4f45f0c..14dd286 100644 --- a/renderer/src/i18n/de.json +++ b/renderer/src/i18n/de.json @@ -176,5 +176,11 @@ "tool-manage": "Werkzeugverwaltung", "enable-all-tools": "Alle Tools aktivieren", "disable-all-tools": "Alle Tools deaktivieren", - "using-tool": "Werkzeug wird verwendet" + "using-tool": "Werkzeug wird verwendet", + "copy-success": "Erfolgreich kopiert", + "copy-fail": "Kopieren fehlgeschlagen", + "copy": "Kopieren", + "export": "Exportieren", + "export-filename": "Exportdateiname", + "how-to-use": "Wie benutzt man?" } \ No newline at end of file diff --git a/renderer/src/i18n/en.json b/renderer/src/i18n/en.json index 6bd6c9e..02c9246 100644 --- a/renderer/src/i18n/en.json +++ b/renderer/src/i18n/en.json @@ -176,5 +176,11 @@ "tool-manage": "Tool Management", "enable-all-tools": "Activate all tools", "disable-all-tools": "Disable all tools", - "using-tool": "Using tool" + "using-tool": "Using tool", + "copy-success": "Copied successfully", + "copy-fail": "Copy failed", + "copy": "Copy", + "export": "Export", + "export-filename": "Export file name", + "how-to-use": "How to use?" } \ No newline at end of file diff --git a/renderer/src/i18n/fr.json b/renderer/src/i18n/fr.json index bebf323..1dda9a9 100644 --- a/renderer/src/i18n/fr.json +++ b/renderer/src/i18n/fr.json @@ -176,5 +176,11 @@ "tool-manage": "Gestion des outils", "enable-all-tools": "Activer tous les outils", "disable-all-tools": "Désactiver tous les outils", - "using-tool": "Utilisation de l'outil" + "using-tool": "Utilisation de l'outil", + "copy-success": "Copié avec succès", + "copy-fail": "Échec de la copie", + "copy": "Copier", + "export": "Exporter", + "export-filename": "Nom du fichier d'exportation", + "how-to-use": "Comment utiliser ?" } \ No newline at end of file diff --git a/renderer/src/i18n/ja.json b/renderer/src/i18n/ja.json index df0ee58..e5e70fd 100644 --- a/renderer/src/i18n/ja.json +++ b/renderer/src/i18n/ja.json @@ -176,5 +176,11 @@ "tool-manage": "ツール管理", "enable-all-tools": "すべてのツールを有効にする", "disable-all-tools": "すべてのツールを無効にする", - "using-tool": "ツール使用中" + "using-tool": "ツール使用中", + "copy-success": "コピーしました", + "copy-fail": "コピーに失敗しました", + "copy": "コピー", + "export": "エクスポート", + "export-filename": "エクスポートファイル名", + "how-to-use": "使い方は?" } \ No newline at end of file diff --git a/renderer/src/i18n/ko.json b/renderer/src/i18n/ko.json index ae18984..d175a89 100644 --- a/renderer/src/i18n/ko.json +++ b/renderer/src/i18n/ko.json @@ -176,5 +176,11 @@ "tool-manage": "도구 관리", "enable-all-tools": "모든 도구 활성화", "disable-all-tools": "모든 도구 비활성화", - "using-tool": "도구 사용 중" + "using-tool": "도구 사용 중", + "copy-success": "성공적으로 복사되었습니다", + "copy-fail": "복사 실패", + "copy": "복사", + "export": "내보내기", + "export-filename": "내보내기 파일 이름", + "how-to-use": "사용 방법?" } \ No newline at end of file diff --git a/renderer/src/i18n/ru.json b/renderer/src/i18n/ru.json index 5f009ca..d088ece 100644 --- a/renderer/src/i18n/ru.json +++ b/renderer/src/i18n/ru.json @@ -176,5 +176,11 @@ "tool-manage": "Управление инструментами", "enable-all-tools": "Активировать все инструменты", "disable-all-tools": "Отключить все инструменты", - "using-tool": "Использование инструмента" + "using-tool": "Использование инструмента", + "copy-success": "Скопировано успешно", + "copy-fail": "Ошибка копирования", + "copy": "Копировать", + "export": "Экспорт", + "export-filename": "Имя экспортируемого файла", + "how-to-use": "Как использовать?" } \ No newline at end of file diff --git a/renderer/src/i18n/zh-cn.json b/renderer/src/i18n/zh-cn.json index 0096a40..7906cc5 100644 --- a/renderer/src/i18n/zh-cn.json +++ b/renderer/src/i18n/zh-cn.json @@ -176,5 +176,11 @@ "tool-manage": "工具管理", "enable-all-tools": "激活所有工具", "disable-all-tools": "禁用所有工具", - "using-tool": "正在使用工具" + "using-tool": "正在使用工具", + "copy-success": "复制成功", + "copy-fail": "复制失败", + "copy": "复制", + "export": "导出", + "export-filename": "导出文件名", + "how-to-use": "如何使用?" } \ No newline at end of file diff --git a/renderer/src/i18n/zh-tw.json b/renderer/src/i18n/zh-tw.json index e3e7c6b..fa951d6 100644 --- a/renderer/src/i18n/zh-tw.json +++ b/renderer/src/i18n/zh-tw.json @@ -176,5 +176,11 @@ "tool-manage": "工具管理", "enable-all-tools": "啟用所有工具", "disable-all-tools": "禁用所有工具", - "using-tool": "正在使用工具" + "using-tool": "正在使用工具", + "copy-success": "複製成功", + "copy-fail": "複製失敗", + "copy": "複製", + "export": "匯出", + "export-filename": "匯出檔案名稱", + "how-to-use": "如何使用?" } \ No newline at end of file diff --git a/renderer/src/views/about/index.vue b/renderer/src/views/about/index.vue index a64b720..2f49467 100644 --- a/renderer/src/views/about/index.vue +++ b/renderer/src/views/about/index.vue @@ -47,7 +47,7 @@ import { useI18n } from 'vue-i18n'; const { t } = useI18n(); -const version = '0.1.7'; +const version = '0.1.8'; const author = 'LSTM-Kirigaya (锦恢)'; defineComponent({ name: 'about' }); diff --git a/renderer/src/views/connect/core.ts b/renderer/src/views/connect/core.ts index 126d2be..e90be33 100644 --- a/renderer/src/views/connect/core.ts +++ b/renderer/src/views/connect/core.ts @@ -506,7 +506,11 @@ class McpClientAdapter { constructor( public platform: string - ) {} + ) { + if (platform !== 'nodejs') { + this.addConnectRefreshListener(); + } + } /** * @description 获取连接参数签名 @@ -564,18 +568,23 @@ class McpClientAdapter { * @description register HMR */ public addConnectRefreshListener() { - // 创建对于 connect/refresh 的监听 + // 创建对于 connect/refresh 的监听 if (!this.connectrefreshListener) { const bridge = useMessageBridge(); - this.connectrefreshListener = bridge.addCommandListener('connect/refresh', async (message) => { + this.connectrefreshListener = bridge.addCommandListener('connect/refresh', async (message) => { const { code, msg } = message; + console.log('refresh'); + + if (code === 200) { // 查找目标客户端 const clientIndex = this.findClientIndexByUuid(msg.uuid); if (clientIndex > -1) { // 刷新该客户端的所有资源 + console.log('clientIndex', clientIndex); + await this.clients[clientIndex].refreshAllResources(); this.refreshSignal.value++; } else { diff --git a/service/src/common/router.ts b/service/src/common/router.ts index c9d53fb..571b269 100644 --- a/service/src/common/router.ts +++ b/service/src/common/router.ts @@ -22,7 +22,6 @@ export async function routeMessage(command: string, data: any, webview: PostMess const { handler, option = {} } = handlerStore; try { - // TODO: select client based on something const res = await handler(data, webview); // res.code = -1 代表当前请求不需要返回发送 diff --git a/service/src/hook/adapter.ts b/service/src/hook/adapter.ts index 0e65837..47fc0ef 100644 --- a/service/src/hook/adapter.ts +++ b/service/src/hook/adapter.ts @@ -57,6 +57,7 @@ export class VSCodeWebViewLike { * @param message - 包含 command 和 args 的消息 */ postMessage(message: WebSocketMessage): void { + if (this.ws.readyState === WebSocket.OPEN) { this.ws.send(JSON.stringify(message)); } else { diff --git a/service/src/main.ts b/service/src/main.ts index cec6a29..10cbce2 100644 --- a/service/src/main.ts +++ b/service/src/main.ts @@ -5,8 +5,7 @@ import { dirname, join } from 'path'; import { routeMessage } from './common/router.js'; import { VSCodeWebViewLike } from './hook/adapter.js'; import fs from 'fs/promises'; // 使用 Promise API 替代回调 -import path from 'path'; -import axios from 'axios'; +import chalk from 'chalk'; export interface VSCodeMessage { command: string; @@ -101,7 +100,10 @@ wss.on('connection', (ws) => { acquireConnectionOption().then(option => { webview.onDidReceiveMessage(async (message) => { - logger.info(`收到命令: [${message.command || '未定义'}]`); + console.log( + chalk.white('receive command') + + chalk.blue(` [${message.command || '未定义'}]`) + ); const { command, data } = message; diff --git a/service/src/mcp/connect-monitor.service.ts b/service/src/mcp/connect-monitor.service.ts index e228d6a..b486354 100644 --- a/service/src/mcp/connect-monitor.service.ts +++ b/service/src/mcp/connect-monitor.service.ts @@ -4,6 +4,8 @@ import { PostMessageble } from '../hook/adapter.js'; import * as fs from 'fs'; import * as path from 'path'; import { pino } from 'pino'; +import chalk from 'chalk'; + // 保留现有 logger 配置 const logger = pino({ @@ -59,15 +61,11 @@ export class McpServerConnectMonitor { debounceTime: 500, duplicateCheckTime: 500, onChange: async (curr, prev) => { - // 使用 info 级别记录文件修改 - logger.info({ - uuid: this.uuid, - size: curr.size, - mtime: new Date(curr.mtime).toLocaleString() - }, 'File modified'); - try { await onchange(this.uuid, this.Options); + + console.log('send something'); + this.sendWebviewMessage('connect/refresh', { code: 200, msg: { @@ -75,7 +73,11 @@ export class McpServerConnectMonitor { uuid: this.uuid, } }); - logger.info({ uuid: this.uuid }, 'Connection refresh successful'); + + console.log( + chalk.green('Connection refresh successfully') + ); + } catch (err) { this.sendWebviewMessage('connect/refresh', { code: 500, @@ -122,4 +124,9 @@ export class McpServerConnectMonitor { // 发送消息到webview this.webview?.postMessage({ command, data }); } + + public close() { + this.Monitor?.close(); + } + } diff --git a/service/src/mcp/connect.service.ts b/service/src/mcp/connect.service.ts index efc26e0..1e7c6e8 100644 --- a/service/src/mcp/connect.service.ts +++ b/service/src/mcp/connect.service.ts @@ -9,7 +9,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'; +import chalk from 'chalk'; export const clientMap: Map = new Map(); export function getClient(clientId?: string): RequestClientType | undefined { @@ -21,7 +21,10 @@ export async function updateClientMap(uuid: string, options: McpOptions): Promis const client = await connect(options); clientMap.set(uuid, client); const tools = await client.listTools(); - console.log('[updateClientMap] tools:', tools); + console.log( + chalk.white('update client tools'), + chalk.blue(tools.tools.map(tool => tool.name).join(',')) + ); return { res: true }; } catch (error) { console.error('[updateClientMap] error:', error); @@ -267,25 +270,6 @@ export async function connectService( webview?: PostMessageble ): Promise { 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); @@ -301,6 +285,11 @@ export async function connectService( // } // const client = clientMap.get(uuid)!; + { + clientMap.get(uuid)?.disconnect(); + clientMonitorMap.get(uuid)?.close(); + } + const client = await connect(option); clientMap.set(uuid, client); clientMonitorMap.set(uuid, new McpServerConnectMonitor(uuid, option, updateClientMap, webview)); diff --git a/src/global.ts b/src/global.ts index c46e6b0..a7d0df8 100644 --- a/src/global.ts +++ b/src/global.ts @@ -377,3 +377,18 @@ export async function getFirstValidPathFromCommand(command: string, cwd: string) return undefined; } + + +export async function exportFile(filename: string, content: any) { + // 使用 vscode 的 api,创建文件导出窗口,询问用户 + const uri = await vscode.window.showSaveDialog({ + defaultUri: vscode.Uri.file(filename), + filters: { + 'JSON': ['json'] + } + }); + + if (uri) { + fs.writeFileSync(uri.fsPath, content, 'utf-8'); + } +} \ No newline at end of file diff --git a/src/webview/webview.service.ts b/src/webview/webview.service.ts index 8be19a3..c392bee 100644 --- a/src/webview/webview.service.ts +++ b/src/webview/webview.service.ts @@ -1,7 +1,7 @@ import * as vscode from 'vscode'; import * as fs from 'fs'; import * as fspath from 'path'; -import { ConnectionType, McpOptions, panels, updateInstalledConnectionConfig, updateWorkspaceConnectionConfig } from '../global.js'; +import { ConnectionType, exportFile, McpOptions, panels, updateInstalledConnectionConfig, updateWorkspaceConnectionConfig } from '../global.js'; import { routeMessage } from '../../openmcp-sdk/service/index.js'; export function getWebviewContent(context: vscode.ExtensionContext, panel: vscode.WebviewPanel): string | undefined { @@ -101,6 +101,10 @@ export function revealOpenMcpWebviewPanel( } break; + case 'vscode/export-file': + exportFile(data.filename, data.content); + break; + case 'vscode/clipboard/writeText': vscode.env.clipboard.writeText(data.text); break;