修复 vscode/trae 下 tab 恢复的问题
This commit is contained in:
parent
e306388f14
commit
a0968902f4
@ -1,5 +1,12 @@
|
||||
# Change Log
|
||||
|
||||
## [main] 0.0.2
|
||||
|
||||
- 优化页面布局
|
||||
- 解决更新标签页后打开无法显示的 bug
|
||||
- 解决不如输入组件按下回车直接黑屏的 bug
|
||||
- 更加完整方便的开发脚本
|
||||
|
||||
## [main] 0.0.1
|
||||
|
||||
- 完成 openmcp 的基础 inspector 功能
|
||||
|
39
README.md
39
README.md
@ -13,13 +13,23 @@
|
||||
|
||||
## OpenMCP
|
||||
|
||||
一款用于 MCP 服务端调试的一体化 vscode 插件。
|
||||
一款用于 MCP 服务端调试的一体化 vscode/trae 插件。
|
||||
|
||||

|
||||
集成 Inspector + MCP 客户端基础功能,开发测试一体化。
|
||||
|
||||
- 包含原版 Inpsector 的所有功能
|
||||
- 包含一个简易的用于进行测试的大模型对话 & 执行窗口
|
||||
- 支持多种大模型
|
||||

|
||||
|
||||
进行资源协议、工具、Prompt 的 MCP 服务器测试。
|
||||
|
||||

|
||||
|
||||
测试完成的工具可以放入 「交互测试」 模块之间进行大模型交互测试。
|
||||
|
||||

|
||||
|
||||
支持多种大模型
|
||||
|
||||

