完成 connect

This commit is contained in:
huangzhelong.byte 2025-03-29 16:34:47 +08:00
parent bac3e5c253
commit c8c069574e
27 changed files with 680 additions and 165 deletions

View File

@ -19,6 +19,15 @@
- 包含一个简易的用于进行测试的大模型对话 & 执行窗口
- 支持多种大模型
---
## TODO
- [x] 完成最基本的各类基础设施
- [ ] 支持同时调试多个 MCP Server
- [ ] 支持通过大模型进行在线验证
---
## Dev

Binary file not shown.

View File

@ -11,6 +11,8 @@
--toolbar-height: 50px;
--toolbar-item-height: 32px;
--code-font-family: "Cascadia code";
/* css 动画属性 */
--animation-7s: .7s cubic-bezier(0.23,1,0.32,1);
--animation-5s: .5s cubic-bezier(0.23,1,0.32,1);

View File

@ -18,10 +18,12 @@ import { useMessageBridge } from './api/message-bridge';
const bridge = useMessageBridge();
//
bridge.onMessage((message) => {
console.log('Received:', message.command, message.data);
bridge.addCommandListener('hello', data => {
pinkLog(`${data.name} 上线`);
pinkLog(`version: ${data.version}`);
});
//
const sendPing = () => {
bridge.postMessage({

View File

@ -8,11 +8,13 @@ export interface VSCodeMessage {
}
export type MessageHandler = (message: VSCodeMessage) => void;
export type CommandHandler = (data: any) => void;
export const acquireVsCodeApi = (window as any)['acquireVsCodeApi'];
class MessageBridge {
private ws: WebSocket | null = null;
private handlers = new Set<MessageHandler>();
private handlers = new Map<string, Set<CommandHandler>>();
public isConnected = ref(false);
constructor(private wsUrl: string = 'ws://localhost:8080') {
@ -72,17 +74,35 @@ class MessageBridge {
};
}
/**
* @description message command
* @param message
*/
private dispatchMessage(message: VSCodeMessage) {
this.handlers.forEach(handler => handler(message));
const command = message.command;
const data = message.data;
const handlers = this.handlers.get(command) || [];
handlers.forEach(handler => handler(data));
}
public postMessage(message: VSCodeMessage) {
throw new Error('PostMessage not initialized');
}
/**
* @description
* @param handler
* @returns
*/
public addCommandListener(command: string, commandHandler: CommandHandler) {
if (!this.handlers.has(command)) {
this.handlers.set(command, new Set<CommandHandler>());
}
const commandHandlers = this.handlers.get(command)!;
commandHandlers.add(commandHandler);
public onMessage(handler: MessageHandler) {
this.handlers.add(handler);
return () => this.handlers.delete(handler);
return () => commandHandlers.delete(commandHandler);
}
public destroy() {
@ -104,7 +124,7 @@ export function useMessageBridge() {
return {
postMessage: bridge.postMessage.bind(bridge),
onMessage: bridge.onMessage.bind(bridge),
addCommandListener: bridge.addCommandListener.bind(bridge),
isConnected: bridge.isConnected
};
}

View File

@ -22,6 +22,8 @@ export function setDefaultCss() {
document.body.style.setProperty('--el-color-primary-dark-2', 'var(--main-light-color)');
document.body.style.setProperty('--el-fill-color-dark', 'var(--main-light-color)');
document.body.style.setProperty('--el-fill-color-darker', 'var(--main-light-color)');
document.body.style.setProperty('--el-color-primary-light-5', 'var(--button-disabled)');
// document.body.style.setProperty('--el-color-white', 'var(--background)');
// 设置全局宏

View File

@ -103,5 +103,6 @@
"api-token": "مفتاح API",
"connection-method": "طريقة الاتصال",
"command": "أمر",
"env-var": "متغيرات البيئة"
"env-var": "متغيرات البيئة",
"log": "سجلات"
}

View File

@ -103,5 +103,6 @@
"api-token": "API-Schlüssel",
"connection-method": "Verbindungsmethode",
"command": "Befehl",
"env-var": "Umgebungsvariablen"
"env-var": "Umgebungsvariablen",
"log": "Protokolle"
}

View File

@ -103,5 +103,6 @@
"api-token": "API key",
"connection-method": "Connection method",
"command": "Command",
"env-var": "Environment variables"
"env-var": "Environment variables",
"log": "Logs"
}

View File

@ -103,5 +103,6 @@
"api-token": "Clé API",
"connection-method": "Méthode de connexion",
"command": "Commande",
"env-var": "Variables d'environnement"
"env-var": "Variables d'environnement",
"log": "Journaux"
}

View File

@ -103,5 +103,6 @@
"api-token": "APIキー",
"connection-method": "接続方法",
"command": "コマンド",
"env-var": "環境変数"
"env-var": "環境変数",
"log": "ログ"
}

View File

@ -103,5 +103,6 @@
"api-token": "API 키",
"connection-method": "연결 방법",
"command": "명령",
"env-var": "환경 변수"
"env-var": "환경 변수",
"log": "로그"
}

View File

@ -103,5 +103,6 @@
"api-token": "API-ключ",
"connection-method": "Способ подключения",
"command": "Команда",
"env-var": "Переменные среды"
"env-var": "Переменные среды",
"log": "Логи"
}

View File

@ -103,5 +103,6 @@
"api-token": "API 密钥",
"connection-method": "连接方式",
"command": "命令",
"env-var": "环境变量"
"env-var": "环境变量",
"log": "日志"
}

View File

@ -103,5 +103,6 @@
"api-token": "API 密鑰",
"connection-method": "連接方式",
"command": "命令",
"env-var": "環境變數"
"env-var": "環境變數",
"log": "日誌"
}

