修复 vscode/trae 下 tab 恢复的问题

This commit is contained in:
锦恢 2025-04-13 16:12:58 +08:00
parent e306388f14
commit a0968902f4
19 changed files with 260 additions and 220 deletions

View File

@ -1,5 +1,12 @@
# Change Log # Change Log
## [main] 0.0.2
- 优化页面布局
- 解决更新标签页后打开无法显示的 bug
- 解决不如输入组件按下回车直接黑屏的 bug
- 更加完整方便的开发脚本
## [main] 0.0.1 ## [main] 0.0.1
- 完成 openmcp 的基础 inspector 功能 - 完成 openmcp 的基础 inspector 功能

View File

@ -13,13 +13,23 @@
## OpenMCP ## OpenMCP
一款用于 MCP 服务端调试的一体化 vscode 插件。 一款用于 MCP 服务端调试的一体化 vscode/trae 插件。
![](./icons/sreenshot.png) 集成 Inspector + MCP 客户端基础功能,开发测试一体化。
- 包含原版 Inpsector 的所有功能 ![](./icons/openmcp.welcome.png)
- 包含一个简易的用于进行测试的大模型对话 & 执行窗口
- 支持多种大模型 进行资源协议、工具、Prompt 的 MCP 服务器测试。
![](./icons/openmcp.resource.png)
测试完成的工具可以放入 「交互测试」 模块之间进行大模型交互测试。
![](./icons/openmcp.chatbot.png)
支持多种大模型
![](./icons/openmcp.support.llm.png)
## TODO ## TODO
@ -50,23 +60,22 @@ B <--mcp--> m(MCP Server)
配置项目 配置项目
```bash ```bash
source configure.sh ## linux
./configure.sh
## windows
./configure.ps1
``` ```
启动 dev server 启动 dev server
```bash ```bash
cd renderer ## linux
npm run serve ./dev.sh
``` ## windows
./dev.ps1
启动 service
```bash
cd service
npm run serve
``` ```
> 端口占用: 8080 (renderer) + 8081 (service)
### Extention Dev ### Extention Dev

View File