|
||||
|
||||
|
||||
## TODO
|
||||
@ -50,23 +60,22 @@ B <--mcp--> m(MCP Server)
|
||||
配置项目
|
||||
|
||||
```bash
|
||||
source configure.sh
|
||||
## linux
|
||||
./configure.sh
|
||||
## windows
|
||||
./configure.ps1
|
||||
```
|
||||
|
||||
启动 dev server
|
||||
|
||||
```bash
|
||||
cd renderer
|
||||
npm run serve
|
||||
```
|
||||
|
||||
启动 service
|
||||
|
||||
```bash
|
||||
cd service
|
||||
npm run serve
|
||||
## linux
|
||||
./dev.sh
|
||||
## windows
|
||||
./dev.ps1
|
||||
```
|
||||
|
||||
> 端口占用: 8080 (renderer) + 8081 (service)
|
||||
|
||||
### Extention Dev
|
||||
|
||||
|
@ -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
|
||||
$jobs = @(
|
||||
Start-Job -ScriptBlock {
|
||||
Set-Location $using:PWD\renderer
|
||||
npm run build
|
||||
Move-Item -Force -Path .\dist -Destination ..\resources\renderer
|
||||
}
|
||||
Start-Job -ScriptBlock {
|
||||
Set-Location $using:PWD\service
|
||||
npm run build
|
||||
Move-Item -Force -Path .\dist -Destination ..\resources\service
|
||||
}
|
||||
)
|
||||
# 并行构建 renderer 和 service
|
||||
$rendererJob = Start-Job -ScriptBlock {
|
||||
cd ./renderer
|
||||
npm run build
|
||||
mv ./dist ../resources/renderer
|
||||
}
|
||||
|
||||
# Wait for all jobs to complete
|
||||
Wait-Job -Job $jobs | Out-Null
|
||||
Receive-Job -Job $jobs
|
||||
Remove-Job -Job $jobs
|
||||
$serviceJob = Start-Job -ScriptBlock {
|
||||
cd ./service
|
||||
npm run build
|
||||
mv ./dist ../resources/service
|
||||
}
|
||||
|
||||
Write-Host "finish building services in ./resources"
|
||||
# 等待任务完成
|
||||
$rendererJob | Wait-Job | Receive-Job
|
||||
$serviceJob | Wait-Job | Receive-Job
|
||||
|
||||
Write-Output "finish building services in ./resources"
|
||||
|
@ -1,6 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
mkdir -p ./resources
|
||||
rm -rf ./resources/
|
||||
mkdir -p ./resources
|
||||
|
||||
(cd ./renderer && npm run build && mv ./dist ../resources/renderer) &
|
||||
(cd ./service && npm run build && mv ./dist ../resources/service) &
|
||||
wait
|
||||
|
13
configure.ps1
Normal file
13
configure.ps1
Normal 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
16
dev.ps1
Normal 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
12
dev.sh
Executable 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
BIN
icons/openmcp.chatbot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 435 KiB |
BIN
icons/openmcp.resource.png
Normal file
BIN
icons/openmcp.resource.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 492 KiB |
BIN
icons/openmcp.support.llm.png
Normal file
BIN
icons/openmcp.support.llm.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 435 KiB |
BIN
icons/openmcp.welcome.png
Normal file
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 |
@ -7,6 +7,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { Connection } from './components/sidebar/sidebar';
|
||||
|
||||
import Sidebar from '@/components/sidebar/index.vue';
|
||||
@ -26,40 +27,28 @@ bridge.addCommandListener('hello', data => {
|
||||
greenLog(`version: ${data.version}`);
|
||||
}, { 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() {
|
||||
connectionArgs.commandString = 'mcp run ../servers/main.py';
|
||||
connectionMethods.current = 'STDIO';
|
||||
|
||||
setTimeout(() => {
|
||||
setTimeout(async () => {
|
||||
// 初始化 设置
|
||||
loadSetting();
|
||||
|
||||
// 尝试连接
|
||||
await doConnect();
|
||||
|
||||
// 初始化 tab
|
||||
loadPanels();
|
||||
|
||||
// 尝试连接
|
||||
doConnect();
|
||||
|
||||
// 200 是我的电脑上的 ws 的连接时间,部署环境中不存在 ws 连接延时的问题,所以
|
||||
// 可以直接不管
|
||||
}, 200);
|
||||
}
|
||||
|
||||
function initProduce() {
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
async function initProduce() {
|
||||
// TODO: get from vscode
|
||||
connectionArgs.commandString = 'mcp run ../servers/main.py';
|
||||
connectionMethods.current = 'STDIO';
|
||||
@ -67,10 +56,15 @@ function initProduce() {
|
||||
// 初始化 设置
|
||||
loadSetting();
|
||||
|
||||
// 初始化 tab
|
||||
loadPanels();
|
||||
// 尝试连接
|
||||
await launchConnect();
|
||||
|
||||
launchConnect();
|
||||
// 初始化 tab
|
||||
await loadPanels();
|
||||
|
||||
if (route.name !== 'debug') {
|
||||
router.replace('/debug');
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -40,6 +40,7 @@
|
||||
import { defineComponent } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { addNewTab, tabs, closeTab } from './panel';
|
||||
import { panelLoaded } from '@/hook/panel';
|
||||
|
||||
defineComponent({ name: 'main-panel' });
|
||||
|
||||
|
@ -19,49 +19,50 @@ interface SaveTab {
|
||||
export const panelLoaded = ref(false);
|
||||
|
||||
export function loadPanels() {
|
||||
const bridge = useMessageBridge();
|
||||
|
||||
bridge.addCommandListener('panel/load', data => {
|
||||
if (data.code !== 200) {
|
||||
pinkLog('tabs 加载失败');
|
||||
console.log(data.msg);
|
||||
return new Promise((resolve, reject) => {
|
||||
const bridge = useMessageBridge();
|
||||
|
||||
} else {
|
||||
const persistTab = data.msg as SaveTab;
|
||||
bridge.addCommandListener('panel/load', data => {
|
||||
if (data.code !== 200) {
|
||||
pinkLog('tabs 加载失败');
|
||||
console.log(data.msg);
|
||||
|
||||
pinkLog('tabs 加载成功');
|
||||
} else {
|
||||
const persistTab = data.msg as SaveTab;
|
||||
|
||||
if (persistTab.tabs.length === 0) {
|
||||
// 空的,直接返回不需要管
|
||||
return;
|
||||
}
|
||||
pinkLog('tabs 加载成功');
|
||||
|
||||
tabs.activeIndex = 0;
|
||||
tabs.content = [];
|
||||
if (persistTab.tabs.length === 0) {
|
||||
// 空的,直接返回不需要管
|
||||
return;
|
||||
}
|
||||
|
||||
for (const tab of persistTab.tabs || []) {
|
||||
tabs.content.push({
|
||||
name: tab.name,
|
||||
icon: tab.icon,
|
||||
type: tab.type,
|
||||
componentIndex: tab.componentIndex,
|
||||
component: markRaw(debugModes[tab.componentIndex]),
|
||||
storage: tab.storage
|
||||
});
|
||||
}
|
||||
tabs.activeIndex = 0;
|
||||
tabs.content = [];
|
||||
|
||||
for (const tab of persistTab.tabs || []) {
|
||||
tabs.content.push({
|
||||
name: tab.name,
|
||||
icon: tab.icon,
|
||||
type: tab.type,
|
||||
componentIndex: tab.componentIndex,
|
||||
component: markRaw(debugModes[tab.componentIndex]),
|
||||
storage: tab.storage
|
||||
});
|
||||
}
|
||||
|
||||
tabs.activeIndex = persistTab.currentIndex;
|
||||
nextTick(() => {
|
||||
tabs.activeIndex = persistTab.currentIndex;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
panelLoaded.value = true;
|
||||
}, { once: true });
|
||||
panelLoaded.value = true;
|
||||
resolve(void 0);
|
||||
}, { once: true });
|
||||
|
||||
bridge.postMessage({
|
||||
command: 'panel/load'
|
||||
});
|
||||
bridge.postMessage({
|
||||
command: 'panel/load'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let debounceHandler: NodeJS.Timeout;
|
||||
@ -74,10 +75,10 @@ export function safeSavePanels() {
|
||||
}
|
||||
|
||||
export function savePanels(saveHandler?: () => void) {
|
||||
// 没有完成 panel 加载就不保存
|
||||
if (!panelLoaded.value) {
|
||||
return;
|
||||
}
|
||||
// // 没有完成 panel 加载就不保存
|
||||
// if (!panelLoaded.value) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
const bridge = useMessageBridge();
|
||||
|
||||
|
@ -57,44 +57,58 @@ export interface MCPOptions {
|
||||
|
||||
export function doConnect() {
|
||||
let connectOption: MCPOptions;
|
||||
|
||||
if (connectionMethods.current === 'STDIO') {
|
||||
|
||||
if (connectionArgs.commandString.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const commandComponents = connectionArgs.commandString.split(/\s+/g);
|
||||
const command = commandComponents[0];
|
||||
commandComponents.shift();
|
||||
|
||||
connectOption = {
|
||||
connectionType: 'STDIO',
|
||||
command: command,
|
||||
args: commandComponents,
|
||||
clientName: 'openmcp.connect.stdio.' + command,
|
||||
clientVersion: '0.0.1'
|
||||
}
|
||||
|
||||
} else {
|
||||
const url = connectionArgs.urlString;
|
||||
|
||||
if (url.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
connectOption = {
|
||||
connectionType: 'SSE',
|
||||
url: url,
|
||||
clientName: 'openmcp.connect.sse',
|
||||
clientVersion: '0.0.1'
|
||||
}
|
||||
}
|
||||
|
||||
const bridge = useMessageBridge();
|
||||
bridge.postMessage({
|
||||
command: 'connect',
|
||||
data: connectOption
|
||||
|
||||
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 (connectionArgs.commandString.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const commandComponents = connectionArgs.commandString.split(/\s+/g);
|
||||
const command = commandComponents[0];
|
||||
commandComponents.shift();
|
||||
|
||||
connectOption = {
|
||||
connectionType: 'STDIO',
|
||||
command: command,
|
||||
args: commandComponents,
|
||||
clientName: 'openmcp.connect.stdio.' + command,
|
||||
clientVersion: '0.0.1'
|
||||
}
|
||||
|
||||
} else {
|
||||
const url = connectionArgs.urlString;
|
||||
|
||||
if (url.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
connectOption = {
|
||||
connectionType: 'SSE',
|
||||
url: url,
|
||||
clientName: 'openmcp.connect.sse',
|
||||
clientVersion: '0.0.1'
|
||||
}
|
||||
}
|
||||
|
||||
bridge.postMessage({
|
||||
command: 'connect',
|
||||
data: connectOption
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -106,33 +120,47 @@ export async function launchConnect() {
|
||||
// 后续需要考虑到不同的连接方式
|
||||
|
||||
connectionMethods.current = 'STDIO';
|
||||
const bridge = useMessageBridge();
|
||||
|
||||
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();
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
// 监听 connect
|
||||
bridge.addCommandListener('connect', async data => {
|
||||
const { code, msg } = data;
|
||||
connectionResult.success = (code === 200);
|
||||
connectionResult.logString = msg;
|
||||
|
||||
const connectOption = {
|
||||
connectionType: 'STDIO',
|
||||
command: command,
|
||||
args: commandComponents,
|
||||
cwd: cwd,
|
||||
clientName: 'openmcp.connect.stdio.' + command,
|
||||
clientVersion: '0.0.1'
|
||||
};
|
||||
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 bridge = useMessageBridge();
|
||||
bridge.postMessage({
|
||||
command: 'connect',
|
||||
data: connectOption
|
||||
|
||||
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'
|
||||
};
|
||||
|
||||
bridge.postMessage({
|
||||
command: 'connect',
|
||||
data: connectOption
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -170,18 +198,18 @@ export const connectionResult = reactive({
|
||||
});
|
||||
|
||||
export function getServerVersion() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const bridge = useMessageBridge();
|
||||
bridge.addCommandListener('server/version', data => {
|
||||
if (data.code === 200) {
|
||||
resolve(data.msg);
|
||||
} else {
|
||||
reject(data.msg);
|
||||
}
|
||||
}, { once: true });
|
||||
return new Promise((resolve, reject) => {
|
||||
const bridge = useMessageBridge();
|
||||
bridge.addCommandListener('server/version', data => {
|
||||
if (data.code === 200) {
|
||||
resolve(data.msg);
|
||||
} else {
|
||||
reject(data.msg);
|
||||
}
|
||||
}, { once: true });
|
||||
|
||||
bridge.postMessage({
|
||||
command: 'server/version',
|
||||
});
|
||||
});
|
||||
bridge.postMessage({
|
||||
command: 'server/version',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
<Welcome v-show="!haveActiveTab"></Welcome>
|
||||
|
||||
<!-- 如果存在激活标签页,则根据标签页进行渲染 -->
|
||||
<div v-show="haveActiveTab" style="height: 100%;">
|
||||
<div v-show="haveActiveTab" v-if="panelLoaded" style="height: 100%;">
|
||||
<!-- vscode/trae 中,下面存在初始化问题 -->
|
||||
<component
|
||||
v-show="tab === tabs.content[tabs.activeIndex]"
|
||||
|
@ -36,5 +36,8 @@ module.exports = defineConfig({
|
||||
},
|
||||
css: {
|
||||
extract: false
|
||||
},
|
||||
devServer: {
|
||||
port: 8081
|
||||
}
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"currentIndex": 0,
|
||||
"currentIndex": 1,
|
||||
"tabs": [
|
||||
{
|
||||
"name": "资源",
|
||||
@ -7,54 +7,7 @@
|
||||
"type": "blank",
|
||||
"componentIndex": 0,
|
||||
"storage": {
|
||||
"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": ""
|
||||
}
|
||||
"currentResourceName": "greeting"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user