View File

@ -1,6 +1,11 @@
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
const routes: Array<RouteRecordRaw> = [
{
name : "default",
path : "/",
redirect : "/debug"
},
{
path: "/debug",
name: "debug",

View File

@ -33,13 +33,9 @@ import { connectionArgs, connectionMethods } from './connection';
const { t } = useI18n();
interface ConnectionMethods {
current: string
}
const stdioForm = ref<FormInstance>()
const urlForm = ref<FormInstance>()
//
const rules = reactive<FormRules>({
commandString: [

View File

@ -0,0 +1,42 @@
<template>
<div class="connection-option">
<span>{{ t('log') }}</span>
<el-scrollbar height="300px">
<div
class="output-content"
contenteditable="false"
>
{{ connectionResult.logString }}
</div>
</el-scrollbar>
</div>
</template>
<script setup lang="ts">
import { defineComponent } from 'vue';
import { useI18n } from 'vue-i18n';
import { connectionResult } from './connection';
defineComponent({ name: 'connection-log' });
const { t } = useI18n();
</script>
<style>
.connection-option .output-content {
border-radius: .5em;
padding: 15px;
min-height: 300px;
height: fit-content;
font-family: var(--code-font-family);
white-space: pre-wrap;
word-break: break-all;
user-select: text;
cursor: text;
font-size: 15px;
line-height: 1.3;
background-color: var(--sidebar);
}
</style>

View File

@ -0,0 +1,24 @@
<template>
<div class="connection-option">
<span>{{ t('connection-method') }}</span>
<span style="width: 200px;">
<el-select name="language-setting" class="language-setting" v-model="connectionMethods.current">
<el-option v-for="option in connectionMethods.data" :value="option.value" :label="option.label"
:key="option.label"></el-option>
</el-select>
</span>
</div>
</template>
<script setup lang="ts">
import { defineComponent } from 'vue';
import { useI18n } from 'vue-i18n';
import { connectionMethods } from './connection';
defineComponent({ name: 'connection-method' });
const { t } = useI18n();
</script>
<style></style>

View File

@ -37,10 +37,6 @@ export const connectionEnv = reactive<IConnectionEnv>({
newValue: ''
});
export function onconnectionmethodchange() {
console.log();
}
// 定义连接类型
type ConnectionType = 'STDIO' | 'SSE';
@ -58,12 +54,15 @@ export interface MCPOptions {
clientVersion?: string;
}
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();
@ -79,6 +78,10 @@ export function doConnect() {
} else {
const url = connectionArgs.urlString;
if (url.length === 0) {
return;
}
connectOption = {
connectionType: 'SSE',
url: url,
@ -92,4 +95,14 @@ export function doConnect() {
command: 'connect',
data: connectOption
});
}
}
export function doReconnect() {
// TODO: finish this
console.log();
}
export const connectionResult = reactive({
success: false,
logString: ''
});

View File

@ -0,0 +1,83 @@
<template>
<div class="connection-option">
<span>{{ t('env-var') }}</span>
<div class="input-env">
<span class="input-env-container">
<span>
<el-input v-model="connectionEnv.newKey" @keyup.enter="addEnvVar"></el-input>
</span>
<span>
<el-input v-model="connectionEnv.newValue" @keyup.enter="addEnvVar"></el-input>
</span>
<span>
<div @click="addEnvVar">
<span class="iconfont icon-add"></span>
</div>
</span>
</span>
</div>
<el-scrollbar height="200px" width="350px" class="display-env-container">
<div class="display-env">
<div class="input-env-container" v-for="option of connectionEnv.data" :key="option.key">
<span> <el-input v-model="option.key"></el-input></span>
<span> <el-input v-model="option.value" show-password></el-input></span>
<span @click="deleteEnvVar(option)">
<span class="iconfont icon-delete"></span>
</span>
</div>
</div>
</el-scrollbar>
</div>
</template>
<script setup lang="ts">
import { defineComponent } from 'vue';
import { useI18n } from 'vue-i18n';
import { connectionEnv, EnvItem } from './connection';
defineComponent({ name: 'env-var' });
const { t } = useI18n();
/**
* @description 添加环境变量
*/
function addEnvVar() {
// key
const currentKey = connectionEnv.newKey;
const currentValue = connectionEnv.newValue;
if (currentKey.length === 0 || currentValue.length === 0) {
return;
}
const sameNameItems = connectionEnv.data.filter(item => item.key === currentKey);
if (sameNameItems.length > 0) {
const conflictItem = sameNameItems[0];
conflictItem.value = currentValue;
} else {
connectionEnv.data.push({
key: currentKey, value: currentValue
});
connectionEnv.newKey = '';
connectionEnv.newValue = '';
}
}
/**
* @description 删除环境变量
*/
function deleteEnvVar(option: EnvItem) {
const currentKey = option.key;
const reserveItems = connectionEnv.data.filter(item => item.key !== currentKey);
connectionEnv.data = reserveItems;
}
</script>
<style></style>

View File

@ -1,124 +1,74 @@
<template>
<div class="connect-panel-container">
<div class="connection-option">
<span>{{ t('connection-method') }}</span>
<span style="width: 200px;">
<el-select name="language-setting" class="language-setting" v-model="connectionMethods.current"
@change="onconnectionmethodchange">
<el-option v-for="option in connectionMethods.data" :value="option.value" :label="option.label"
:key="option.label"></el-option>
</el-select>
</span>
</div>
<div class="connection-container">
<div class="connect-panel-container">
<ConnectionMethod></ConnectionMethod>
<ConnectionArgs></ConnectionArgs>
<EnvVar></EnvVar>
<div class="connect-action">
<el-button
type="primary"
size="large"
:disabled="!connectionResult"
@click="doConnect()"
>
Connect
</el-button>
<ConnectionArgs></ConnectionArgs>
<div class="connection-option">
<span>{{ t('env-var') }}</span>
<div class="input-env">
<span class="input-env-container">
<span>
<el-input v-model="connectionEnv.newKey" @keyup.enter="addEnvVar"></el-input>
</span>
<span>
<el-input v-model="connectionEnv.newValue" @keyup.enter="addEnvVar"></el-input>
</span>
<span>
<div @click="addEnvVar">
<span class="iconfont icon-add"></span>
</div>
</span>
</span>
<el-button
type="primary"
size="large"
@click="doReconnect()"
>
Reconnect
</el-button>
</div>
<el-scrollbar height="200px" width="350px" class="display-env-container">
<div class="display-env">
<div class="input-env-container" v-for="option of connectionEnv.data" :key="option.key">
<span> <el-input v-model="option.key"></el-input></span>
<span> <el-input v-model="option.value" show-password></el-input></span>
<span @click="deleteEnvVar(option)">
<span class="iconfont icon-delete"></span>
</span>
</div>
</div>
</el-scrollbar>
</div>
<div class="connect-action">
<el-button type="primary" size="large"
@click="doConnect()"
>
Connect
</el-button>
<div class="connect-panel-container">
<ConnectionLog></ConnectionLog>
</div>
</div>
</template>
<script setup lang="ts">
import { defineComponent } from 'vue';
import { useI18n } from 'vue-i18n';
import { connectionArgs, connectionEnv, connectionMethods, doConnect, EnvItem, onconnectionmethodchange } from './connection';
import { connectionResult, doConnect, doReconnect } from './connection';
import ConnectionMethod from './connection-method.vue';
import ConnectionArgs from './connection-args.vue';
import EnvVar from './env-var.vue';
import ConnectionLog from './connection-log.vue';
import { useMessageBridge } from '@/api/message-bridge';
defineComponent({ name: 'connect' });
const { t } = useI18n();
const bridge = useMessageBridge();
bridge.onMessage(message => {
if (message.command === 'connect') {
console.log('connect result');
console.log(message.data);
}
bridge.addCommandListener('connect', data => {
const { code, msg } = data;
connectionResult.success = (code === 200);
connectionResult.logString = msg;
});
/**
* @description 添加环境变量
*/
function addEnvVar() {
// key
const currentKey = connectionEnv.newKey;
const currentValue = connectionEnv.newValue;
if (currentKey.length === 0 || currentValue.length === 0) {
return;
}
const sameNameItems = connectionEnv.data.filter(item => item.key === currentKey);
if (sameNameItems.length > 0) {
const conflictItem = sameNameItems[0];
conflictItem.value = currentValue;
} else {
connectionEnv.data.push({
key: currentKey, value: currentValue
});
connectionEnv.newKey = '';
connectionEnv.newValue = '';
}
}
/**
* @description 删除环境变量
*/
function deleteEnvVar(option: EnvItem) {
const currentKey = option.key;
const reserveItems = connectionEnv.data.filter(item => item.key !== currentKey);
connectionEnv.data = reserveItems;
}
</script>
<style>
.connection-container {
display: flex;
}
.connect-panel-container {
display: flex;
flex-direction: column;
width: 60%;
width: 45%;
min-width: 300px;
padding: 20px;
}
@ -129,6 +79,7 @@ function deleteEnvVar(option: EnvItem) {
padding: 10px;
margin-bottom: 20px;
border-radius: .5em;
border: 1px solid var(--background);
}
.connection-option>span:first-child {

401
test/package-lock.json generated
View File

@ -11,14 +11,19 @@
"dependencies": {
"@modelcontextprotocol/sdk": "^1.8.0",
"body-parser": "^1.20.3",
"chalk": "^5.4.1",
"cors": "^2.8.5",
"debug": "^4.4.0",
"express": "^4.21.1",
"morgan": "^1.10.0",
"pako": "^2.1.0",
"pino": "^9.6.0",
"pino-pretty": "^13.0.0",
"ws": "^8.18.1"
},
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/debug": "^4.1.12",
"@types/express": "^5.0.0",
"@types/morgan": "^1.9.9",
"@types/node": "^22.7.5",
@ -465,6 +470,16 @@
"@types/node": "*"
}
},
"node_modules/@types/debug": {
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
"integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/ms": "*"
}
},
"node_modules/@types/express": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.1.tgz",
@ -514,6 +529,13 @@
"@types/node": "*"
}
},
"node_modules/@types/ms": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "22.13.13",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.13.tgz",
@ -630,6 +652,15 @@
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"license": "MIT"
},
"node_modules/atomic-sleep": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
"integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
"license": "MIT",
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/basic-auth": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
@ -672,6 +703,21 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/body-parser/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/body-parser/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@ -710,6 +756,24 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/chalk": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
"integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
"license": "MIT",
"engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/colorette": {
"version": "2.0.20",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
"license": "MIT"
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@ -779,13 +843,30 @@
"node": ">= 8"
}
},
"node_modules/dateformat": {
"version": "4.6.3",
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
"integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/depd": {
@ -846,6 +927,15 @@
"node": ">= 0.8"
}
},
"node_modules/end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"license": "MIT",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
@ -970,6 +1060,42 @@
"express": "^4.11 || 5 || ^5.0.0-beta.1"
}
},
"node_modules/express/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/express/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
"node_modules/fast-copy": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz",
"integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==",
"license": "MIT"
},
"node_modules/fast-redact": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz",
"integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/fast-safe-stringify": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
"integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
"license": "MIT"
},
"node_modules/finalhandler": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
@ -988,6 +1114,21 @@
"node": ">= 0.8"
}
},
"node_modules/finalhandler/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/finalhandler/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@ -1088,6 +1229,12 @@
"node": ">= 0.4"
}
},
"node_modules/help-me": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz",
"integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==",
"license": "MIT"
},
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
@ -1141,6 +1288,15 @@
"resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
},
"node_modules/joycon": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
"integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
@ -1217,6 +1373,15 @@
"node": ">= 0.6"
}
},
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/morgan": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
@ -1233,6 +1398,21 @@
"node": ">= 0.8.0"
}
},
"node_modules/morgan/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/morgan/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
"node_modules/morgan/node_modules/on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
@ -1246,9 +1426,9 @@
}
},
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/negotiator": {
@ -1281,6 +1461,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/on-exit-leak-free": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
"integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
@ -1339,6 +1528,67 @@
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
"license": "MIT"
},
"node_modules/pino": {
"version": "9.6.0",
"resolved": "https://registry.npmjs.org/pino/-/pino-9.6.0.tgz",
"integrity": "sha512-i85pKRCt4qMjZ1+L7sy2Ag4t1atFcdbEt76+7iRJn1g2BvsnRMGu9p8pivl9fs63M2kF/A0OacFZhTub+m/qMg==",
"license": "MIT",
"dependencies": {
"atomic-sleep": "^1.0.0",
"fast-redact": "^3.1.1",
"on-exit-leak-free": "^2.1.0",
"pino-abstract-transport": "^2.0.0",
"pino-std-serializers": "^7.0.0",
"process-warning": "^4.0.0",
"quick-format-unescaped": "^4.0.3",
"real-require": "^0.2.0",
"safe-stable-stringify": "^2.3.1",
"sonic-boom": "^4.0.1",
"thread-stream": "^3.0.0"
},
"bin": {
"pino": "bin.js"
}
},
"node_modules/pino-abstract-transport": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz",
"integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==",
"license": "MIT",
"dependencies": {
"split2": "^4.0.0"
}
},
"node_modules/pino-pretty": {
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.0.0.tgz",
"integrity": "sha512-cQBBIVG3YajgoUjo1FdKVRX6t9XPxwB9lcNJVD5GCnNM4Y6T12YYx8c6zEejxQsU0wrg9TwmDulcE9LR7qcJqA==",
"license": "MIT",
"dependencies": {
"colorette": "^2.0.7",
"dateformat": "^4.6.3",
"fast-copy": "^3.0.2",
"fast-safe-stringify": "^2.1.1",
"help-me": "^5.0.0",
"joycon": "^3.1.1",
"minimist": "^1.2.6",
"on-exit-leak-free": "^2.1.0",
"pino-abstract-transport": "^2.0.0",
"pump": "^3.0.0",
"secure-json-parse": "^2.4.0",
"sonic-boom": "^4.0.1",
"strip-json-comments": "^3.1.1"
},
"bin": {
"pino-pretty": "bin.js"
}
},
"node_modules/pino-std-serializers": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz",
"integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==",
"license": "MIT"
},
"node_modules/pkce-challenge": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/pkce-challenge/-/pkce-challenge-4.1.0.tgz",
@ -1347,6 +1597,22 @@
"node": ">=16.20.0"
}
},
"node_modules/process-warning": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz",
"integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"license": "MIT"
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@ -1360,6 +1626,16 @@
"node": ">= 0.10"
}
},
"node_modules/pump": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz",
"integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==",
"license": "MIT",
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"node_modules/qs": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
@ -1375,6 +1651,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/quick-format-unescaped": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
"license": "MIT"
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@ -1399,6 +1681,15 @@
"node": ">= 0.8"
}
},
"node_modules/real-require": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
"integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
"license": "MIT",
"engines": {
"node": ">= 12.13.0"
}
},
"node_modules/router": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/router/-/router-2.2.0.tgz",
@ -1414,27 +1705,6 @@
"node": ">= 18"
}
},
"node_modules/router/node_modules/debug": {
"version": "4.4.0",
"resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.0.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/router/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/router/node_modules/path-to-regexp": {
"version": "8.2.0",
"resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
@ -1463,12 +1733,27 @@
],
"license": "MIT"
},
"node_modules/safe-stable-stringify": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
"integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT"
},
"node_modules/secure-json-parse": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz",
"integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==",
"license": "BSD-3-Clause"
},
"node_modules/send": {
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
@ -1493,6 +1778,21 @@
"node": ">= 0.8.0"
}
},
"node_modules/send/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/send/node_modules/debug/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
"node_modules/send/node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
@ -1502,12 +1802,6 @@
"node": ">= 0.8"
}
},
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/serve-static": {
"version": "1.16.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
@ -1620,6 +1914,24 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/sonic-boom": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz",
"integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==",
"license": "MIT",
"dependencies": {
"atomic-sleep": "^1.0.0"
}
},
"node_modules/split2": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
"license": "ISC",
"engines": {
"node": ">= 10.x"
}
},
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
@ -1629,6 +1941,27 @@
"node": ">= 0.8"
}
},
"node_modules/strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"license": "MIT",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/thread-stream": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz",
"integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==",
"license": "MIT",
"dependencies": {
"real-require": "^0.2.0"
}
},
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",

