解决无法重新连接的问题 | 优化调试器布局

This commit is contained in:
锦恢 2025-04-21 01:02:21 +08:00
parent 69b907901f
commit 471ad41c8e
13 changed files with 230 additions and 95 deletions

View File

@ -1,4 +1,5 @@
<template> <template>
<el-scrollbar height="100%">
<div class="prompt-module"> <div class="prompt-module">
<div class="left"> <div class="left">
<h2> <h2>
@ -7,21 +8,16 @@
</h2> </h2>
<h3><code>prompts/list</code></h3> <h3><code>prompts/list</code></h3>
<PromptTemplates <PromptTemplates :tab-id="props.tabId"></PromptTemplates>
:tab-id="props.tabId"
></PromptTemplates>
</div> </div>
<div class="right"> <div class="right">
<PromptReader <PromptReader :tab-id="props.tabId"></PromptReader>
:tab-id="props.tabId"
></PromptReader>
<PromptLogger <PromptLogger :tab-id="props.tabId"></PromptLogger>
:tab-id="props.tabId"
></PromptLogger>
</div> </div>
</div> </div>
</el-scrollbar>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@ -1,4 +1,5 @@
<template> <template>
<el-scrollbar height="100%">
<div class="resource-module"> <div class="resource-module">
<div class="left"> <div class="left">
<h2> <h2>
@ -22,6 +23,7 @@
></ResourceLogger> ></ResourceLogger>
</div> </div>
</div> </div>
</el-scrollbar>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@ -1,4 +1,5 @@
<template> <template>
<el-scrollbar height="100%">
<div class="tool-module"> <div class="tool-module">
<div class="left"> <div class="left">
<h2> <h2>
@ -7,21 +8,17 @@
</h2> </h2>
<h3><code>tools/list</code></h3> <h3><code>tools/list</code></h3>
<ToolList <ToolList :tab-id="props.tabId"></ToolList>
:tab-id="props.tabId"
></ToolList>
</div> </div>
<div class="right"> <div class="right">
<ToolExecutor <ToolExecutor :tab-id="props.tabId"></ToolExecutor>
:tab-id="props.tabId"
></ToolExecutor>
<ToolLogger <ToolLogger :tab-id="props.tabId"></ToolLogger>
:tab-id="props.tabId"
></ToolLogger>
</div> </div>
</div> </div>
</el-scrollbar>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@ -5,7 +5,6 @@
<div class="tool-executor-container"> <div class="tool-executor-container">
<el-form :model="formData" :rules="formRules" ref="formRef" label-position="top"> <el-form :model="formData" :rules="formRules" ref="formRef" label-position="top">
<template v-if="currentTool?.inputSchema?.properties"> <template v-if="currentTool?.inputSchema?.properties">
<el-scrollbar height="150px">
<el-form-item <el-form-item
v-for="[name, property] in Object.entries(currentTool.inputSchema.properties)" v-for="[name, property] in Object.entries(currentTool.inputSchema.properties)"
:key="name" :key="name"
@ -34,7 +33,6 @@
v-model="formData[name]" v-model="formData[name]"
/> />
</el-form-item> </el-form-item>
</el-scrollbar>
</template> </template>
<el-form-item> <el-form-item>

View File