@ -1,23 +1,23 @@
# Create resources directory if it doesn't exist # 创建并清理资源目录
New-Item -ItemType Directory -Force -Path .\resources | Out-Null New-Item -ItemType Directory -Path ./resources -Force
Remove-Item -Recurse -Force ./resources/*
New-Item -ItemType Directory -Path ./resources -Force
# Start both build tasks in parallel # 并行构建 renderer 和 service
$jobs = @( $rendererJob = Start-Job -ScriptBlock {
Start-Job -ScriptBlock { cd ./renderer
Set-Location $using:PWD\renderer
npm run build npm run build
Move-Item -Force -Path .\dist -Destination ..\resources\renderer mv ./dist ../resources/renderer
} }
Start-Job -ScriptBlock {
Set-Location $using:PWD\service $serviceJob = Start-Job -ScriptBlock {
cd ./service
npm run build npm run build
Move-Item -Force -Path .\dist -Destination ..\resources\service mv ./dist ../resources/service
} }
)
# Wait for all jobs to complete # 等待任务完成
Wait-Job -Job $jobs | Out-Null $rendererJob | Wait-Job | Receive-Job
Receive-Job -Job $jobs $serviceJob | Wait-Job | Receive-Job
Remove-Job -Job $jobs
Write-Host "finish building services in ./resources" Write-Output "finish building services in ./resources"

View File

@ -1,6 +1,9 @@
#!/bin/bash #!/bin/bash
mkdir -p ./resources mkdir -p ./resources
rm -rf ./resources/
mkdir -p ./resources
(cd ./renderer && npm run build && mv ./dist ../resources/renderer) & (cd ./renderer && npm run build && mv ./dist ../resources/renderer) &
(cd ./service && npm run build && mv ./dist ../resources/service) & (cd ./service && npm run build && mv ./dist ../resources/service) &
wait wait

13
configure.ps1 Normal file
View File

@ -0,0 +1,13 @@
# 安装 renderer 依赖
Set-Location renderer
npm i
Set-Location ..
# 安装 service 依赖并打补丁
Set-Location service
npm i
node patch-mcp-sdk.js
Set-Location ..
# 安装根目录依赖
npm i

16
dev.ps1 Normal file
View File

@ -0,0 +1,16 @@
# 定义颜色变量
$PINK = "$([char]27)[33m"
$GREEN = "$([char]27)[32m"
$NC = "$([char]27)[0m"
Start-Job -ScriptBlock {
cd renderer
npm run serve | ForEach-Object { "$using:PINK[renderer]$using:NC $_" }
}
Start-Job -ScriptBlock {
cd service
npm run serve | ForEach-Object { "$using:GREEN[service]$using:NC $_" }
}
Get-Job | Wait-Job | Receive-Job -Wait

12
dev.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/bash
# 定义颜色变量(使用 -e 选项的 echo 时)
PINK=$'\033[33m'
GREEN=$'\033[32m'
NC=$'\033[0m'
(cd renderer && npm run serve | while read -r line; do echo -e "${PINK}[renderer]${NC} $line"; done) &
(cd service && npm run serve | while read -r line; do echo -e "${GREEN}[service]${NC} $line"; done) &
wait

BIN
icons/openmcp.chatbot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 KiB

BIN
icons/openmcp.resource.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 KiB

BIN
icons/openmcp.welcome.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 KiB

View File

@ -7,6 +7,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted } from 'vue'; import { onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { Connection } from './components/sidebar/sidebar'; import { Connection } from './components/sidebar/sidebar';
import Sidebar from '@/components/sidebar/index.vue'; import Sidebar from '@/components/sidebar/index.vue';
@ -26,40 +27,28 @@ bridge.addCommandListener('hello', data => {
greenLog(`version: ${data.version}`); greenLog(`version: ${data.version}`);
}, { once: true }); }, { once: true });
// connect
bridge.addCommandListener('connect', async data => {
const { code, msg } = data;
connectionResult.success = (code === 200);
connectionResult.logString = msg;
const res = await getServerVersion() as { name: string, version: string };
connectionResult.serverInfo.name = res.name || '';
connectionResult.serverInfo.version = res.version || '';
}, { once: true });
function initDebug() { function initDebug() {
connectionArgs.commandString = 'mcp run ../servers/main.py'; connectionArgs.commandString = 'mcp run ../servers/main.py';
connectionMethods.current = 'STDIO'; connectionMethods.current = 'STDIO';
setTimeout(() => { setTimeout(async () => {
// //
loadSetting(); loadSetting();
//
await doConnect();
// tab // tab
loadPanels(); loadPanels();
//
doConnect();
// 200 ws ws
//
}, 200); }, 200);
} }
function initProduce() { const route = useRoute();
const router = useRouter();
async function initProduce() {
// TODO: get from vscode // TODO: get from vscode
connectionArgs.commandString = 'mcp run ../servers/main.py'; connectionArgs.commandString = 'mcp run ../servers/main.py';
connectionMethods.current = 'STDIO'; connectionMethods.current = 'STDIO';
@ -67,10 +56,15 @@ function initProduce() {
// //
loadSetting(); loadSetting();
// tab //
loadPanels(); await launchConnect();
launchConnect(); // tab
await loadPanels();
if (route.name !== 'debug') {
router.replace('/debug');
}
} }
onMounted(() => { onMounted(() => {

View File

@ -40,6 +40,7 @@
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { addNewTab, tabs, closeTab } from './panel'; import { addNewTab, tabs, closeTab } from './panel';
import { panelLoaded } from '@/hook/panel';
defineComponent({ name: 'main-panel' }); defineComponent({ name: 'main-panel' });

View File

@ -19,6 +19,8 @@ interface SaveTab {
export const panelLoaded = ref(false); export const panelLoaded = ref(false);
export function loadPanels() { export function loadPanels() {
return new Promise((resolve, reject) => {
const bridge = useMessageBridge(); const bridge = useMessageBridge();
bridge.addCommandListener('panel/load', data => { bridge.addCommandListener('panel/load', data => {
@ -51,17 +53,16 @@ export function loadPanels() {
} }
tabs.activeIndex = persistTab.currentIndex; tabs.activeIndex = persistTab.currentIndex;
nextTick(() => {
tabs.activeIndex = persistTab.currentIndex;
});
} }
panelLoaded.value = true; panelLoaded.value = true;
resolve(void 0);
}, { once: true }); }, { once: true });
bridge.postMessage({ bridge.postMessage({
command: 'panel/load' command: 'panel/load'
}); });
});
} }
let debounceHandler: NodeJS.Timeout; let debounceHandler: NodeJS.Timeout;
@ -74,10 +75,10 @@ export function safeSavePanels() {
} }
export function savePanels(saveHandler?: () => void) { export function savePanels(saveHandler?: () => void) {
// 没有完成 panel 加载就不保存 // // 没有完成 panel 加载就不保存
if (!panelLoaded.value) { // if (!panelLoaded.value) {
return; // return;
} // }
const bridge = useMessageBridge(); const bridge = useMessageBridge();

View File

@ -57,6 +57,20 @@ export interface MCPOptions {
export function doConnect() { export function doConnect() {
let connectOption: MCPOptions; let connectOption: MCPOptions;
const bridge = useMessageBridge();
return new Promise((resolve, reject) => {
// 监听 connect
bridge.addCommandListener('connect', async data => {
const { code, msg } = data;
connectionResult.success = (code === 200);
connectionResult.logString = msg;
const res = await getServerVersion() as { name: string, version: string };
connectionResult.serverInfo.name = res.name || '';
connectionResult.serverInfo.version = res.version || '';
resolve(void 0);
}, { once: true });
if (connectionMethods.current === 'STDIO') { if (connectionMethods.current === 'STDIO') {
@ -91,11 +105,11 @@ export function doConnect() {
} }
} }
const bridge = useMessageBridge();
bridge.postMessage({ bridge.postMessage({
command: 'connect', command: 'connect',
data: connectOption data: connectOption
}); });
});
} }
/** /**
@ -106,16 +120,30 @@ export async function launchConnect() {
// 后续需要考虑到不同的连接方式 // 后续需要考虑到不同的连接方式
connectionMethods.current = 'STDIO'; connectionMethods.current = 'STDIO';
const bridge = useMessageBridge();
pinkLog('请求启动参数'); pinkLog('请求启动参数');
const { commandString, cwd } = await getLaunchCommand(); const { commandString, cwd } = await getLaunchCommand();
connectionArgs.commandString = commandString; connectionArgs.commandString = commandString;
if (connectionArgs.commandString.length === 0) { if (connectionArgs.commandString.length === 0) {
return; return;
} }
return new Promise<void>((resolve, reject) => {
// 监听 connect
bridge.addCommandListener('connect', async data => {
const { code, msg } = data;
connectionResult.success = (code === 200);
connectionResult.logString = msg;
const res = await getServerVersion() as { name: string, version: string };
connectionResult.serverInfo.name = res.name || '';
connectionResult.serverInfo.version = res.version || '';
resolve(void 0);
}, { once: true });
const commandComponents = connectionArgs.commandString.split(/\s+/g); const commandComponents = connectionArgs.commandString.split(/\s+/g);
const command = commandComponents[0]; const command = commandComponents[0];
commandComponents.shift(); commandComponents.shift();
@ -129,11 +157,11 @@ export async function launchConnect() {
clientVersion: '0.0.1' clientVersion: '0.0.1'
}; };
const bridge = useMessageBridge();
bridge.postMessage({ bridge.postMessage({
command: 'connect', command: 'connect',
data: connectOption data: connectOption
}); });
});
} }

View File

@ -3,7 +3,7 @@
<Welcome v-show="!haveActiveTab"></Welcome> <Welcome v-show="!haveActiveTab"></Welcome>
<!-- 如果存在激活标签页则根据标签页进行渲染 --> <!-- 如果存在激活标签页则根据标签页进行渲染 -->
<div v-show="haveActiveTab" style="height: 100%;"> <div v-show="haveActiveTab" v-if="panelLoaded" style="height: 100%;">
<!-- vscode/trae 下面存在初始化问题 --> <!-- vscode/trae 下面存在初始化问题 -->
<component <component
v-show="tab === tabs.content[tabs.activeIndex]" v-show="tab === tabs.content[tabs.activeIndex]"

View File

@ -36,5 +36,8 @@ module.exports = defineConfig({
}, },
css: { css: {
extract: false extract: false
},
devServer: {
port: 8081
} }
}); });

View File

@ -1,5 +1,5 @@
{ {
"currentIndex": 0, "currentIndex": 1,
"tabs": [ "tabs": [
{ {
"name": "资源", "name": "资源",
@ -7,54 +7,7 @@
"type": "blank", "type": "blank",
"componentIndex": 0, "componentIndex": 0,
"storage": { "storage": {
"currentResourceName": "greeting", "currentResourceName": "greeting"
"lastResourceReadResponse": {
"contents": [
{
"uri": "greeting://12312",
"mimeType": "text/plain",
"text": "Hello, 12312!"
}
]
}
}
},
{
"name": "交互测试",
"icon": "icon-robot",
"type": "blank",
"componentIndex": 3,
"storage": {
"messages": [],
"settings": {
"modelIndex": 0,
"enableTools": [
{
"name": "add",
"description": "对两个数字进行实数域的加法",
"enabled": true
},
{
"name": "multiply",
"description": "对两个数字进行实数域的乘法运算",
"enabled": true
},
{
"name": "is_even",
"description": "判断一个整数是否为偶数",
"enabled": true
},
{
"name": "capitalize",
"description": "将字符串首字母大写",
"enabled": true
}
],
"enableWebSearch": false,
"temperature": 0.7,
"contextLength": 10,
"systemPrompt": ""
}
} }
}, },
{ {