View File

@ -18,6 +18,7 @@
"license": "MIT",
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/debug": "^4.1.12",
"@types/express": "^5.0.0",
"@types/morgan": "^1.9.9",
"@types/node": "^22.7.5",
@ -29,10 +30,14 @@
"dependencies": {
"@modelcontextprotocol/sdk": "^1.8.0",
"body-parser": "^1.20.3",
"chalk": "^5.4.1",
"cors": "^2.8.5",
"debug": "^4.4.0",
"express": "^4.21.1",
"morgan": "^1.10.0",
"pako": "^2.1.0",
"pino": "^9.6.0",
"pino-pretty": "^13.0.0",
"ws": "^8.18.1"
}
}

View File

@ -51,8 +51,8 @@ class MCPClient {
switch (this.options.connectionType) {
case 'STDIO':
this.transport = new StdioClientTransport({
command: this.options.command || 'node',
args: this.options.args || ['server.js']
command: this.options.command || '',
args: this.options.args || []
});
break;
case 'SSE':

View File

@ -11,9 +11,14 @@ async function connectHandler(option: MCPOptions, webview: VSCodeWebViewLike) {
};
webview.postMessage({ command: 'connect', data: connectResult });
} catch (error) {
// TODO: 这边获取到的 error 不够精致,如何才能获取到更加精准的错误
// 比如 error: Failed to spawn: `server.py`
// Caused by: No such file or directory (os error 2)
const connectResult = {
code: 500,
msg: error
msg: (error as any).toString()
};
webview.postMessage({ command: 'connect', data: connectResult });
}

View File

@ -1,5 +1,6 @@
// server/wsServer.ts
import WebSocket from 'ws';
import pino from 'pino';
import { messageController } from './controller';
import { VSCodeWebViewLike } from './adapter';
@ -9,6 +10,18 @@ export interface VSCodeMessage {
callbackId?: string;
}
const logger = pino({
transport: {
target: 'pino-pretty', // 启用 pino-pretty
options: {
colorize: true, // 开启颜色
levelFirst: true, // 先打印日志级别
translateTime: 'yyyy-mm-dd HH:MM:ss', // 格式化时间
ignore: 'pid,hostname', // 忽略部分字段
}
}
});
export type MessageHandler = (message: VSCodeMessage) => void;
const wss = new WebSocket.Server({ port: 8080 });
@ -20,16 +33,17 @@ wss.on('connection', ws => {
// 先发送成功建立的消息
webview.postMessage({
command: 'hello',
data: 'hello'
data: {
version: '0.0.1',
name: 'OpenMCP 测试用中台调度程序'
}
});
// 注册消息接受的管线
webview.onDidReceiveMessage(message => {
try {
const { command, data } = message;
messageController(command, data, webview);
} catch (error) {
console.log('backend, meet error during [message], ', error);
}
logger.info(`command: [${message.command || 'No Command'}]`);
const { command, data } = message;
messageController(command, data, webview);
});
});