@ -13,7 +13,7 @@
/> />
</span> </span>
</span> </span>
<el-scrollbar height="300px"> <el-scrollbar height="500px">
<div <div
class="output-content" class="output-content"
contenteditable="false" contenteditable="false"
@ -95,7 +95,7 @@ const formattedJson = computed(() => {
.tool-logger .output-content { .tool-logger .output-content {
border-radius: .5em; border-radius: .5em;
padding: 15px; padding: 15px;
min-height: 300px; min-height: 450px;
height: fit-content; height: fit-content;
font-family: var(--code-font-family); font-family: var(--code-font-family);
white-space: pre-wrap; white-space: pre-wrap;

View File

@ -81,6 +81,7 @@ export function doConnect() {
resolve(void 0); resolve(void 0);
}, { once: true }); }, { once: true });
// TODO: 增加判断,获取 cwd
if (connectionMethods.current === 'STDIO') { if (connectionMethods.current === 'STDIO') {
if (connectionArgs.commandString.length === 0) { if (connectionArgs.commandString.length === 0) {
@ -124,20 +125,26 @@ export function doConnect() {
/** /**
* @description vscode * @description vscode
*/ */
export async function launchConnect() { export async function launchConnect(option: { updateCommandString?: boolean } = {}) {
// 本地开发只用 IPC 进行启动 // 本地开发只用 IPC 进行启动
// 后续需要考虑到不同的连接方式 // 后续需要考虑到不同的连接方式
const {
updateCommandString = true
} = option;
connectionMethods.current = 'STDIO'; connectionMethods.current = 'STDIO';
const bridge = useMessageBridge(); const bridge = useMessageBridge();
pinkLog('请求启动参数'); pinkLog('请求启动参数');
const { commandString, cwd } = await getLaunchCommand(); const { commandString, cwd } = await getLaunchCommand();
connectionArgs.commandString = commandString;
if (updateCommandString) {
connectionArgs.commandString = commandString;
if (connectionArgs.commandString.length === 0) { if (connectionArgs.commandString.length === 0) {
return; return;
} }
}
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
// 监听 connect // 监听 connect
@ -146,9 +153,17 @@ export async function launchConnect() {
connectionResult.success = (code === 200); connectionResult.success = (code === 200);
connectionResult.logString = msg; connectionResult.logString = msg;
if (code === 200) {
const res = await getServerVersion() as { name: string, version: string }; const res = await getServerVersion() as { name: string, version: string };
connectionResult.serverInfo.name = res.name || ''; connectionResult.serverInfo.name = res.name || '';
connectionResult.serverInfo.version = res.version || ''; connectionResult.serverInfo.version = res.version || '';
} else {
ElMessage({
type: 'error',
message: msg
});
}
resolve(void 0); resolve(void 0);
}, { once: true }); }, { once: true });

View File

@ -10,7 +10,7 @@
type="primary" type="primary"
size="large" size="large"
:disabled="!connectionResult" :disabled="!connectionResult"
@click="doConnect()" @click="suitableConnect()"
> >
{{ t('connect.appearance.connect') }} {{ t('connect.appearance.connect') }}
</el-button> </el-button>
@ -38,7 +38,7 @@ import { useI18n } from 'vue-i18n';
const { t } = useI18n(); const { t } = useI18n();
import { connectionResult, doConnect, doReconnect } from './connection'; import { connectionResult, doConnect, doReconnect, launchConnect } from './connection';
import ConnectionMethod from './connection-method.vue'; import ConnectionMethod from './connection-method.vue';
import ConnectionArgs from './connection-args.vue'; import ConnectionArgs from './connection-args.vue';
@ -46,7 +46,7 @@ import EnvVar from './env-var.vue';
import ConnectionLog from './connection-log.vue'; import ConnectionLog from './connection-log.vue';
import { useMessageBridge } from '@/api/message-bridge'; import { acquireVsCodeApi, useMessageBridge } from '@/api/message-bridge';
defineComponent({ name: 'connect' }); defineComponent({ name: 'connect' });
@ -58,6 +58,13 @@ bridge.addCommandListener('connect', data => {
connectionResult.logString = msg; connectionResult.logString = msg;
}, { once: false }); }, { once: false });
function suitableConnect() {
if (acquireVsCodeApi === undefined) {
doConnect();
} else {
launchConnect({ updateCommandString: false });
}
}
</script> </script>

74
servers/bing-picture.py Normal file
View File

@ -0,0 +1,74 @@
import requests
from pydantic import BaseModel
from typing import Optional, List
from mcp.server.fastmcp import FastMCP
from bs4 import BeautifulSoup
import json
from urllib import parse
import argparse
import os
import shutil
import uuid
import time
from threading import Thread
from typing import List
from bs4 import BeautifulSoup
import requests
class ImageResult(BaseModel):
url: str
title: str
source: str
class BingImage:
path = 'https://cn.bing.com/images/search'
block_num = 35
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36"
}
def bing_crawler(key_word: str, image_num: int) -> Optional[List[ImageResult]]:
query = {
'q': key_word,
'count': image_num
}
url = BingImage.path + '?' + parse.urlencode(query)
res = requests.get(url, headers=BingImage.headers)
soup = BeautifulSoup(res.content, "html.parser")
results = []
for a in soup.select('.iusc'):
href = a.attrs['href']
decode_href = parse.unquote(href)
media_url = decode_href.split('&')[4][9:]
title = a.get('alt', '')
results.append(ImageResult(
url=media_url,
title=title,
source="bing"
))
if len(results) >= image_num:
break
return results if results else None
mcp = FastMCP('image_crawler', version="0.0.1")
@mcp.tool(
name='image_crawler',
description='根据关键词从指定搜索引擎爬取图片'
)
def image_crawler_tool(key_word: str, image_num: int) -> str:
"""图片爬取工具,返回格式化字符串"""
images = bing_crawler(key_word, image_num)
return str(images)
if __name__ == "__main__":
images = bing_crawler("明日方舟 m3", 5)
print(images)

View File

@ -5,6 +5,7 @@ description = "Add your description here"
readme = "README.md" readme = "README.md"
requires-python = ">=3.11" requires-python = ">=3.11"
dependencies = [ dependencies = [
"bs4>=0.0.2",
"mcp[cli]>=1.5.0", "mcp[cli]>=1.5.0",
"requests>=2.32.3", "requests>=2.32.3",
] ]

36
servers/uv.lock generated
View File

@ -25,6 +25,31 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 }, { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 },
] ]
[[package]]
name = "beautifulsoup4"
version = "4.13.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "soupsieve" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d8/e4/0c4c39e18fd76d6a628d4dd8da40543d136ce2d1752bd6eeeab0791f4d6b/beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195", size = 621067 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/50/cd/30110dc0ffcf3b131156077b90e9f60ed75711223f306da4db08eff8403b/beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b", size = 187285 },
]
[[package]]
name = "bs4"
version = "0.0.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "beautifulsoup4" },
]
sdist = { url = "https://files.pythonhosted.org/packages/c9/aa/4acaf814ff901145da37332e05bb510452ebed97bc9602695059dd46ef39/bs4-0.0.2.tar.gz", hash = "sha256:a48685c58f50fe127722417bae83fe6badf500d54b55f7e39ffe43b798653925", size = 698 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/51/bb/bf7aab772a159614954d84aa832c129624ba6c32faa559dfb200a534e50b/bs4-0.0.2-py2.py3-none-any.whl", hash = "sha256:abf8742c0805ef7f662dce4b51cca104cffe52b835238afc169142ab9b3fbccc", size = 1189 },
]
[[package]] [[package]]
name = "certifi" name = "certifi"
version = "2025.1.31" version = "2025.1.31"
@ -335,12 +360,14 @@ name = "servers"
version = "0.1.0" version = "0.1.0"
source = { virtual = "." } source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "bs4" },
{ name = "mcp", extra = ["cli"] }, { name = "mcp", extra = ["cli"] },
{ name = "requests" }, { name = "requests" },
] ]
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "bs4", specifier = ">=0.0.2" },
{ name = "mcp", extras = ["cli"], specifier = ">=1.5.0" }, { name = "mcp", extras = ["cli"], specifier = ">=1.5.0" },
{ name = "requests", specifier = ">=2.32.3" }, { name = "requests", specifier = ">=2.32.3" },
] ]
@ -363,6 +390,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
] ]
[[package]]
name = "soupsieve"
version = "2.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186 },
]
[[package]] [[package]]
name = "sse-starlette" name = "sse-starlette"
version = "2.2.1" version = "2.2.1"

