This commit is contained in:
锦恢 2025-05-18 21:05:49 +08:00
parent c1cad24790
commit 9b45c272b4
31 changed files with 320 additions and 235 deletions

View File

@ -1,8 +1,9 @@
# Change Log # Change Log
## [main] 0.0.9 ## [main] 0.0.9
- 修复 0.0.8 引入的bugsystem prompt 返回的是索引而非真实内容 - 修复 0.0.8 引入的 bugsystem prompt 返回的是索引而非真实内容
- - 新特性:支持同时连入多个 mcp server
- 新特性:更新协议内容,支持 streamable http 协议,未来将逐步取代 SSE 的连接方式
## [main] 0.0.8 ## [main] 0.0.8
- 大模型 API 测试时更加完整的报错 - 大模型 API 测试时更加完整的报错

View File

@ -1 +1,2 @@
VITE_USE_AUTH=false
VITE_WEBSOCKET_URL=ws://localhost:8282 VITE_WEBSOCKET_URL=ws://localhost:8282

View File

@ -1 +1,2 @@
VITE_USE_AUTH=false
VITE_WEBSOCKET_URL=ws://localhost:8282 VITE_WEBSOCKET_URL=ws://localhost:8282

View File

@ -1,33 +1,18 @@
# test-vite ## dev
This template should help get you started developing with Vue 3 in Vite. 如果想要部署到公网中,想要通过密码认证才能进入,进行如下步骤:
## Recommended IDE Setup ```bash
touch .env.website.local
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
## Type Support for `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
## Customize configuration
See [Vite Configuration Reference](https://vite.dev/config/).
## Project Setup
```sh
npm install
``` ```
### Compile and Hot-Reload for Development 写入:
```sh ```toml
npm run dev VITE_USE_AUTH=true
VITE_WEBSOCKET_URL=wss://<IP>/<路径>
``` ```
### Type-Check, Compile and Minify for Production 使用 `npm run serve:website` 进行测试(服务端使用 ts-node src/server.ts
```sh 使用 `npm run build:website` 进行打包
npm run build
```

Binary file not shown.

View File

@ -4,7 +4,7 @@
<MainPanel></MainPanel> <MainPanel></MainPanel>
<Tour v-if="!userHasReadGuide"/> <Tour v-if="!userHasReadGuide"/>
<PasswordDialog v-if="password"/> <PasswordDialog v-if="useAuth"/>
</div> </div>
</template> </template>
@ -37,8 +37,8 @@ bridge.addCommandListener('hello', data => {
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const password = Boolean(import.meta.env.VITE_USE_PASSWORD); const useAuth = Boolean(import.meta.env.VITE_USE_AUTH);
privilegeStatus.allow = !Boolean(password); privilegeStatus.allow = !Boolean(useAuth);
onMounted(async () => { onMounted(async () => {
// css // css

View File

@ -7,9 +7,9 @@ export interface VSCodeMessage {
callbackId?: string; callbackId?: string;
} }
export interface RestFulResponse { export interface RestFulResponse<T = any> {
code: number; code: number;
msg: any; msg: T;
} }
export type MessageHandler = (message: VSCodeMessage) => void; export type MessageHandler = (message: VSCodeMessage) => void;
@ -206,7 +206,7 @@ export class MessageBridge {
* @param data * @param data
* @returns * @returns
*/ */
public commandRequest(command: string, data?: any) { public commandRequest<T = any>(command: string, data?: any): Promise<RestFulResponse<T>> {
return new Promise<RestFulResponse>((resolve, reject) => { return new Promise<RestFulResponse>((resolve, reject) => {
this.addCommandListener(command, (data) => { this.addCommandListener(command, (data) => {
resolve(data as RestFulResponse); resolve(data as RestFulResponse);

View File

@ -149,7 +149,7 @@ export type APIRequest =
| ToolCallRequest; | ToolCallRequest;
export interface IStdioConnectionItem { export interface IStdioConnectionItem {
type: 'stdio'; type: 'STDIO';
name: string; name: string;
command: string; command: string;
args: string[]; args: string[];
@ -159,7 +159,7 @@ export interface IStdioConnectionItem {
} }
export interface ISSEConnectionItem { export interface ISSEConnectionItem {
type: 'sse'; type: 'SSE';
name: string; name: string;
url: string; url: string;
oauth?: string; oauth?: string;
@ -169,13 +169,13 @@ export interface ISSEConnectionItem {
export interface IStdioLaunchSignature { export interface IStdioLaunchSignature {
type: 'stdio'; type: 'STDIO';
commandString: string; commandString: string;
cwd: string; cwd: string;
} }
export interface ISSELaunchSignature { export interface ISSELaunchSignature {
type:'sse'; type:'SSE';
url: string; url: string;
oauth: string; oauth: string;
} }

View File

@ -0,0 +1,58 @@
import { reactive, type Reactive } from "vue";
export type ConnectionType = 'STDIO' | 'SSE' | 'STREAMABLE_HTTP';
export interface ConnectionTypeOptionItem {
value: ConnectionType;
label: string;
}
export const connectionSelectDataViewOption: ConnectionTypeOptionItem[] = [
{
value: 'STDIO',
label: 'STDIO'
},
{
value: 'SSE',
label: 'SSE'
},
{
value: 'STREAMABLE_HTTP',
label: 'STREAMABLE_HTTP'
}
]
export interface IConnectionArgs {
type: ConnectionType;
commandString?: string;
cwd?: string;
urlString?: string;
}
export class McpClient {
public clientId?: string;
public name?: string;
public version?: string;
public connectionArgs: Reactive<IConnectionArgs>;
constructor() {
this.connectionArgs = reactive({
type: 'STDIO',
commandString: '',
cwd: '',
urlString: ''
});
}
async connect() {
}
}
// 用于描述一个连接的数据结构
export interface McpServer {
type: ConnectionType;
clientId: string;
name: string;
}

View File

@ -5,8 +5,15 @@ import { ElLoading, ElMessage } from 'element-plus';
import { getPlatform, type OpenMcpSupportPlatform } from '@/api/platform'; import { getPlatform, type OpenMcpSupportPlatform } from '@/api/platform';
import { getTour, loadSetting } from '@/hook/setting'; import { getTour, loadSetting } from '@/hook/setting';
import { loadPanels } from '@/hook/panel'; import { loadPanels } from '@/hook/panel';
import type { ConnectionType } from './connection-item';
export const connectionMethods = reactive({ export const connectionMethods = reactive<{
current: ConnectionType,
data: {
value: ConnectionType,
label: string
}[]
}>({
current: 'STDIO', current: 'STDIO',
data: [ data: [
{ {
@ -16,6 +23,10 @@ export const connectionMethods = reactive({
{ {
value: 'SSE', value: 'SSE',
label: 'SSE' label: 'SSE'
},
{
value: 'STREAMABLE_HTTP',
label: 'STREAMABLE_HTTP'
} }
] ]
}); });
@ -23,6 +34,7 @@ export const connectionMethods = reactive({
export const connectionSettingRef = ref<any>(null); export const connectionSettingRef = ref<any>(null);
export const connectionLogRef = ref<any>(null); export const connectionLogRef = ref<any>(null);
// 主 mcp 服务器的连接参数
export const connectionArgs = reactive({ export const connectionArgs = reactive({
commandString: '', commandString: '',
cwd: '', cwd: '',
@ -41,6 +53,13 @@ export interface IConnectionEnv {
newValue: string newValue: string
} }
export interface ConnectionResult {
status: string
clientId: string
name: string
version: string
}
export const connectionEnv = reactive<IConnectionEnv>({ export const connectionEnv = reactive<IConnectionEnv>({
data: [], data: [],
newKey: '', newKey: '',
@ -56,9 +75,6 @@ export function makeEnv() {
} }
// 定义连接类型
type ConnectionType = 'STDIO' | 'SSE';
// 定义命令行参数接口 // 定义命令行参数接口
export interface McpOptions { export interface McpOptions {
connectionType: ConnectionType; connectionType: ConnectionType;
@ -74,6 +90,14 @@ export interface McpOptions {
clientVersion?: string; clientVersion?: string;
} }
/**
* @description mcp
* 1.
* 2. mcp
* 3.
* @param option
* @returns
*/
export async function doConnect( export async function doConnect(
option: { option: {
namespace: OpenMcpSupportPlatform namespace: OpenMcpSupportPlatform
@ -86,32 +110,21 @@ export async function doConnect(
updateCommandString = true updateCommandString = true
} = option; } = option;
// 如果是初始化,则需要请求启动参数
if (updateCommandString) { if (updateCommandString) {
pinkLog('请求启动参数'); pinkLog('请求启动参数');
const connectionItem = await getLaunchSignature(namespace + '/launch-signature'); const connectionItem = await getLaunchSignature(namespace + '/launch-signature');
connectionMethods.current = connectionItem.type;
if (connectionItem.type ==='stdio') { connectionArgs.commandString = connectionItem.commandString || '';
connectionMethods.current = 'STDIO'; connectionArgs.cwd = connectionItem.cwd || '';
connectionArgs.commandString = connectionItem.commandString; connectionArgs.oauth = connectionItem.oauth || '';
connectionArgs.cwd = connectionItem.cwd;
if (connectionArgs.commandString.length === 0) {
return;
}
} else {
connectionMethods.current = 'SSE';
connectionArgs.urlString = connectionItem.url || ''; connectionArgs.urlString = connectionItem.url || '';
if (connectionArgs.urlString.length === 0) {
return;
}
}
} }
if (connectionMethods.current === 'STDIO') { if (connectionMethods.current === 'STDIO') {
await launchStdio(namespace); return await launchStdio(namespace);
} else { } else {
await launchSSE(namespace); return await launchRemote(namespace);
} }
} }
@ -128,26 +141,25 @@ async function launchStdio(namespace: string) {
command: command, command: command,
args: commandComponents, args: commandComponents,
cwd: connectionArgs.cwd, cwd: connectionArgs.cwd,
clientName: 'openmcp.connect.stdio', clientName: 'openmcp.connect.STDIO',
clientVersion: '0.0.1', clientVersion: '0.0.1',
env env
}; };
const { code, msg } = await bridge.commandRequest('connect', connectOption); const { code, msg } = await bridge.commandRequest<ConnectionResult>('connect', connectOption);
connectionResult.success = (code === 200); connectionResult.success = (code === 200);
if (code === 200) { if (code === 200) {
connectionResult.logString.push({
type: 'info',
message: msg
});
const res = await getServerVersion() as { name: string, version: string }; const message = `connect to ${msg.name} ${msg.version} success, clientId: ${msg.clientId}`;
connectionResult.serverInfo.name = res.name || ''; connectionResult.logString.push({ type: 'info', message });
connectionResult.serverInfo.version = res.version || '';
// 同步信息到 vscode connectionResult.serverInfo.name = msg.name || '';
connectionResult.serverInfo.version = msg.version || '';
connectionResult.clientId = msg.clientId || '';
// 同步信息到 后端
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();
@ -155,7 +167,7 @@ async function launchStdio(namespace: string) {
const clientStdioConnectionItem = { const clientStdioConnectionItem = {
serverInfo: connectionResult.serverInfo, serverInfo: connectionResult.serverInfo,
connectionType: 'STDIO', connectionType: 'STDIO',
name: 'openmcp.connect.stdio', name: 'openmcp.connect.STDIO',
command: command, command: command,
args: commandComponents, args: commandComponents,
cwd: connectionArgs.cwd, cwd: connectionArgs.cwd,
@ -168,46 +180,49 @@ async function launchStdio(namespace: string) {
}); });
} else { } else {
const messaage = msg.toString();
connectionResult.logString.push({ connectionResult.logString.push({
type: 'error', type: 'error',
message: msg message: messaage
}); });
ElMessage.error(msg); ElMessage.error(messaage);
} }
} }
async function launchSSE(namespace: string) { async function launchRemote(namespace: string) {
const bridge = useMessageBridge(); const bridge = useMessageBridge();
const env = makeEnv(); const env = makeEnv();
const connectOption: McpOptions = { const connectOption: McpOptions = {
connectionType: 'SSE', connectionType: connectionMethods.current,
url: connectionArgs.urlString, url: connectionArgs.urlString,
clientName: 'openmcp.connect.sse', clientName: 'openmcp.connect.' + connectionMethods.current,
clientVersion: '0.0.1', clientVersion: '0.0.1',
env env
}; };
const { code, msg } = await bridge.commandRequest('connect', connectOption); const { code, msg } = await bridge.commandRequest<ConnectionResult>('connect', connectOption);
connectionResult.success = (code === 200); connectionResult.success = (code === 200);
if (code === 200) { if (code === 200) {
const message = `connect to ${msg.name} ${msg.version} success, clientId: ${msg.clientId}`;
connectionResult.logString.push({ connectionResult.logString.push({
type: 'info', type: 'info',
message: msg message: message
}); });
const res = await getServerVersion() as { name: string, version: string }; connectionResult.serverInfo.name = msg.name || '';
connectionResult.serverInfo.name = res.name || ''; connectionResult.serverInfo.version = msg.version || '';
connectionResult.serverInfo.version = res.version || ''; connectionResult.clientId = msg.clientId || '';
// 同步信息到 vscode // 同步信息到 vscode
const clientSseConnectionItem = { const clientSseConnectionItem = {
serverInfo: connectionResult.serverInfo, serverInfo: connectionResult.serverInfo,
connectionType: 'SSE', connectionType: connectionMethods.current,
name: 'openmcp.connect.sse', name: 'openmcp.connect.' + connectionMethods.current,
url: connectionArgs.urlString, url: connectionArgs.urlString,
oauth: connectionArgs.oauth, oauth: connectionArgs.oauth,
env: env env: env
@ -219,12 +234,13 @@ async function launchSSE(namespace: string) {
}); });
} else { } else {
const message = msg.toString();
connectionResult.logString.push({ connectionResult.logString.push({
type: 'error', type: 'error',
message: msg message: message
}); });
ElMessage.error(msg); ElMessage.error(message);
} }
} }
@ -247,32 +263,19 @@ export const connectionResult = reactive<{
serverInfo: { serverInfo: {
name: string, name: string,
version: string version: string
} },
clientId: string
}>({ }>({
success: false, success: false,
logString: [], logString: [],
serverInfo: { serverInfo: {
name: '', name: '',
version: '' version: ''
} },
clientId: ''
}); });
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 });
bridge.postMessage({
command: 'server/version',
});
});
}
export const envVarStatus = { export const envVarStatus = {
launched: false launched: false

10
servers/uv.lock generated
View File

@ -175,10 +175,10 @@ wheels = [
] ]
[[package]] [[package]]
name = "httpx-sse" name = "httpx-SSE"
version = "0.4.0" version = "0.4.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 } sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-SSE-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 }, { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 },
] ]
@ -258,10 +258,10 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "anyio" }, { name = "anyio" },
{ name = "httpx" }, { name = "httpx" },
{ name = "httpx-sse" }, { name = "httpx-SSE" },
{ name = "pydantic" }, { name = "pydantic" },
{ name = "pydantic-settings" }, { name = "pydantic-settings" },
{ name = "sse-starlette" }, { name = "SSE-starlette" },
{ name = "starlette" }, { name = "starlette" },
{ name = "uvicorn" }, { name = "uvicorn" },
] ]
@ -477,7 +477,7 @@ wheels = [
] ]
[[package]] [[package]]
name = "sse-starlette" name = "SSE-starlette"
version = "2.2.1" version = "2.2.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [

View File

@ -3,9 +3,13 @@ import { McpClient } from "../mcp/client.service";
export type RequestClientType = McpClient | undefined; export type RequestClientType = McpClient | undefined;
export interface RequestData {
clientId?: string;
[key: string]: any;
}
export type RequestHandler<T, R> = ( export type RequestHandler<T, R> = (
client: RequestClientType, data: T & RequestData,
data: T,
webview: PostMessageble webview: PostMessageble
) => Promise<R>; ) => Promise<R>;

View File

@ -3,7 +3,6 @@ import type { PostMessageble } from "../hook/adapter";
import { LlmController } from "../llm/llm.controller"; import { LlmController } from "../llm/llm.controller";
import { ClientController } from "../mcp/client.controller"; import { ClientController } from "../mcp/client.controller";
import { ConnectController } from "../mcp/connect.controller"; import { ConnectController } from "../mcp/connect.controller";
import { client } from "../mcp/connect.service";
import { OcrController } from "../mcp/ocr.controller"; import { OcrController } from "../mcp/ocr.controller";
import { PanelController } from "../panel/panel.controller"; import { PanelController } from "../panel/panel.controller";
import { SettingController } from "../setting/setting.controller"; import { SettingController } from "../setting/setting.controller";
@ -24,7 +23,7 @@ export async function routeMessage(command: string, data: any, webview: PostMess
try { try {
// TODO: select client based on something // TODO: select client based on something
const res = await handler(client, data, webview); const res = await handler(data, webview);
// res.code = -1 代表当前请求不需要返回发送 // res.code = -1 代表当前请求不需要返回发送
if (res.code >= 0) { if (res.code >= 0) {

View File

@ -2,7 +2,7 @@ import { WebSocket } from 'ws';
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import { routeMessage } from '../common/router'; import { routeMessage } from '../common/router';
import { McpOptions } from '../mcp/client.dto'; import { McpOptions } from '../mcp/client.dto';
import { client, connectService } from '../mcp/connect.service'; import { clientMap, connectService } from '../mcp/connect.service';
// WebSocket 消息格式 // WebSocket 消息格式
export interface WebSocketMessage { export interface WebSocketMessage {
@ -114,9 +114,12 @@ export class TaskLoopAdapter {
* @param mcpOption * @param mcpOption
*/ */
public async connectMcpServer(mcpOption: McpOptions) { public async connectMcpServer(mcpOption: McpOptions) {
const res = await connectService(undefined, mcpOption); const res = await connectService(mcpOption);
if (res.code === 200) { if (res.code === 200) {
console.log('✅ 成功连接 mcp 服务器: ' + res.msg); console.log('✅ 成功连接 mcp 服务器: ' + res.msg);
const uuid = res.msg.uuid;
const client = clientMap.get(uuid);
const version = client?.getServerVersion(); const version = client?.getServerVersion();
console.log(version); console.log(version);
} else { } else {
@ -129,14 +132,19 @@ export class TaskLoopAdapter {
* @returns * @returns
*/ */
public async listTools() { public async listTools() {
const tools = await client?.listTools(); const tools = [];
if (tools?.tools) { for (const client of clientMap.values()) {
return tools.tools.map((tool) => { const clientTools = await client?.listTools();
const enabledTools = { ...tool, enabled: true }; if (clientTools?.tools) {
const enabledTools = clientTools.tools.map((tool) => {
const enabledTools = {...tool, enabled: true };
return enabledTools; return enabledTools;
}); });
tools.push(...enabledTools);
} }
return []; }
return tools;
} }
} }

View File

@ -1,13 +1,17 @@
import { Controller, RequestClientType } from "../common"; import { Controller, RequestClientType } from "../common";
import { RequestData } from "../common/index.dto";
import { PostMessageble } from "../hook/adapter"; import { PostMessageble } from "../hook/adapter";
import { getClient } from "../mcp/connect.service";
import { abortMessageService, streamingChatCompletion } from "./llm.service"; import { abortMessageService, streamingChatCompletion } from "./llm.service";
export class LlmController { export class LlmController {
@Controller('llm/chat/completions') @Controller('llm/chat/completions')
async chatCompletion(client: RequestClientType, data: any, webview: PostMessageble) { async chatCompletion(data: RequestData, webview: PostMessageble) {
let { tools = [] } = data; let { tools = [] } = data;
const client = getClient(data.clientId);
if (tools.length > 0 && !client) { if (tools.length > 0 && !client) {
return { return {
code: 501, code: 501,
@ -37,7 +41,7 @@ export class LlmController {
} }
@Controller('llm/chat/completions/abort') @Controller('llm/chat/completions/abort')
async abortChatCompletion(client: RequestClientType, data: any, webview: PostMessageble) { async abortChatCompletion(data: RequestData, webview: PostMessageble) {
return abortMessageService(data, webview); return abortMessageService(data, webview);
} }

View File

@ -28,13 +28,13 @@ const logger = pino({
export type MessageHandler = (message: VSCodeMessage) => void; export type MessageHandler = (message: VSCodeMessage) => void;
interface IStdioLaunchSignature { interface IStdioLaunchSignature {
type: 'stdio'; type: 'STDIO';
commandString: string; commandString: string;
cwd: string; cwd: string;
} }
interface ISSELaunchSignature { interface ISSELaunchSignature {
type:'sse'; type:'SSE';
url: string; url: string;
oauth: string; oauth: string;
} }
@ -43,7 +43,7 @@ export type ILaunchSigature = IStdioLaunchSignature | ISSELaunchSignature;
function refreshConnectionOption(envPath: string) { function refreshConnectionOption(envPath: string) {
const defaultOption = { const defaultOption = {
type:'stdio', type:'STDIO',
command: 'mcp', command: 'mcp',
args: ['run', 'main.py'], args: ['run', 'main.py'],
cwd: '../server' cwd: '../server'
@ -76,7 +76,7 @@ function updateConnectionOption(data: any) {
if (data.connectionType === 'STDIO') { if (data.connectionType === 'STDIO') {
const connectionItem = { const connectionItem = {
type: 'stdio', type: 'STDIO',
command: data.command, command: data.command,
args: data.args, args: data.args,
cwd: data.cwd.replace(/\\/g, '/') cwd: data.cwd.replace(/\\/g, '/')
@ -85,7 +85,7 @@ function updateConnectionOption(data: any) {
fs.writeFileSync(envPath, JSON.stringify(connectionItem, null, 4)); fs.writeFileSync(envPath, JSON.stringify(connectionItem, null, 4));
} else { } else {
const connectionItem = { const connectionItem = {
type: 'sse', type: 'SSE',
url: data.url, url: data.url,
oauth: data.oauth oauth: data.oauth
}; };
@ -124,14 +124,14 @@ wss.on('connection', (ws: any) => {
switch (command) { switch (command) {
case 'web/launch-signature': case 'web/launch-signature':
const launchResultMessage: ILaunchSigature = option.type === 'stdio' ? const launchResultMessage: ILaunchSigature = option.type === 'STDIO' ?
{ {
type: 'stdio', type: 'STDIO',
commandString: option.command + ' ' + option.args.join(' '), commandString: option.command + ' ' + option.args.join(' '),
cwd: option.cwd || '' cwd: option.cwd || ''
} : } :
{ {
type: 'sse', type: 'SSE',
url: option.url, url: option.url,
oauth: option.oauth || '' oauth: option.oauth || ''
}; };

View File

@ -1,11 +1,14 @@
import { Controller, RequestClientType } from "../common"; import { Controller } from "../common";
import { RequestData } from "../common/index.dto";
import { PostMessageble } from "../hook/adapter"; import { PostMessageble } from "../hook/adapter";
import { postProcessMcpToolcallResponse } from "./client.service"; import { postProcessMcpToolcallResponse } from "./client.service";
import { getClient } from "./connect.service";
export class ClientController { export class ClientController {
@Controller('server/version') @Controller('server/version')
async getServerVersion(client: RequestClientType, data: any, webview: PostMessageble) { async getServerVersion(data: RequestData, webview: PostMessageble) {
const client = getClient(data.clientId);
if (!client) { if (!client) {
return { return {
code: 501, code: 501,
@ -21,7 +24,8 @@ export class ClientController {
} }
@Controller('prompts/list') @Controller('prompts/list')
async listPrompts(client: RequestClientType, data: any, webview: PostMessageble) { async listPrompts(data: RequestData, webview: PostMessageble) {
const client = getClient(data.clientId);
if (!client) { if (!client) {
const connectResult = { const connectResult = {
code: 501, code: 501,
@ -39,7 +43,8 @@ export class ClientController {
} }
@Controller('prompts/get') @Controller('prompts/get')
async getPrompt(client: RequestClientType, option: any, webview: PostMessageble) { async getPrompt(data: RequestData, webview: PostMessageble) {
const client = getClient(data.clientId);
if (!client) { if (!client) {
return { return {
code: 501, code: 501,
@ -47,7 +52,7 @@ export class ClientController {
}; };
} }
const prompt = await client.getPrompt(option.promptId, option.args || {}); const prompt = await client.getPrompt(data.promptId, data.args || {});
return { return {
code: 200, code: 200,
msg: prompt msg: prompt
@ -55,7 +60,8 @@ export class ClientController {
} }
@Controller('resources/list') @Controller('resources/list')
async listResources(client: RequestClientType, data: any, webview: PostMessageble) { async listResources(data: RequestData, webview: PostMessageble) {
const client = getClient(data.clientId);
if (!client) { if (!client) {
return { return {
code: 501, code: 501,
@ -71,8 +77,8 @@ export class ClientController {
} }
@Controller('resources/templates/list') @Controller('resources/templates/list')
async listResourceTemplates(client: RequestClientType, data: any, webview: PostMessageble) { async listResourceTemplates(data: RequestData, webview: PostMessageble) {
const client = getClient(data.clientId);
if (!client) { if (!client) {
return { return {
code: 501, code: 501,
@ -88,7 +94,8 @@ export class ClientController {
} }
@Controller('resources/read') @Controller('resources/read')
async readResource(client: RequestClientType, option: any, webview: PostMessageble) { async readResource(data: RequestData, webview: PostMessageble) {
const client = getClient(data.clientId);
if (!client) { if (!client) {
return { return {
code: 501, code: 501,
@ -96,7 +103,7 @@ export class ClientController {
}; };
} }
const resource = await client.readResource(option.resourceUri); const resource = await client.readResource(data.resourceUri);
console.log(resource); console.log(resource);
return { return {
@ -106,7 +113,8 @@ export class ClientController {
} }
@Controller('tools/list') @Controller('tools/list')
async listTools(client: RequestClientType, data: any, webview: PostMessageble) { async listTools(data: RequestData, webview: PostMessageble) {
const client = getClient(data.clientId);
if (!client) { if (!client) {
return { return {
code: 501, code: 501,
@ -122,7 +130,8 @@ export class ClientController {
} }
@Controller('tools/call') @Controller('tools/call')
async callTool(client: RequestClientType, option: any, webview: PostMessageble) { async callTool(data: RequestData, webview: PostMessageble) {
const client = getClient(data.clientId);
if (!client) { if (!client) {
return { return {
code: 501, code: 501,
@ -131,18 +140,13 @@ export class ClientController {
} }
const toolResult = await client.callTool({ const toolResult = await client.callTool({
name: option.toolName, name: data.toolName,
arguments: option.toolArgs, arguments: data.toolArgs,
callToolOption: option.callToolOption callToolOption: data.callToolOption
}); });
// console.log(JSON.stringify(toolResult, null, 2));
postProcessMcpToolcallResponse(toolResult, webview); postProcessMcpToolcallResponse(toolResult, webview);
// console.log(JSON.stringify(toolResult, null, 2));
return { return {
code: 200, code: 200,
msg: toolResult msg: toolResult

View File

@ -1,5 +1,5 @@
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/STDIO.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"; import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/SSE.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import { Implementation } from "@modelcontextprotocol/sdk/types"; import { Implementation } from "@modelcontextprotocol/sdk/types";

View File

@ -1,7 +1,7 @@
import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/STDIO.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"; import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/SSE.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import type { McpOptions, McpTransport, IServerVersion, ToolCallResponse, ToolCallContent } from './client.dto'; import type { McpOptions, McpTransport, IServerVersion, ToolCallResponse, ToolCallContent } from './client.dto';
import { PostMessageble } from "../hook/adapter"; import { PostMessageble } from "../hook/adapter";

View File

@ -1,17 +1,19 @@
import { Controller, RequestClientType } from '../common'; import { Controller } from '../common';
import { PostMessageble } from '../hook/adapter'; import { PostMessageble } from '../hook/adapter';
import { connectService } from './connect.service'; import { RequestData } from '../common/index.dto';
import { connectService, getClient } from './connect.service';
export class ConnectController { export class ConnectController {
@Controller('connect') @Controller('connect')
async connect(client: RequestClientType, data: any, webview: PostMessageble) { async connect(data: any, webview: PostMessageble) {
const res = await connectService(client, data); const res = await connectService(data);
return res; return res;
} }
@Controller('lookup-env-var') @Controller('lookup-env-var')
async lookupEnvVar(client: RequestClientType, data: any, webview: PostMessageble) { async lookupEnvVar(data: RequestData, webview: PostMessageble) {
const client = getClient(data.clientId);
const { keys } = data; const { keys } = data;
const values = keys.map((key: string) => process.env[key] || ''); const values = keys.map((key: string) => process.env[key] || '');
@ -22,7 +24,8 @@ export class ConnectController {
} }
@Controller('ping') @Controller('ping')
async ping(client: RequestClientType, data: any, webview: PostMessageble) { async ping(data: RequestData, webview: PostMessageble) {
const client = getClient(data.clientId);
if (!client) { if (!client) {
const connectResult = { const connectResult = {
code: 501, code: 501,

View File

@ -3,10 +3,12 @@ import { RequestClientType } from '../common';
import { connect } from './client.service'; import { connect } from './client.service';
import { RestfulResponse } from '../common/index.dto'; import { RestfulResponse } from '../common/index.dto';
import { McpOptions } from './client.dto'; import { McpOptions } from './client.dto';
import { randomUUID } from 'node:crypto';
export const clientMap: Map<string, RequestClientType> = new Map();
// TODO: 更多的 client export function getClient(clientId?: string): RequestClientType | undefined {
export let client: RequestClientType = undefined; return clientMap.get(clientId || '');
}
export function tryGetRunCommandError(command: string, args: string[] = [], cwd?: string): string | null { export function tryGetRunCommandError(command: string, args: string[] = [], cwd?: string): string | null {
try { try {
@ -15,7 +17,7 @@ export function tryGetRunCommandError(command: string, args: string[] = [], cwd?
const result = spawnSync(command, args, { const result = spawnSync(command, args, {
cwd: cwd || process.cwd(), cwd: cwd || process.cwd(),
stdio: 'pipe', STDIO: 'pipe',
encoding: 'utf-8' encoding: 'utf-8'
}); });
@ -32,7 +34,6 @@ export function tryGetRunCommandError(command: string, args: string[] = [], cwd?
} }
export async function connectService( export async function connectService(
_client: RequestClientType,
option: McpOptions option: McpOptions
): Promise<RestfulResponse> { ): Promise<RestfulResponse> {
try { try {
@ -48,19 +49,25 @@ export async function connectService(
}); });
} }
client = await connect(option); const client = await connect(option);
const uuid = randomUUID();
clientMap.set(uuid, client);
const versionInfo = client.getServerVersion();
const connectResult = { const connectResult = {
code: 200, code: 200,
msg: 'Connect to OpenMCP successfully\nWelcome back, Kirigaya' msg: {
status: 'success',
clientId: uuid,
name: versionInfo?.name,
version: versionInfo?.version
}
}; };
return connectResult; return connectResult;
} catch (error) { } catch (error) {
console.log('meet error');
console.log(error);
// TODO: 这边获取到的 error 不够精致,如何才能获取到更加精准的错误 // TODO: 这边获取到的 error 不够精致,如何才能获取到更加精准的错误
// 比如 error: Failed to spawn: `server.py` // 比如 error: Failed to spawn: `server.py`
// Caused by: No such file or directory (os error 2) // Caused by: No such file or directory (os error 2)

View File

@ -1,11 +1,14 @@
import { Controller, RequestClientType } from "../common"; import { Controller } from "../common";
import { PostMessageble } from "../hook/adapter"; import { PostMessageble } from "../hook/adapter";
import { RequestData } from "../common/index.dto";
import { getClient } from "../mcp/connect.service";
import { systemPromptDB } from "../hook/db"; import { systemPromptDB } from "../hook/db";
import { loadTabSaveConfig, saveTabSaveConfig } from "./panel.service"; import { loadTabSaveConfig, saveTabSaveConfig } from "./panel.service";
export class PanelController { export class PanelController {
@Controller('panel/save') @Controller('panel/save')
async savePanel(client: RequestClientType, data: any, webview: PostMessageble) { async savePanel(data: RequestData, webview: PostMessageble) {
const client = getClient(data.clientId);
const serverInfo = client?.getServerVersion(); const serverInfo = client?.getServerVersion();
saveTabSaveConfig(serverInfo, data); saveTabSaveConfig(serverInfo, data);
@ -15,9 +18,9 @@ export class PanelController {
}; };
} }
@Controller('panel/load') @Controller('panel/load')
async loadPanel(client: RequestClientType, data: any, webview: PostMessageble) { async loadPanel(data: RequestData, webview: PostMessageble) {
const client = getClient(data.clientId);
const serverInfo = client?.getServerVersion(); const serverInfo = client?.getServerVersion();
const config = loadTabSaveConfig(serverInfo); const config = loadTabSaveConfig(serverInfo);
@ -28,7 +31,8 @@ export class PanelController {
} }
@Controller('system-prompts/set') @Controller('system-prompts/set')
async setSystemPrompt(client: RequestClientType, data: any, webview: PostMessageble) { async setSystemPrompt(data: RequestData, webview: PostMessageble) {
const client = getClient(data.clientId);
const { name, content } = data; const { name, content } = data;
await systemPromptDB.insert({ await systemPromptDB.insert({
@ -44,7 +48,8 @@ export class PanelController {
} }
@Controller('system-prompts/delete') @Controller('system-prompts/delete')
async deleteSystemPrompt(client: RequestClientType, data: any, webview: PostMessageble) { async deleteSystemPrompt(data: RequestData, webview: PostMessageble) {
const client = getClient(data.clientId);
const { name } = data; const { name } = data;
await systemPromptDB.delete(name); await systemPromptDB.delete(name);
return { return {
@ -54,7 +59,8 @@ export class PanelController {
} }
@Controller('system-prompts/save') @Controller('system-prompts/save')
async saveSystemPrompts(client: RequestClientType, data: any, webview: PostMessageble) { async saveSystemPrompts(data: RequestData, webview: PostMessageble) {
const client = getClient(data.clientId);
const { prompts } = data; const { prompts } = data;
await Promise.all(prompts.map((prompt: any) => { await Promise.all(prompts.map((prompt: any) => {
@ -72,8 +78,8 @@ export class PanelController {
} }
@Controller('system-prompts/load') @Controller('system-prompts/load')
async loadSystemPrompts(client: RequestClientType, data: any, webview: PostMessageble) { async loadSystemPrompts(data: RequestData, webview: PostMessageble) {
const client = getClient(data.clientId);
const queryPrompts = await systemPromptDB.findAll(); const queryPrompts = await systemPromptDB.findAll();
const prompts = []; const prompts = [];
for (const prompt of queryPrompts) { for (const prompt of queryPrompts) {

View File

@ -29,13 +29,13 @@ const logger = pino({
export type MessageHandler = (message: VSCodeMessage) => void; export type MessageHandler = (message: VSCodeMessage) => void;
interface IStdioLaunchSignature { interface IStdioLaunchSignature {
type: 'stdio'; type: 'STDIO';
commandString: string; commandString: string;
cwd: string; cwd: string;
} }
interface ISSELaunchSignature { interface ISSELaunchSignature {
type: 'sse'; type: 'SSE';
url: string; url: string;
oauth: string; oauth: string;
} }
@ -44,7 +44,7 @@ export type ILaunchSigature = IStdioLaunchSignature | ISSELaunchSignature;
function refreshConnectionOption(envPath: string) { function refreshConnectionOption(envPath: string) {
const defaultOption = { const defaultOption = {
type: 'stdio', type: 'STDIO',
command: 'mcp', command: 'mcp',
args: ['run', 'main.py'], args: ['run', 'main.py'],
cwd: '../server' cwd: '../server'
@ -84,7 +84,7 @@ function updateConnectionOption(data: any) {
if (data.connectionType === 'STDIO') { if (data.connectionType === 'STDIO') {
const connectionItem = { const connectionItem = {
type: 'stdio', type: 'STDIO',
command: data.command, command: data.command,
args: data.args, args: data.args,
cwd: data.cwd.replace(/\\/g, '/') cwd: data.cwd.replace(/\\/g, '/')
@ -93,7 +93,7 @@ function updateConnectionOption(data: any) {
fs.writeFileSync(envPath, JSON.stringify(connectionItem, null, 4)); fs.writeFileSync(envPath, JSON.stringify(connectionItem, null, 4));
} else { } else {
const connectionItem = { const connectionItem = {
type: 'sse', type: 'SSE',
url: data.url, url: data.url,
oauth: data.oauth oauth: data.oauth
}; };
@ -155,14 +155,14 @@ wss.on('connection', (ws: any) => {
switch (command) { switch (command) {
case 'web/launch-signature': case 'web/launch-signature':
const launchResultMessage: ILaunchSigature = option.type === 'stdio' ? const launchResultMessage: ILaunchSigature = option.type === 'STDIO' ?
{ {
type: 'stdio', type: 'STDIO',
commandString: option.command + ' ' + option.args.join(' '), commandString: option.command + ' ' + option.args.join(' '),
cwd: option.cwd || '' cwd: option.cwd || ''
} : } :
{ {
type: 'sse', type: 'SSE',
url: option.url, url: option.url,
oauth: option.oauth || '' oauth: option.oauth || ''
}; };

View File

@ -1,11 +1,14 @@
import { Controller, RequestClientType } from "../common"; import { Controller } from "../common";
import { PostMessageble } from "../hook/adapter"; import { PostMessageble } from "../hook/adapter";
import { RequestData } from "../common/index.dto";
import { getClient } from "../mcp/connect.service";
import { getTour, loadSetting, saveSetting, setTour } from "./setting.service"; import { getTour, loadSetting, saveSetting, setTour } from "./setting.service";
export class SettingController { export class SettingController {
@Controller('setting/save') @Controller('setting/save')
async saveSetting(client: RequestClientType, data: any, webview: PostMessageble) { async saveSetting(data: RequestData, webview: PostMessageble) {
const client = getClient(data.clientId);
saveSetting(data); saveSetting(data);
console.log('Settings saved successfully'); console.log('Settings saved successfully');
@ -16,8 +19,8 @@ export class SettingController {
} }
@Controller('setting/load') @Controller('setting/load')
async loadSetting(client: RequestClientType, data: any, webview: PostMessageble) { async loadSetting(data: RequestData, webview: PostMessageble) {
const client = getClient(data.clientId);
const config = loadSetting(); const config = loadSetting();
return { return {
code: 200, code: 200,
@ -26,10 +29,8 @@ export class SettingController {
} }
@Controller('setting/set-tour') @Controller('setting/set-tour')
async setTourController(client: RequestClientType, data: any, webview: PostMessageble) { async setTourController(data: any, webview: PostMessageble) {
const { userHasReadGuide } = data; const { userHasReadGuide } = data;
setTour(userHasReadGuide); setTour(userHasReadGuide);
return { return {
@ -39,7 +40,7 @@ export class SettingController {
} }
@Controller('setting/get-tour') @Controller('setting/get-tour')
async getTourController(client: RequestClientType, data: any, webview: PostMessageble) { async getTourController(data: any, webview: PostMessageble) {
const { userHasReadGuide } = getTour(); const { userHasReadGuide } = getTour();

View File

@ -40,14 +40,14 @@ function createWindow(): void {
switch (command) { switch (command) {
case 'electron/launch-signature': case 'electron/launch-signature':
const launchResultMessage: ILaunchSigature = option.type === 'stdio' ? const launchResultMessage: ILaunchSigature = option.type === 'STDIO' ?
{ {
type: 'stdio', type: 'STDIO',
commandString: option.command + ' ' + option.args.join(' '), commandString: option.command + ' ' + option.args.join(' '),
cwd: option.cwd || '' cwd: option.cwd || ''
} : } :
{ {
type: 'sse', type: 'SSE',
url: option.url, url: option.url,
oauth: option.oauth || '' oauth: option.oauth || ''
}; };

View File

@ -24,13 +24,13 @@ export class ElectronIPCLike {
interface IStdioLaunchSignature { interface IStdioLaunchSignature {
type: 'stdio'; type: 'STDIO';
commandString: string; commandString: string;
cwd: string; cwd: string;
} }
interface ISSELaunchSignature { interface ISSELaunchSignature {
type:'sse'; type:'SSE';
url: string; url: string;
oauth: string; oauth: string;
} }
@ -39,7 +39,7 @@ export type ILaunchSigature = IStdioLaunchSignature | ISSELaunchSignature;
export function refreshConnectionOption(envPath: string) { export function refreshConnectionOption(envPath: string) {
const defaultOption = { const defaultOption = {
type:'stdio', type:'STDIO',
command: 'mcp', command: 'mcp',
args: ['run', 'main.py'], args: ['run', 'main.py'],
cwd: '../server' cwd: '../server'
@ -80,7 +80,7 @@ export function updateConnectionOption(data: any) {
if (data.connectionType === 'STDIO') { if (data.connectionType === 'STDIO') {
const connectionItem = { const connectionItem = {
type: 'stdio', type: 'STDIO',
command: data.command, command: data.command,
args: data.args, args: data.args,
cwd: data.cwd.replace(/\\/g, '/') cwd: data.cwd.replace(/\\/g, '/')
@ -89,7 +89,7 @@ export function updateConnectionOption(data: any) {
fs.writeFileSync(envPath, JSON.stringify(connectionItem, null, 4)); fs.writeFileSync(envPath, JSON.stringify(connectionItem, null, 4));
} else { } else {
const connectionItem = { const connectionItem = {
type: 'sse', type: 'SSE',
url: data.url, url: data.url,
oauth: data.oauth oauth: data.oauth
}; };

View File

@ -7,7 +7,7 @@ export type FsPath = string;
export const panels = new Map<FsPath, vscode.WebviewPanel>(); export const panels = new Map<FsPath, vscode.WebviewPanel>();
export interface IStdioConnectionItem { export interface IStdioConnectionItem {
type: 'stdio'; type: 'STDIO';
name: string; name: string;
version?: string; version?: string;
command: string; command: string;
@ -18,7 +18,7 @@ export interface IStdioConnectionItem {
} }
export interface ISSEConnectionItem { export interface ISSEConnectionItem {
type: 'sse'; type: 'SSE';
name: string; name: string;
version: string; version: string;
url: string; url: string;
@ -29,13 +29,13 @@ export interface ISSEConnectionItem {
interface IStdioLaunchSignature { interface IStdioLaunchSignature {
type: 'stdio'; type: 'STDIO';
commandString: string; commandString: string;
cwd: string; cwd: string;
} }
interface ISSELaunchSignature { interface ISSELaunchSignature {
type:'sse'; type:'SSE';
url: string; url: string;
oauth: string; oauth: string;
} }
@ -123,7 +123,7 @@ export function getWorkspaceConnectionConfig() {
if (item.filePath && item.filePath.startsWith('{workspace}')) { if (item.filePath && item.filePath.startsWith('{workspace}')) {
item.filePath = item.filePath.replace('{workspace}', workspacePath).replace(/\\/g, '/'); item.filePath = item.filePath.replace('{workspace}', workspacePath).replace(/\\/g, '/');
} }
if (item.type === 'stdio' && item.cwd && item.cwd.startsWith('{workspace}')) { if (item.type === 'STDIO' && item.cwd && item.cwd.startsWith('{workspace}')) {
item.cwd = item.cwd.replace('{workspace}', workspacePath).replace(/\\/g, '/'); item.cwd = item.cwd.replace('{workspace}', workspacePath).replace(/\\/g, '/');
} }
} }
@ -169,7 +169,7 @@ export function saveWorkspaceConnectionConfig(workspace: string) {
if (item.filePath && item.filePath.replace(/\\/g, '/').startsWith(workspacePath)) { if (item.filePath && item.filePath.replace(/\\/g, '/').startsWith(workspacePath)) {
item.filePath = item.filePath.replace(workspacePath, '{workspace}').replace(/\\/g, '/'); item.filePath = item.filePath.replace(workspacePath, '{workspace}').replace(/\\/g, '/');
} }
if (item.type ==='stdio' && item.cwd && item.cwd.replace(/\\/g, '/').startsWith(workspacePath)) { if (item.type ==='STDIO' && item.cwd && item.cwd.replace(/\\/g, '/').startsWith(workspacePath)) {
item.cwd = item.cwd.replace(workspacePath, '{workspace}').replace(/\\/g, '/'); item.cwd = item.cwd.replace(workspacePath, '{workspace}').replace(/\\/g, '/');
} }
} }
@ -213,7 +213,7 @@ export function updateWorkspaceConnectionConfig(
if (data.connectionType === 'STDIO') { if (data.connectionType === 'STDIO') {
const connectionItem: IStdioConnectionItem = { const connectionItem: IStdioConnectionItem = {
type: 'stdio', type: 'STDIO',
name: data.serverInfo.name, name: data.serverInfo.name,
version: data.serverInfo.version, version: data.serverInfo.version,
command: data.command, command: data.command,
@ -234,7 +234,7 @@ export function updateWorkspaceConnectionConfig(
} else { } else {
const connectionItem: ISSEConnectionItem = { const connectionItem: ISSEConnectionItem = {
type: 'sse', type: 'SSE',
name: data.serverInfo.name, name: data.serverInfo.name,
version: data.serverInfo.version, version: data.serverInfo.version,
url: data.url, url: data.url,
@ -267,7 +267,7 @@ export function updateInstalledConnectionConfig(
if (data.connectionType === 'STDIO') { if (data.connectionType === 'STDIO') {
const connectionItem: IStdioConnectionItem = { const connectionItem: IStdioConnectionItem = {
type: 'stdio', type: 'STDIO',
name: data.serverInfo.name, name: data.serverInfo.name,
version: data.serverInfo.version, version: data.serverInfo.version,
command: data.command, command: data.command,
@ -287,7 +287,7 @@ export function updateInstalledConnectionConfig(
} else { } else {
const connectionItem: ISSEConnectionItem = { const connectionItem: ISSEConnectionItem = {
type: 'sse', type: 'SSE',
name: data.serverInfo.name, name: data.serverInfo.name,
version: data.serverInfo.version, version: data.serverInfo.version,
url: data.url, url: data.url,

View File

@ -52,7 +52,7 @@ export async function validateAndGetCommandPath(commandString: string, cwd?: str
export async function acquireInstalledConnection(): Promise<IConnectionItem | undefined> { export async function acquireInstalledConnection(): Promise<IConnectionItem | undefined> {
// 让用户选择连接类型 // 让用户选择连接类型
const connectionType = await vscode.window.showQuickPick(['stdio', 'sse'], { const connectionType = await vscode.window.showQuickPick(['STDIO', 'SSE'], {
placeHolder: '请选择连接类型', placeHolder: '请选择连接类型',
canPickMany: false canPickMany: false
}); });
@ -61,7 +61,7 @@ export async function acquireInstalledConnection(): Promise<IConnectionItem | un
return; // 用户取消选择 return; // 用户取消选择
} }
if (connectionType === 'stdio') { if (connectionType === 'STDIO') {
// 获取 command // 获取 command
const commandString = await vscode.window.showInputBox({ const commandString = await vscode.window.showInputBox({
prompt: '请输入连接的 command', prompt: '请输入连接的 command',
@ -97,15 +97,15 @@ export async function acquireInstalledConnection(): Promise<IConnectionItem | un
// 保存连接配置 // 保存连接配置
return { return {
type: 'stdio', type: 'STDIO',
name: `stdio-${Date.now()}`, name: `STDIO-${Date.now()}`,
command: command, command: command,
args, args,
cwd: cwd || '', cwd: cwd || '',
filePath: filePath, filePath: filePath,
}; };
} else if (connectionType === 'sse') { } else if (connectionType === 'SSE') {
// 获取 url // 获取 url
const url = await vscode.window.showInputBox({ const url = await vscode.window.showInputBox({
prompt: '请输入连接的 URL', prompt: '请输入连接的 URL',
@ -124,8 +124,8 @@ export async function acquireInstalledConnection(): Promise<IConnectionItem | un
// 保存连接配置 // 保存连接配置
return { return {
type: 'sse', type: 'SSE',
name: `sse-${Date.now()}`, name: `SSE-${Date.now()}`,
version: '1.0', // 假设默认版本为 1.0,可根据实际情况修改 version: '1.0', // 假设默认版本为 1.0,可根据实际情况修改
url: url, url: url,
oauth: oauth || '' oauth: oauth || ''

View File

@ -6,7 +6,7 @@ import * as vscode from 'vscode';
export async function acquireUserCustomConnection(): Promise<IConnectionItem | undefined> { export async function acquireUserCustomConnection(): Promise<IConnectionItem | undefined> {
// 让用户选择连接类型 // 让用户选择连接类型
const connectionType = await vscode.window.showQuickPick(['stdio', 'sse'], { const connectionType = await vscode.window.showQuickPick(['STDIO', 'SSE'], {
placeHolder: '请选择连接类型' placeHolder: '请选择连接类型'
}); });
@ -14,7 +14,7 @@ export async function acquireUserCustomConnection(): Promise<IConnectionItem | u
return; // 用户取消选择 return; // 用户取消选择
} }
if (connectionType === 'stdio') { if (connectionType === 'STDIO') {
// 获取 command // 获取 command
const commandString = await vscode.window.showInputBox({ const commandString = await vscode.window.showInputBox({
prompt: '请输入连接的 command', prompt: '请输入连接的 command',
@ -47,15 +47,15 @@ export async function acquireUserCustomConnection(): Promise<IConnectionItem | u
// 保存连接配置 // 保存连接配置
return { return {
type: 'stdio', type: 'STDIO',
name: `stdio-${Date.now()}`, name: `STDIO-${Date.now()}`,
command: command, command: command,
args, args,
cwd: cwd || '', cwd: cwd || '',
filePath filePath
}; };
} else if (connectionType === 'sse') { } else if (connectionType === 'SSE') {
// 获取 url // 获取 url
const url = await vscode.window.showInputBox({ const url = await vscode.window.showInputBox({
prompt: '请输入连接的 URL', prompt: '请输入连接的 URL',
@ -74,8 +74,8 @@ export async function acquireUserCustomConnection(): Promise<IConnectionItem | u
// 保存连接配置 // 保存连接配置
return { return {
type: 'sse', type: 'SSE',
name: `sse-${Date.now()}`, name: `SSE-${Date.now()}`,
version: '1.0', // 假设默认版本为 1.0,可根据实际情况修改 version: '1.0', // 假设默认版本为 1.0,可根据实际情况修改
url: url, url: url,
oauth: oauth || '' oauth: oauth || ''

View File

@ -19,7 +19,7 @@ export class WebviewController {
} }
revealOpenMcpWebviewPanel(context, 'workspace', uri.fsPath, { revealOpenMcpWebviewPanel(context, 'workspace', uri.fsPath, {
type: 'stdio', type: 'STDIO',
name: 'OpenMCP', name: 'OpenMCP',
command: sigature.command, command: sigature.command,
args: sigature.args, args: sigature.args,

View File

@ -29,7 +29,7 @@ export function revealOpenMcpWebviewPanel(
type: 'workspace' | 'installed', type: 'workspace' | 'installed',
panelKey: string, panelKey: string,
option: IConnectionItem = { option: IConnectionItem = {
type: 'stdio', type: 'STDIO',
name: 'OpenMCP', name: 'OpenMCP',
command: 'mcp', command: 'mcp',
args: ['run', 'main.py'] args: ['run', 'main.py']
@ -68,14 +68,14 @@ export function revealOpenMcpWebviewPanel(
// 拦截消息,注入额外信息 // 拦截消息,注入额外信息
switch (command) { switch (command) {
case 'vscode/launch-signature': case 'vscode/launch-signature':
const launchResultMessage: ILaunchSigature = option.type === 'stdio' ? const launchResultMessage: ILaunchSigature = option.type === 'STDIO' ?
{ {
type: 'stdio', type: 'STDIO',
commandString: option.command + ' ' + option.args.join(' '), commandString: option.command + ' ' + option.args.join(' '),
cwd: option.cwd || '' cwd: option.cwd || ''
} : } :
{ {
type: 'sse', type: 'SSE',
url: option.url, url: option.url,
oauth: option.oauth || '' oauth: option.oauth || ''
}; };