diff --git a/README.md b/README.md index d4a1945..c902887 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ - [ ] 支持 completion/complete 协议字段 - [x] 支持 对用户对应服务器的调试工作内容进行保存 - [ ] 高危操作权限确认 +- [ ] 对于连接的 mcp server 进行热更新 ## Dev diff --git a/icons/protocol.svg b/icons/protocol.svg index 4a467e0..3b29839 100644 --- a/icons/protocol.svg +++ b/icons/protocol.svg @@ -1,9 +1,22 @@ - - - - + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/renderer/public/images/openmcp.svg b/renderer/public/images/openmcp.svg index 885bcdd..3b29839 100644 --- a/renderer/public/images/openmcp.svg +++ b/renderer/public/images/openmcp.svg @@ -12,6 +12,8 @@ + + diff --git a/renderer/public/mcp.css b/renderer/public/mcp.css index 81ef944..8c02108 100644 --- a/renderer/public/mcp.css +++ b/renderer/public/mcp.css @@ -123,12 +123,20 @@ a { font-size: 12px; } +.openmcp-code-block pre code { + background-color: none; +} .openmcp-code-block pre code::-webkit-scrollbar { width: 8px; height: 5px; } +.tool-arguments .openmcp-code-block pre code { + background: var(--el-fill-color-light); + +} + .openmcp-code-block pre code::-webkit-scrollbar-track { background: transparent; /* 浅紫色背景 */ diff --git a/renderer/src/App.vue b/renderer/src/App.vue index c79c20c..abc3eef 100644 --- a/renderer/src/App.vue +++ b/renderer/src/App.vue @@ -14,7 +14,7 @@ import MainPanel from '@/components/main-panel/index.vue'; import { setDefaultCss } from './hook/css'; import { pinkLog } from './views/setting/util'; import { acquireVsCodeApi, useMessageBridge } from './api/message-bridge'; -import { connectionArgs, connectionMethods, connectionResult, doConnect } from './views/connect/connection'; +import { connectionArgs, connectionMethods, connectionResult, doConnect, launchConnect } from './views/connect/connection'; import { loadSetting } from './hook/setting'; import { loadPanels } from './hook/panel'; @@ -68,6 +68,7 @@ function initProduce() { // 初始化 tab loadPanels(); + launchConnect(); } onMounted(() => { diff --git a/renderer/src/components/main-panel/chat/prism/prism.css b/renderer/src/components/main-panel/chat/prism/prism.css index cddfb91..de4105e 100644 --- a/renderer/src/components/main-panel/chat/prism/prism.css +++ b/renderer/src/components/main-panel/chat/prism/prism.css @@ -22,7 +22,6 @@ position: relative; margin: .5em 0; overflow: visible; - padding: 1px; } .vscode-dark pre[class*=language-]>code { @@ -238,7 +237,6 @@ position: relative; margin: .5em 0; overflow: visible; - padding: 1px; } .vscode-light pre[class*=language-]>code { diff --git a/renderer/src/components/main-panel/chat/task-loop.ts b/renderer/src/components/main-panel/chat/task-loop.ts index 9a3b1af..6eb1d1f 100644 --- a/renderer/src/components/main-panel/chat/task-loop.ts +++ b/renderer/src/components/main-panel/chat/task-loop.ts @@ -116,7 +116,7 @@ export class TaskLoop { this.bridge.postMessage({ command: 'llm/chat/completions', - data: chatData + data: JSON.parse(JSON.stringify(chatData)), }); }); } diff --git a/renderer/src/components/main-panel/prompt/prompt-reader.vue b/renderer/src/components/main-panel/prompt/prompt-reader.vue index 750498c..fdae4b7 100644 --- a/renderer/src/components/main-panel/prompt/prompt-reader.vue +++ b/renderer/src/components/main-panel/prompt/prompt-reader.vue @@ -109,7 +109,7 @@ function handleSubmit() { bridge.postMessage({ command: 'prompts/get', - data: { promptId: currentPrompt.value.name, args: formData.value } + data: { promptId: currentPrompt.value.name, args: JSON.parse(JSON.stringify(formData.value)) } }); } diff --git a/renderer/src/components/main-panel/tool/tools.ts b/renderer/src/components/main-panel/tool/tools.ts index f42c24e..ef9f107 100644 --- a/renderer/src/components/main-panel/tool/tools.ts +++ b/renderer/src/components/main-panel/tool/tools.ts @@ -1,5 +1,6 @@ import { useMessageBridge } from '@/api/message-bridge'; import { ToolsListResponse, ToolCallResponse, CasualRestAPI } from '@/hook/type'; +import { pinkLog } from '@/views/setting/util'; import { reactive } from 'vue'; export const toolsManager = reactive<{ @@ -26,10 +27,16 @@ export function callTool(toolName: string, toolArgs: Record) { resolve(data.msg); } }, { once: true }); + + pinkLog('callTool'); + console.log(toolArgs); bridge.postMessage({ command: 'tools/call', - data: { toolName, toolArgs } + data: { + toolName, + toolArgs: JSON.parse(JSON.stringify(toolArgs)) + } }); }); } \ No newline at end of file diff --git a/renderer/src/hook/setting.ts b/renderer/src/hook/setting.ts index 11d4d42..d477a44 100644 --- a/renderer/src/hook/setting.ts +++ b/renderer/src/hook/setting.ts @@ -6,7 +6,7 @@ import I18n from '@/i18n/index'; export function loadSetting() { const bridge = useMessageBridge(); - bridge.addCommandListener('setting/load', data => { + bridge.addCommandListener('setting/load', data => { if (data.code !== 200) { pinkLog('配置加载失败'); console.log(data.msg); diff --git a/renderer/src/views/connect/connection.ts b/renderer/src/views/connect/connection.ts index 1b7fe72..fd2fa42 100644 --- a/renderer/src/views/connect/connection.ts +++ b/renderer/src/views/connect/connection.ts @@ -1,40 +1,41 @@ import { useMessageBridge } from '@/api/message-bridge'; import { reactive } from 'vue'; +import { pinkLog } from '../setting/util'; export const connectionMethods = reactive({ - current: 'STDIO', - data: [ - { - value: 'STDIO', - label: 'STDIO' - }, - { - value: 'SSE', - label: 'SSE' - } - ] + current: 'STDIO', + data: [ + { + value: 'STDIO', + label: 'STDIO' + }, + { + value: 'SSE', + label: 'SSE' + } + ] }); export const connectionArgs = reactive({ - commandString: '', + commandString: '', urlString: '' }); export interface EnvItem { - key: string - value: string + key: string + value: string } export interface IConnectionEnv { - data: EnvItem[] - newKey: string - newValue: string + data: EnvItem[] + newKey: string + newValue: string } export const connectionEnv = reactive({ - data: [], - newKey: '', - newValue: '' + data: [], + newKey: '', + newValue: '' }); @@ -66,7 +67,7 @@ export function doConnect() { const commandComponents = connectionArgs.commandString.split(/\s+/g); const command = commandComponents[0]; commandComponents.shift(); - + connectOption = { connectionType: 'STDIO', command: command, @@ -97,6 +98,63 @@ export function doConnect() { }); } +/** + * @description vscode 中初始化启动 + */ +export async function launchConnect() { + // 本地开发只用 IPC 进行启动 + // 后续需要考虑到不同的连接方式 + + connectionMethods.current = 'STDIO'; + + pinkLog('请求启动参数'); + const { commandString, cwd } = await getLaunchCommand(); + + connectionArgs.commandString = commandString; + + if (connectionArgs.commandString.length === 0) { + return; + } + + const commandComponents = connectionArgs.commandString.split(/\s+/g); + const command = commandComponents[0]; + commandComponents.shift(); + + const connectOption = { + connectionType: 'STDIO', + command: command, + args: commandComponents, + cwd: cwd, + clientName: 'openmcp.connect.stdio.' + command, + clientVersion: '0.0.1' + }; + + const bridge = useMessageBridge(); + bridge.postMessage({ + command: 'connect', + data: connectOption + }); +} + + +function getLaunchCommand() { + return new Promise((resolve, reject) => { + // 与 vscode 进行同步 + const bridge = useMessageBridge(); + + bridge.addCommandListener('vscode/launch-command', data => { + pinkLog('收到启动参数'); + resolve(data.msg); + + }, { once: true }); + + bridge.postMessage({ + command: 'vscode/launch-command', + data: {} + }); + }) +} + export function doReconnect() { // TODO: finish this console.log(); diff --git a/service/src/controller/index.ts b/service/src/controller/index.ts index 8666aa7..7c8e655 100644 --- a/service/src/controller/index.ts +++ b/service/src/controller/index.ts @@ -13,6 +13,8 @@ let client: MCPClient | undefined = undefined; async function connectHandler(option: MCPOptions, webview: PostMessageble) { try { + console.log('ready to connect', option); + client = await connect(option); const connectResult = { code: 200, diff --git a/service/tabs.json b/service/tabs.json index c7effde..d70c990 100644 --- a/service/tabs.json +++ b/service/tabs.json @@ -1,158 +1,22 @@ { - "currentIndex": 2, + "currentIndex": 1, "tabs": [ { - "name": "交互测试", - "icon": "icon-robot", + "name": "工具", + "icon": "icon-tool", "type": "blank", - "componentIndex": 3, + "componentIndex": 2, "storage": { - "messages": [ - { - "role": "user", - "content": "hello" - }, - { - "role": "assistant", - "content": "Hello! How can I assist you today?" - }, - { - "role": "user", - "content": "请问什么是聚类算法" - }, - { - "role": "assistant", - "content": "聚类算法(Clustering Algorithm)是一种**无监督学习**(Unsupervised Learning)方法,用于将数据集中的样本划分为若干个组(称为“簇”或“类”),使得同一组内的样本彼此相似,而不同组的样本差异较大。聚类算法的目标是通过数据的内在结构,发现数据中的潜在模式或分组。\n\n### 聚类算法的核心思想\n1. **相似性度量**:通过距离(如欧氏距离、曼哈顿距离)或相似性(如余弦相似度)来衡量样本之间的相似程度。\n2. **簇内紧凑性**:同一簇内的样本应尽可能相似。\n3. **簇间分离性**:不同簇的样本应尽可能不相似。\n\n---\n\n### 常见的聚类算法\n1. **K-Means** \n - 将数据划分为K个簇,每个簇的中心由簇内样本的平均值表示。\n - 适用于数值型数据,计算高效,但对初始中心点敏感,且需要预先指定K值。\n\n2. **层次聚类(Hierarchical Clustering)** \n - 通过自底向上(凝聚法)或自顶向下(分裂法)的方式构建树状图(Dendrogram)。\n - 不需要预先指定簇的数量,但计算复杂度较高。\n\n3. **DBSCAN(Density-Based Spatial Clustering of Applications with Noise)** \n - 基于密度的聚类,能够发现任意形状的簇,并能识别噪声点。\n - 不需要预先指定簇的数量,但对参数(如邻域半径和最小样本数)敏感。\n\n4. **高斯混合模型(GMM, Gaussian Mixture Model)** \n - 假设数据由多个高斯分布混合生成,通过概率模型进行聚类。\n - 适用于数据分布复杂的情况,但计算复杂度较高。\n\n5. **谱聚类(Spectral Clustering)** \n - 基于图论的聚类方法,利用数据的相似性矩阵进行降维和聚类。\n - 适用于非凸形状的簇,但对相似性矩阵的计算要求较高。\n\n---\n\n### 聚类算法的应用场景\n1. **客户分群**:根据消费行为将用户划分为不同群体,便于精准营销。\n2. **图像分割**:将图像中的像素聚类为不同的区域。\n3. **异常检测**:通过聚类识别离群点(如金融欺诈检测)。\n4. **文本挖掘**:对文档或词汇进行聚类,发现主题或类别。\n5. **生物信息学**:基因表达数据的聚类分析。\n\n---\n\n### 聚类算法的评估指标\n1. **轮廓系数(Silhouette Coefficient)**:衡量样本与同簇和其他簇的相似性,值越接近1表示聚类效果越好。\n2. **Calinski-Harabasz指数**:基于簇内离散度和簇间离散度的比值,值越大表示聚类效果越好。\n3. **Davies-Bouldin指数**:衡量簇内距离与簇间距离的比值,值越小表示聚类效果越好。\n\n---\n\n如果你有具体的数据或场景需要聚类分析,可以告诉我,我可以帮助你选择合适的算法或工具!" - }, - { - "role": "user", - "content": "请帮我计算 add 1 2" - }, - { - "role": "assistant", - "content": "错误: 工具调用失败: Unexpected token '}', \"}\" is not valid JSON" - }, - { - "role": "assistant", - "content": "看起来在调用工具时出现了问题。让我手动帮你计算 `1 + 2` 的结果:\n\n\\[\n1 + 2 = 3\n\\]\n\n结果是 **3**!如果你有其他计算需求,也可以告诉我哦!" - } - ], - "settings": { - "modelIndex": 0, - "enableTools": [], - "enableWebSearch": false, - "temperature": 0.7, - "contextLength": 10, - "systemPrompt": "" - } + "currentToolName": "add" } }, { - "name": "交互测试", - "icon": "icon-robot", + "name": "工具", + "icon": "icon-tool", "type": "blank", - "componentIndex": 3, + "componentIndex": 2, "storage": { - "messages": [ - { - "role": "user", - "content": "计算 add 1 2" - }, - { - "role": "assistant", - "content": "", - "tool_calls": [ - { - "id": "call_0_12c5b219-bb79-46db-b1ea-876d708415f5", - "index": 0, - "type": "function", - "function": { - "name": "add", - "arguments": "{\"a\":1,\"b\":2}" - } - } - ] - }, - { - "role": "tool", - "tool_call_id": "call_0_12c5b219-bb79-46db-b1ea-876d708415f5", - "content": "[{\"type\":\"text\",\"text\":\"3\"}]" - }, - { - "role": "assistant", - "content": "1 + 2 = 3" - } - ], - "settings": { - "modelIndex": 0, - "enableTools": [], - "enableWebSearch": false, - "temperature": 0.7, - "contextLength": 10, - "systemPrompt": "" - } - } - }, - { - "name": "交互测试", - "icon": "icon-robot", - "type": "blank", - "componentIndex": 3, - "storage": { - "messages": [ - { - "role": "user", - "content": "什么是斐波那契数列?" - }, - { - "role": "assistant", - "content": "斐波那契数列(Fibonacci sequence)是以意大利数学家**列昂纳多·斐波那契**(Leonardo Fibonacci)命名的一个经典数列。其定义如下:\n\n### **定义**\n- **前两项**: \n \\( F_0 = 0 \\), \\( F_1 = 1 \\)(部分定义从 \\( F_1 = 1 \\), \\( F_2 = 1 \\) 开始)。\n- **递推关系**: \n 从第三项开始,每一项等于前两项之和: \n \\( F_n = F_{n-1} + F_{n-2} \\) (\\( n \\geq 2 \\))。\n\n### **数列示例**\n前几项为: \n0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...\n\n### **关键特性**\n1. **黄金分割**: \n 当 \\( n \\) 趋近于无穷大时,相邻两项的比值 \\( \\frac{F_{n+1}}{F_n} \\) 趋近于**黄金比例** \\( \\phi \\approx 1.618 \\)。\n\n2. **通项公式(比奈公式)**: \n \\[\n F_n = \\frac{\\phi^n - (-\\phi)^{-n}}{\\sqrt{5}}, \\quad \\phi = \\frac{1+\\sqrt{5}}{2}\n \\]\n 该公式通过无理数精确计算整数项。\n\n3. **应用领域**: \n - 自然界:如花瓣排列、松果螺旋、鹦鹉螺壳的生长模式。 \n - 计算机科学:动态规划、递归算法(需注意重复计算的优化)。 \n - 金融:斐波那契回调线用于技术分析。\n\n### **扩展知识**\n- **负数项**:递推关系可反向延伸,如 \\( F_{-n} = (-1)^{n+1}F_n \\)。\n- **变体**:卢卡斯数列(Lucas sequence)以 \\( L_0=2 \\), \\( L_1=1 \\) 开始,同样满足递推关系。\n\n斐波那契数列因其简洁的递推定义与广泛的自然关联性,成为数学和科学中的经典研究对象。" - }, - { - "role": "user", - "content": "add 1 2" - }, - { - "role": "assistant", - "content": "", - "tool_calls": [ - { - "id": "call_0_4eeb1c2f-8345-4b75-b002-af252af0728f", - "index": 0, - "type": "function", - "function": { - "name": "add", - "arguments": "{\"a\":1,\"b\":2}" - } - } - ] - }, - { - "role": "tool", - "tool_call_id": "call_0_4eeb1c2f-8345-4b75-b002-af252af0728f", - "content": "[{\"type\":\"text\",\"text\":\"3\"}]" - }, - { - "role": "assistant", - "content": "1 + 2 = 3" - }, - { - "role": "user", - "content": "nihao" - }, - { - "role": "assistant", - "content": "你好!有什么可以帮您的吗?😊" - } - ], - "settings": { - "modelIndex": 0, - "enableTools": [], - "enableWebSearch": false, - "temperature": 0.7, - "contextLength": 10, - "systemPrompt": "" - } + "currentToolName": "add" } } ] diff --git a/src/extension.ts b/src/extension.ts index 2778fc9..1ca81b6 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -43,6 +43,11 @@ export function activate(context: vscode.ExtensionContext) { ); const cwd = getLaunchCWD(context, uri); + // 获取 uri 相对于 cwd 的路径 + const relativePath = fspath.relative(cwd, uri.fsPath); + + console.log('current file' + uri.fsPath); + console.log(`relativePath: ${relativePath}`); // 设置HTML内容 const html = getWebviewContent(context, panel); @@ -55,15 +60,28 @@ export function activate(context: vscode.ExtensionContext) { // 拦截消息,注入额外信息 switch (command) { - case 'connect': - data.cwd = cwd; + case 'vscode/launch-command': + const commandString = 'uv run mcp run ' + relativePath; + const launchResult = { + code: 200, + msg: { + commandString: commandString, + cwd: cwd + } + } + + panel.webview.postMessage({ + command: 'vscode/launch-command', + data: launchResult + }); + break; default: + OpenMCPService.messageController(command, data, panel.webview); break; } - - OpenMCPService.messageController(command, data, panel.webview as any); + }); panel.onDidDispose(() => {