diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d3d061..efc1dca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,30 @@ # Change Log +## [main] 0.1.5 +- 修复 gemini 获取模型列表时存在 models 前缀的问题 +- 增加 web api 功能 + +## [main] 0.1.4 +- 重新实现 openai 协议的底层网络实现,从而支持 Google Gemini 全系列模型。 +- 实现 index 适配器,从而支持 Grok3 全系列模型。 +- 解决 issue#23 插件创建连接时报错“Cannot read properties of undefined (reading 'name')” +- 在填写 apikey 和 baseurl 的情况下,现在可以一键刷新模型列表,避免用户手动输入模型列表。 + +## [main] 0.1.3 +- 解决 issue#21 点击按钮后的发送文本后不会清空当前的输入框。 +- 修复暂停按键在多轮对话后消失的问题。 +- 修复 issue#25 无法连接 streamable http 的问题。 + +## [main] 0.1.2 +- 新特性:用户发送的信息增加「重新发送」按钮。 +- 支持特性 issue#17 「关于左侧添加mcp服务器操作优化问题」:增加强制聚焦功能,用户创建mcp服务器连接的过程中不会让输入框失去焦点。 +- 更新 MCP & OpenAI 协议内容。 +- 解决 issue#21 vscode插件界面bug,在高度有限情况下无法通过滚动完全显示连接按钮。 +- 解决 issue#21 最后一个标签页关闭并恢复默认页面。 +- 解决 issue#22 工具模块UI异常,现在 openmcp 支持解析 pydantic 进行 typing 的 python mcp 了。 +- 优化对象输入框,现在对象输入框具有语法高亮和受限度的自动补全了。 +- 对于 trae 的所有默认主题进行额外支持。 + ## [main] 0.1.1 - 修复 SSH 连接 Ubuntu 的情况下的部分 bug - 修复 python 项目点击 openmcp 进行连接时,初始化参数错误的问题 diff --git a/icons/openmcp.png b/icons/openmcp.png index 43d8b1b..a0647be 100644 Binary files a/icons/openmcp.png and b/icons/openmcp.png differ diff --git a/package-lock.json b/package-lock.json index 5ea4d7e..e773f77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,23 +1,23 @@ { "name": "openmcp", - "version": "0.1.1", + "version": "0.1.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "openmcp", - "version": "0.1.1", + "version": "0.1.3", "workspaces": [ "service", "renderer", "software" ], "dependencies": { - "@modelcontextprotocol/sdk": "^1.10.2", + "@modelcontextprotocol/sdk": "^1.12.1", "@seald-io/nedb": "^4.1.1", - "axios": "^1.7.7", + "axios": "^1.9.0", "bson": "^6.8.0", - "openai": "^4.93.0", + "openai": "^5.0.1", "pako": "^2.1.0", "tesseract.js": "^6.0.1", "uuid": "^11.1.0", @@ -510,6 +510,133 @@ "node": ">=6.9.0" } }, + "node_modules/@codemirror/autocomplete": { + "version": "6.18.6", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz", + "integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz", + "integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-json": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz", + "integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/json": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.0.tgz", + "integrity": "sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.8.5", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz", + "integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.35.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.5.11", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.11.tgz", + "integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", + "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", + "license": "MIT", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/theme-one-dark": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz", + "integrity": "sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/highlight": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.37.1", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.37.1.tgz", + "integrity": "sha512-Qy4CAUwngy/VQkEz0XzMKVRcckQuqLYWKqVpDDDghBe5FSXSqfVrJn49nw3ePZHxRUz4nRmb05Lgi+9csWo4eg==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.5.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@ctrl/tinycolor": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", @@ -1436,6 +1563,41 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lezer/common": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", + "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==", + "license": "MIT" + }, + "node_modules/@lezer/highlight": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", + "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/json": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz", + "integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", + "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, "node_modules/@malept/cross-spawn-promise": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", @@ -1491,13 +1653,19 @@ "node": ">=10" } }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "license": "MIT" + }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.11.4", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.11.4.tgz", - "integrity": "sha512-OTbhe5slIjiOtLxXhKalkKGhIQrwvhgCDs/C2r8kcBTy5HR/g43aDQU0l7r8O0VGbJPTNJvDc7ZdQMdQDJXmbw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.12.1.tgz", + "integrity": "sha512-KG1CZhZfWg+u8pxeM/mByJDScJSrjjxLc8fwQqbsS8xCjBmQfMNEBTotYdNanKekepnfRI85GtgQlctLFpcYPw==", "license": "MIT", "dependencies": { - "ajv": "^8.17.1", + "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", @@ -1513,6 +1681,28 @@ "node": ">=18" } }, + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, "node_modules/@openmcp/electron": { "resolved": "software", "link": true @@ -3159,6 +3349,7 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -4059,6 +4250,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/codemirror": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", + "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -4392,6 +4598,19 @@ "node": ">= 10" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -5695,7 +5914,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, "license": "MIT" }, "node_modules/fast-redact": { @@ -5717,6 +5935,7 @@ "version": "3.0.6", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, "funding": [ { "type": "github", @@ -7200,6 +7419,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, "license": "MIT" }, "node_modules/json-stringify-safe": { @@ -8221,19 +8441,10 @@ } }, "node_modules/openai": { - "version": "4.100.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.100.0.tgz", - "integrity": "sha512-9soq/wukv3utxcuD7TWFqKdKp0INWdeyhUCvxwrne5KwnxaCp4eHL4GdT/tMFhYolxgNhxFzg5GFwM331Z5CZg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/openai/-/openai-5.0.1.tgz", + "integrity": "sha512-Do6vxhbDv7cXhji/4ct1lrpZYMAOmjYbhyA9LJTuG7OfpbWMpuS+EIXkRT7R+XxpRB1OZhU/op4FU3p3uxU6gw==", "license": "Apache-2.0", - "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7" - }, "bin": { "openai": "bin/cli" }, @@ -8250,21 +8461,6 @@ } } }, - "node_modules/openai/node_modules/@types/node": { - "version": "18.19.101", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.101.tgz", - "integrity": "sha512-Ykg7fcE3+cOQlLUv2Ds3zil6DVjriGQaSN/kEpl5HQ3DIGM6W0F2n9+GkWV4bRt7KjLymgzNdTnSKCbFUUJ7Kw==", - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/openai/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "license": "MIT" - }, "node_modules/opencollective-postinstall": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", @@ -8797,7 +8993,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -9030,6 +9225,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -9823,6 +10019,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-mod": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==", + "license": "MIT" + }, "node_modules/sumchecker": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", @@ -10411,7 +10613,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" @@ -10563,6 +10764,12 @@ "typescript": ">=5.0.0" } }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + }, "node_modules/wasm-feature-detect": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz", @@ -11027,6 +11234,10 @@ "name": "@openmcp/renderer", "version": "0.1.0", "dependencies": { + "@codemirror/autocomplete": "^6.18.6", + "@codemirror/lang-json": "^6.0.1", + "@codemirror/theme-one-dark": "^6.1.2", + "codemirror": "^6.0.1", "core-js": "^3.8.3", "element-plus": "^2.9.9", "katex": "^0.16.21", @@ -11107,6 +11318,51 @@ "node": ">=14.14" } }, + "renderer/node_modules/openai": { + "version": "4.104.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz", + "integrity": "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "renderer/node_modules/openai/node_modules/@types/node": { + "version": "18.19.110", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.110.tgz", + "integrity": "sha512-WW2o4gTmREtSnqKty9nhqF/vA0GKd0V/rbC0OyjSk9Bz6bzlsXKT+i7WDdS/a0z74rfT2PO4dArVCSnapNLA5Q==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "renderer/node_modules/openai/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, "renderer/node_modules/vite": { "version": "6.3.5", "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", @@ -11262,10 +11518,11 @@ "version": "0.0.1", "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "^1.10.2", + "@modelcontextprotocol/sdk": "^1.12.1", "@seald-io/nedb": "^4.1.1", "open": "^10.1.2", - "openai": "^4.96.0", + "axios": "^1.9.0", + "openai": "^5.0.1", "pako": "^2.1.0", "pino": "^9.6.0", "pino-pretty": "^13.0.0", @@ -11445,6 +11702,51 @@ "electron-builder": "^24.13.3", "typescript": "^5.6.3" } + }, + "software/node_modules/@types/node": { + "version": "18.19.110", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.110.tgz", + "integrity": "sha512-WW2o4gTmREtSnqKty9nhqF/vA0GKd0V/rbC0OyjSk9Bz6bzlsXKT+i7WDdS/a0z74rfT2PO4dArVCSnapNLA5Q==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "software/node_modules/openai": { + "version": "4.104.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz", + "integrity": "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "software/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" } } } diff --git a/package.json b/package.json index 5c2aa84..82cd970 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.1", + "version": "0.1.4", "publisher": "kirigaya", "author": { "name": "kirigaya", @@ -233,11 +233,11 @@ "build:task-loop": "webpack --config webpack/webpack.task-loop.js" }, "dependencies": { - "@modelcontextprotocol/sdk": "^1.10.2", + "@modelcontextprotocol/sdk": "^1.12.1", "@seald-io/nedb": "^4.1.1", - "axios": "^1.7.7", + "axios": "^1.9.0", "bson": "^6.8.0", - "openai": "^4.93.0", + "openai": "^5.0.1", "pako": "^2.1.0", "tesseract.js": "^6.0.1", "uuid": "^11.1.0", diff --git a/renderer/package.json b/renderer/package.json index 1719ad9..62d60af 100644 --- a/renderer/package.json +++ b/renderer/package.json @@ -16,6 +16,10 @@ "type-check": "vue-tsc --build" }, "dependencies": { + "@codemirror/autocomplete": "^6.18.6", + "@codemirror/lang-json": "^6.0.1", + "@codemirror/theme-one-dark": "^6.1.2", + "codemirror": "^6.0.1", "core-js": "^3.8.3", "element-plus": "^2.9.9", "katex": "^0.16.21", diff --git a/renderer/public/favicon.png b/renderer/public/favicon.png index 43d8b1b..a0647be 100644 Binary files a/renderer/public/favicon.png and b/renderer/public/favicon.png differ diff --git a/renderer/public/iconfont.css b/renderer/public/iconfont.css index 2f32010..1db10e8 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=1747820198035') format('woff2'), - url('iconfont.woff?t=1747820198035') format('woff'), - url('iconfont.ttf?t=1747820198035') format('truetype'); + src: url('iconfont.woff2?t=1748859145515') format('woff2'), + url('iconfont.woff?t=1748859145515') format('woff'), + url('iconfont.ttf?t=1748859145515') format('truetype'); } .iconfont { @@ -13,6 +13,10 @@ -moz-osx-font-smoothing: grayscale; } +.icon-proxy:before { + content: "\e723"; +} + .icon-parallel:before { content: "\e61d"; } diff --git a/renderer/public/iconfont.woff2 b/renderer/public/iconfont.woff2 index 6e2c510..bd733c2 100644 Binary files a/renderer/public/iconfont.woff2 and b/renderer/public/iconfont.woff2 differ diff --git a/renderer/public/mcp.css b/renderer/public/mcp.css index 8a116f9..7646049 100644 --- a/renderer/public/mcp.css +++ b/renderer/public/mcp.css @@ -221,4 +221,10 @@ a { .el-dropdown-menu__item:hover { background-color: var(--background) !important; +} + +/* codemirror */ +.ͼo, +.ͼo .cm-gutters { + background-color: transparent !important; } \ No newline at end of file diff --git a/renderer/src/components/k-input-object/index.vue b/renderer/src/components/k-input-object/index.vue index df10592..b557a71 100644 --- a/renderer/src/components/k-input-object/index.vue +++ b/renderer/src/components/k-input-object/index.vue @@ -1,147 +1,199 @@ - \ No newline at end of file diff --git a/renderer/src/views/connect/connection-log.vue b/renderer/src/views/connect/connection-log.vue index 6a5535d..7c5d4e3 100644 --- a/renderer/src/views/connect/connection-log.vue +++ b/renderer/src/views/connect/connection-log.vue @@ -53,7 +53,7 @@ function clearLogs() { \ No newline at end of file diff --git a/renderer/src/views/setting/general.vue b/renderer/src/views/setting/general.vue index 28c0d2a..6c9c692 100644 --- a/renderer/src/views/setting/general.vue +++ b/renderer/src/views/setting/general.vue @@ -27,6 +27,20 @@ @change="safeSaveSetting" /> + +
+ + + {{ t('proxy-server') }} + +
+ +
+
diff --git a/renderer/src/views/setting/llm.ts b/renderer/src/views/setting/llm.ts index 7807ed2..7798de4 100644 --- a/renderer/src/views/setting/llm.ts +++ b/renderer/src/views/setting/llm.ts @@ -6,12 +6,25 @@ import type { ToolCall } from '@/components/main-panel/chat/chat-box/chat'; import I18n from '@/i18n'; const { t } = I18n.global; -export const llms = reactive([]); +export const llms = reactive([]); export const llmManager = reactive({ currentModelIndex: 0, }); +export interface BasicLlmDescription { + id: string, + name: string, + baseUrl: string, + models: string[], + isOpenAICompatible: boolean, + description: string, + website: string, + userToken: string, + userModel: string, + [key: string]: any +} + export function createTest(call: ToolCall) { const tab = createTab('tool', 0); tab.componentIndex = 2; @@ -21,8 +34,8 @@ export function createTest(call: ToolCall) { const storage: ToolStorage = { activeNames: [0], - currentToolName: call.function.name, - formData: JSON.parse(call.function.arguments) + currentToolName: call.function!.name!, + formData: JSON.parse(call.function!.arguments!) }; tab.storage = storage; diff --git a/service/package.json b/service/package.json index 68620b2..7f1f46f 100644 --- a/service/package.json +++ b/service/package.json @@ -35,10 +35,11 @@ "webpack-node-externals": "^3.0.0" }, "dependencies": { - "@modelcontextprotocol/sdk": "^1.10.2", + "@modelcontextprotocol/sdk": "^1.12.1", "@seald-io/nedb": "^4.1.1", "open": "^10.1.2", - "openai": "^4.96.0", + "axios": "^1.9.0", + "openai": "^5.0.1", "pako": "^2.1.0", "pino": "^9.6.0", "pino-pretty": "^13.0.0", diff --git a/service/src/hook/axios-fetch.ts b/service/src/hook/axios-fetch.ts new file mode 100644 index 0000000..c53cac9 --- /dev/null +++ b/service/src/hook/axios-fetch.ts @@ -0,0 +1,211 @@ +import axios from "axios"; +import { HttpsProxyAgent } from 'https-proxy-agent'; + +interface FetchOptions { + method?: string; + headers?: Record; + body?: string | Buffer | FormData | URLSearchParams | object; + [key: string]: any; +} + +interface FetchResponse { + ok: boolean; + status: number; + statusText: string; + headers: Headers; + url: string; + redirected: boolean; + type: string; + body: any; + + json(): Promise; + text(): Promise; + arrayBuffer(): Promise; + getReader(): ReadableStreamDefaultReader; +} + +interface ReadableStreamDefaultReader { + read(): Promise<{ done: boolean, value?: any }>; + cancel(): Promise; + releaseLock(): void; + get closed(): boolean; +} + +/** + * 将 axios 配置转换为 fetch 风格的配置 + */ +function adaptRequestOptions(url: string, options: FetchOptions = {}): any { + const axiosConfig: any = { + url, + method: options.method || 'GET', + headers: options.headers, + responseType: 'stream' + }; + + // 处理 body/data 转换 + if (options.body) { + if (typeof options.body === 'string' || Buffer.isBuffer(options.body)) { + axiosConfig.data = options.body; + } else if (typeof options.body === 'object') { + // 如果是 FormData、URLSearchParams 等特殊类型需要特殊处理 + if (options.body instanceof FormData) { + axiosConfig.data = options.body; + axiosConfig.headers = { + ...axiosConfig.headers, + 'Content-Type': 'multipart/form-data' + }; + } else if (options.body instanceof URLSearchParams) { + axiosConfig.data = options.body.toString(); + axiosConfig.headers = { + ...axiosConfig.headers, + 'Content-Type': 'application/x-www-form-urlencoded' + }; + } else { + // 普通 JSON 对象 + axiosConfig.data = JSON.stringify(options.body); + axiosConfig.headers = { + ...axiosConfig.headers, + 'Content-Type': 'application/json' + }; + } + } + } + + return axiosConfig; +} + +/** + * 将 axios 响应转换为 fetch 风格的 Response 对象 + */ +function adaptResponse(axiosResponse: FetchOptions): FetchResponse { + // 创建 Headers 对象 + const headers = new Headers(); + Object.entries(axiosResponse.headers || {}).forEach(([key, value]) => { + headers.append(key, value); + }); + + // 创建符合 Fetch API 的 Response 对象 + const fetchResponse = { + ok: axiosResponse.status >= 200 && axiosResponse.status < 300, + status: axiosResponse.status, + statusText: axiosResponse.statusText, + headers: headers, + url: axiosResponse.config.url, + redirected: false, // axios 不直接提供此信息 + type: 'basic', // 简单类型 + body: null, + + // 标准方法 + json: async () => { + if (typeof axiosResponse.data === 'object') { + return axiosResponse.data; + } + throw new Error('Response is not JSON'); + }, + text: async () => { + if (typeof axiosResponse.data === 'string') { + return axiosResponse.data; + } + return JSON.stringify(axiosResponse.data); + }, + arrayBuffer: async () => { + throw new Error('arrayBuffer not implemented for streaming'); + }, + + // 流式支持 + getReader: () => { + if (!axiosResponse.data.on || typeof axiosResponse.data.on !== 'function') { + throw new Error('Not a stream response'); + } + + // 将 Node.js 流转换为 Web Streams 的 ReadableStream + const nodeStream = axiosResponse.data; + let isCancelled = false; + + return { + read: () => { + if (isCancelled) { + return Promise.resolve({ done: true }); + } + + return new Promise((resolve, reject) => { + const onData = (chunk: any) => { + cleanup(); + resolve({ done: false, value: chunk }); + }; + + const onEnd = () => { + cleanup(); + resolve({ done: true }); + }; + + const onError = (err: Error) => { + cleanup(); + reject(err); + }; + + const cleanup = () => { + nodeStream.off('data', onData); + nodeStream.off('end', onEnd); + nodeStream.off('error', onError); + }; + + nodeStream.once('data', onData); + nodeStream.once('end', onEnd); + nodeStream.once('error', onError); + }); + }, + + cancel: () => { + isCancelled = true; + nodeStream.destroy(); + return Promise.resolve(); + }, + + releaseLock: () => { + // TODO: 实现 releaseLock 方法 + }, + + get closed() { + return isCancelled; + } + }; + } + } as FetchResponse; + + // 设置 body 为可读流 + if (axiosResponse.data.on && typeof axiosResponse.data.on === 'function') { + fetchResponse.body = { + getReader: fetchResponse.getReader + }; + } + + return fetchResponse; +} + +/** + * @description 主函数 - 用 axios 实现 fetch + */ +export async function axiosFetch(input: any, init: any, requestOption: { proxyServer?: string } = {}): Promise { + const axiosConfig = adaptRequestOptions(input, init); + + const { + proxyServer = '' + } = requestOption; + + if (proxyServer) { + const proxyAgent = new HttpsProxyAgent(proxyServer); + axiosConfig.httpsAgent = proxyAgent; + axiosConfig.httpAgent = proxyAgent; + } + + try { + const response = await axios(axiosConfig) as FetchOptions; + return adaptResponse(response); + } catch (error: any) { + if (error.response) { + return adaptResponse(error.response); + } + throw error; + } +} \ No newline at end of file diff --git a/service/src/hook/llm.ts b/service/src/hook/llm.ts index f31d1c9..07be9d5 100644 --- a/service/src/hook/llm.ts +++ b/service/src/hook/llm.ts @@ -47,6 +47,30 @@ export const llms = [ userToken: '', userModel: 'doubao-1.5-pro-32k' }, + { + id: 'gemini', + name: 'Gemini', + baseUrl: 'https://generativelanguage.googleapis.com/v1beta/openai/', + models: ['gemini-2.0-flash', 'gemini-2.5-flash-preview-05-20', 'gemini-2.5-pro-preview-05-06'], + provider: 'google', + isOpenAICompatible: true, + description: 'Google Gemini', + website: 'https://ai.google.dev/gemini-api/docs/models?hl=zh-cn%2F%2Fgemini-2.5-pro-preview-05-06#gemini-2.5-pro-preview-05-06', + userToken: '', + userModel: 'gemini-2.0-flash' + }, + { + id: 'grok', + name: 'Grok', + baseUrl: 'https://api.x.ai/v1', + models: ['grok-3', 'grok-3-fast', 'grok-3-mini', 'grok-3-mini-fast'], + provider: 'xai', + isOpenAICompatible: true, + description: 'xAI Grok', + website: 'https://docs.x.ai/docs/models', + userToken: '', + userModel: 'grok-3-mini' + }, { id: 'mistral', name: 'Mistral', @@ -108,3 +132,5 @@ export const llms = [ userModel: 'moonshot-v1-8k' } ]; + + diff --git a/service/src/llm/llm.controller.ts b/service/src/llm/llm.controller.ts index 099a6e1..3958248 100644 --- a/service/src/llm/llm.controller.ts +++ b/service/src/llm/llm.controller.ts @@ -4,14 +4,14 @@ import { RequestData } from "../common/index.dto.js"; import { PostMessageble } from "../hook/adapter.js"; import { getClient } from "../mcp/connect.service.js"; import { abortMessageService, streamingChatCompletion } from "./llm.service.js"; - +import { OpenAI } from "openai"; export class LlmController { @Controller('llm/chat/completions') async chatCompletion(data: RequestData, webview: PostMessageble) { try { - await streamingChatCompletion(data, webview); + await streamingChatCompletion(data, webview); } catch (error) { console.log('error' + error); @@ -35,4 +35,20 @@ export class LlmController { return abortMessageService(data, webview); } + + @Controller('llm/models') + async getModels(data: RequestData, webview: PostMessageble) { + const { + baseURL, + apiKey, + } = data; + + const client = new OpenAI({ apiKey, baseURL }); + const models = await client.models.list(); + + return { + code: 200, + msg: models.data + } + } } \ No newline at end of file diff --git a/service/src/llm/llm.dto.ts b/service/src/llm/llm.dto.ts index 2514b92..9328eae 100644 --- a/service/src/llm/llm.dto.ts +++ b/service/src/llm/llm.dto.ts @@ -6,4 +6,21 @@ export type MyMessageType = OpenAI.Chat.ChatCompletionMessageParam & { export type MyToolMessageType = OpenAI.Chat.ChatCompletionToolMessageParam & { extraInfo?: any; +} + +export interface OpenMcpChatOption { + baseURL: string; + apiKey: string; + model: string; + messages: any[]; + temperature?: number; + tools?: any[]; + parallelToolCalls?: boolean; +} + +export interface MyStream extends AsyncIterable { + [Symbol.asyncIterator](): AsyncIterator; + controller: { + abort(): void; + }; } \ No newline at end of file diff --git a/service/src/llm/llm.service.ts b/service/src/llm/llm.service.ts index 6672197..d2366d9 100644 --- a/service/src/llm/llm.service.ts +++ b/service/src/llm/llm.service.ts @@ -5,6 +5,7 @@ import { RestfulResponse } from "../common/index.dto.js"; 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"; export let currentStream: AsyncIterable | null = null; @@ -12,33 +13,51 @@ export async function streamingChatCompletion( data: any, webview: PostMessageble ) { - let { - baseURL, - apiKey, - model, - messages, - temperature, - tools = [], - parallelToolCalls = true - } = data; + const { + baseURL, + apiKey, + model, + messages, + temperature, + tools = [], + parallelToolCalls = true, + proxyServer = '' + } = data; const client = new OpenAI({ baseURL, - apiKey + apiKey, + fetch: async (input: string | URL | Request, init?: RequestInit) => { + + console.log('openai fetch begin, proxyServer:', proxyServer); + + if (model.startsWith('gemini') && init) { + // 该死的 google + init.headers = { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${apiKey}` + } + } + + return await axiosFetch(input, init, { proxyServer }); + } }); - if (tools.length === 0) { - tools = undefined; - } - + 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); const stream = await client.chat.completions.create({ model, messages, temperature, - tools, - parallel_tool_calls: parallelToolCalls, + tools: seriableTools, + parallel_tool_calls: seriableParallelToolCalls, stream: true }); diff --git a/service/src/main.ts b/service/src/main.ts index 46cfea8..5063957 100644 --- a/service/src/main.ts +++ b/service/src/main.ts @@ -7,6 +7,7 @@ import { VSCodeWebViewLike } from './hook/adapter.js'; import path from 'node:path'; import * as fs from 'node:fs'; import { setRunningCWD } from './hook/setting.js'; +import axios from 'axios'; export interface VSCodeMessage { command: string; diff --git a/service/src/mcp/client.service.ts b/service/src/mcp/client.service.ts index d7f3d4e..7e9c736 100644 --- a/service/src/mcp/client.service.ts +++ b/service/src/mcp/client.service.ts @@ -166,11 +166,7 @@ export class McpClient { // 调用工具 public async callTool(options: { name: string; arguments: Record, callToolOption?: any }) { const { callToolOption, ...methodArgs } = options; - console.log('methodArgs', methodArgs); - console.log('callToolOption', callToolOption); const res = await this.client.callTool(methodArgs, undefined, callToolOption); - console.log('callTool res', res); - return res; } } diff --git a/service/src/mcp/connect.controller.ts b/service/src/mcp/connect.controller.ts index 9b8acc5..35af81e 100644 --- a/service/src/mcp/connect.controller.ts +++ b/service/src/mcp/connect.controller.ts @@ -15,9 +15,6 @@ export class ConnectController { async lookupEnvVar(data: RequestData, webview: PostMessageble) { const { keys } = data; const values = keys.map((key: string) => { - // TODO: 在 Windows 上测试 - console.log(key); - console.log(process.env); if (process.platform === 'win32') { switch (key) { diff --git a/service/src/mcp/connect.service.ts b/service/src/mcp/connect.service.ts index 13c3e2b..372b76c 100644 --- a/service/src/mcp/connect.service.ts +++ b/service/src/mcp/connect.service.ts @@ -299,4 +299,4 @@ export async function connectService( return connectResult; } -} +} \ No newline at end of file diff --git a/service/src/panel/panel.controller.ts b/service/src/panel/panel.controller.ts index a4909d4..5fe918e 100644 --- a/service/src/panel/panel.controller.ts +++ b/service/src/panel/panel.controller.ts @@ -79,7 +79,6 @@ export class PanelController { @Controller('system-prompts/load') async loadSystemPrompts(data: RequestData, webview: PostMessageble) { - const client = getClient(data.clientId); const queryPrompts = await systemPromptDB.findAll(); const prompts = []; for (const prompt of queryPrompts) { diff --git a/service/src/setting/setting.service.ts b/service/src/setting/setting.service.ts index 7c8856c..bf04fc9 100644 --- a/service/src/setting/setting.service.ts +++ b/service/src/setting/setting.service.ts @@ -52,7 +52,11 @@ export function loadSetting(): IConfig { try { const configData = fs.readFileSync(configPath, 'utf-8'); - return JSON.parse(configData) as IConfig; + const config = JSON.parse(configData) as IConfig; + if (!config.LLM_INFO || (Array.isArray(config.LLM_INFO) && config.LLM_INFO.length === 0)) { + config.LLM_INFO = llms; + } + return config; } catch (error) { console.error('Error loading config file, creating new one:', error); return createConfig(); diff --git a/src/global.ts b/src/global.ts index 7dffb7c..bfa34d2 100644 --- a/src/global.ts +++ b/src/global.ts @@ -68,6 +68,19 @@ export function getConnectionConfig() { let connection; try { connection = JSON.parse(rawConnectionString) as IConnectionConfig; + + // 对连接信息进行校验 + if (!connection.items) { + connection = { items: [] }; + } + + connection.items = connection.items.filter(item => { + if (Array.isArray(item) && item.length === 0) { + return false; + } + return true; + }); + } catch (error) { connection = { items: [] }; } @@ -110,6 +123,19 @@ export function getWorkspaceConnectionConfig() { let connection; try { connection = JSON.parse(rawConnectionString) as IConnectionConfig; + + // 对连接信息进行校验 + if (!connection.items) { + connection = { items: [] }; + } + + connection.items = connection.items.filter(item => { + if (Array.isArray(item) && item.length === 0) { + return false; + } + return true; + }); + } catch (error) { connection = { items: [] }; } @@ -127,7 +153,7 @@ export function getWorkspaceConnectionConfig() { } if (connectionType === 'STDIO' && connection.cwd && connection.cwd.startsWith('{workspace}')) { connection.cwd = connection.cwd.replace('{workspace}', workspacePath).replace(/\\/g, '/'); - } + } } } @@ -169,12 +195,12 @@ export function saveWorkspaceConnectionConfig(workspace: string) { const workspacePath = getWorkspacePath(); for (let item of connectionConfig.items) { - const connections = Array.isArray(item)? item : [item]; + const connections = Array.isArray(item) ? item : [item]; for (let connection of connections) { const connectionType = (connection.type || connection.connectionType).toUpperCase() as ConnectionType; connection.type = undefined; connection.connectionType = connectionType; - + if (connection.filePath && connection.filePath.replace(/\\/g, '/').startsWith(workspacePath)) { connection.filePath = connection.filePath.replace(workspacePath, '{workspace}').replace(/\\/g, '/'); } @@ -283,7 +309,7 @@ export function getWorkspaceConnectionConfigItemByPath(absPath: string) { const normaliseAbsPath = absPath.replace(/\\/g, '/'); for (let item of workspaceConnectionConfig.items) { - const nItem = Array.isArray(item)? item[0] : item; + const nItem = Array.isArray(item) ? item[0] : item; const filePath = normaliseConnectionFilePath(nItem, workspacePath); if (filePath === normaliseAbsPath) { @@ -303,7 +329,7 @@ export function getInstalledConnectionConfigItemByPath(absPath: string) { const normaliseAbsPath = absPath.replace(/\\/g, '/'); for (let item of installedConnectionConfig.items) { - const nItem = Array.isArray(item)? item[0] : item; + const nItem = Array.isArray(item) ? item[0] : item; const filePath = (nItem.filePath || '').replace(/\\/g, '/'); if (filePath === normaliseAbsPath) { diff --git a/src/sidebar/installed.service.ts b/src/sidebar/installed.service.ts index 7b77376..54e77f0 100644 --- a/src/sidebar/installed.service.ts +++ b/src/sidebar/installed.service.ts @@ -58,7 +58,8 @@ export async function acquireInstalledConnection(): Promise { // 让用户选择连接类型 const connectionType = await vscode.window.showQuickPick(['STDIO', 'SSE', 'STREAMABLE_HTTP'], { placeHolder: '请选择连接类型', - canPickMany: false + canPickMany: false, + ignoreFocusOut: true, }); if (!connectionType) { diff --git a/src/sidebar/workspace.controller.ts b/src/sidebar/workspace.controller.ts index 84e118d..e6f119a 100644 --- a/src/sidebar/workspace.controller.ts +++ b/src/sidebar/workspace.controller.ts @@ -63,7 +63,7 @@ export class McpWorkspaceConnectProvider implements vscode.TreeDataProvider { // 让用户选择连接类型 - const connectionType = await vscode.window.showQuickPick(['STDIO', 'SSE'], { - placeHolder: '请选择连接类型' + const connectionType = await vscode.window.showQuickPick(['STDIO', 'SSE', 'STREAMABLE_HTTP'], { + placeHolder: '请选择连接类型', + canPickMany: false, + ignoreFocusOut: true, }); if (!connectionType) {