View File

@ -63,21 +63,9 @@ export class MCPClient {
this.transport = new StdioClientTransport({ this.transport = new StdioClientTransport({
command: this.options.command || '', command: this.options.command || '',
args: this.options.args || [], args: this.options.args || [],
cwd: this.options.cwd || process.cwd(), cwd: this.options.cwd || process.cwd()
// TODO
stderr: 'pipe'
}); });
this.transport.onmessage = (message) => {
console.log('Received message from server:', message);
this.transportStdErr += message;
};
this.transport.onerror = (error) => {
console.log('Error from server:', error);
this.transportStdErr += error;
};
break; break;
case 'SSE': case 'SSE':
if (!this.options.url) { if (!this.options.url) {

View File

@ -15,6 +15,9 @@ let client: MCPClient | undefined = undefined;
function tryGetRunCommandError(command: string, args: string[] = [], cwd?: string): string | null { function tryGetRunCommandError(command: string, args: string[] = [], cwd?: string): string | null {
try { try {
console.log('current command', command);
console.log('current args', args);
const result = spawnSync(command, args, { const result = spawnSync(command, args, {
cwd: cwd || process.cwd(), cwd: cwd || process.cwd(),
stdio: 'pipe', stdio: 'pipe',

View File

@ -1,5 +1,5 @@
{ {
"currentIndex": 0, "currentIndex": 2,
"tabs": [ "tabs": [
{ {
"name": "交互测试", "name": "交互测试",
@ -43,6 +43,24 @@
"systemPrompt": "" "systemPrompt": ""
} }
} }
},
{
"name": "工具",
"icon": "icon-tool",
"type": "blank",
"componentIndex": 2,
"storage": {
"currentToolName": "add"
}
},
{
"name": "资源",
"icon": "icon-file",
"type": "blank",
"componentIndex": 0,
"storage": {
"currentResourceName": "greeting"
}
} }
] ]
} }