解决无法重新连接的问题 | 优化调试器布局
This commit is contained in:
parent
69b907901f
commit
471ad41c8e
@ -1,27 +1,23 @@
|
||||
<template>
|
||||
<div class="prompt-module">
|
||||
<div class="left">
|
||||
<h2>
|
||||
<span class="iconfont icon-chat"></span>
|
||||
提示词模块
|
||||
</h2>
|
||||
<h3><code>prompts/list</code></h3>
|
||||
|
||||
<PromptTemplates
|
||||
:tab-id="props.tabId"
|
||||
></PromptTemplates>
|
||||
<el-scrollbar height="100%">
|
||||
<div class="prompt-module">
|
||||
<div class="left">
|
||||
<h2>
|
||||
<span class="iconfont icon-chat"></span>
|
||||
提示词模块
|
||||
</h2>
|
||||
<h3><code>prompts/list</code></h3>
|
||||
|
||||
</div>
|
||||
<div class="right">
|
||||
<PromptReader
|
||||
:tab-id="props.tabId"
|
||||
></PromptReader>
|
||||
<PromptTemplates :tab-id="props.tabId"></PromptTemplates>
|
||||
|
||||
<PromptLogger
|
||||
:tab-id="props.tabId"
|
||||
></PromptLogger>
|
||||
</div>
|
||||
<div class="right">
|
||||
<PromptReader :tab-id="props.tabId"></PromptReader>
|
||||
|
||||
<PromptLogger :tab-id="props.tabId"></PromptLogger>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -1,4 +1,5 @@
|
||||
<template>
|
||||
<el-scrollbar height="100%">
|
||||
<div class="resource-module">
|
||||
<div class="left">
|
||||
<h2>
|
||||
@ -21,7 +22,8 @@
|
||||
:tab-id="props.tabId"
|
||||
></ResourceLogger>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -1,27 +1,24 @@
|
||||
<template>
|
||||
<div class="tool-module">
|
||||
<div class="left">
|
||||
<h2>
|
||||
<span class="iconfont icon-tool"></span>
|
||||
工具模块
|
||||
</h2>
|
||||
<h3><code>tools/list</code></h3>
|
||||
|
||||
<ToolList
|
||||
:tab-id="props.tabId"
|
||||
></ToolList>
|
||||
<el-scrollbar height="100%">
|
||||
<div class="tool-module">
|
||||
<div class="left">
|
||||
<h2>
|
||||
<span class="iconfont icon-tool"></span>
|
||||
工具模块
|
||||
</h2>
|
||||
<h3><code>tools/list</code></h3>
|
||||
|
||||
</div>
|
||||
<div class="right">
|
||||
<ToolExecutor
|
||||
:tab-id="props.tabId"
|
||||
></ToolExecutor>
|
||||
<ToolList :tab-id="props.tabId"></ToolList>
|
||||
|
||||
<ToolLogger
|
||||
:tab-id="props.tabId"
|
||||
></ToolLogger>
|
||||
</div>
|
||||
<div class="right">
|
||||
<ToolExecutor :tab-id="props.tabId"></ToolExecutor>
|
||||
|
||||
<ToolLogger :tab-id="props.tabId"></ToolLogger>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -5,36 +5,34 @@
|
||||
<div class="tool-executor-container">
|
||||
<el-form :model="formData" :rules="formRules" ref="formRef" label-position="top">
|
||||
<template v-if="currentTool?.inputSchema?.properties">
|
||||
<el-scrollbar height="150px">
|
||||
<el-form-item
|
||||
v-for="[name, property] in Object.entries(currentTool.inputSchema.properties)"
|
||||
:key="name"
|
||||
:label="property.title || name"
|
||||
:prop="name"
|
||||
:required="currentTool.inputSchema.required?.includes(name)"
|
||||
>
|
||||
<el-input
|
||||
v-if="property.type === 'string'"
|
||||
v-model="formData[name]"
|
||||
type="text"
|
||||
:placeholder="t('enter') + ' ' + (property.title || name)"
|
||||
@keydown.enter.prevent="handleExecute"
|
||||
/>
|
||||
<el-form-item
|
||||
v-for="[name, property] in Object.entries(currentTool.inputSchema.properties)"
|
||||
:key="name"
|
||||
:label="property.title || name"
|
||||
:prop="name"
|
||||
:required="currentTool.inputSchema.required?.includes(name)"
|
||||
>
|
||||
<el-input
|
||||
v-if="property.type === 'string'"
|
||||
v-model="formData[name]"
|
||||
type="text"
|
||||
:placeholder="t('enter') + ' ' + (property.title || name)"
|
||||
@keydown.enter.prevent="handleExecute"
|
||||
/>
|
||||
|
||||
<el-input-number
|
||||
v-else-if="property.type === 'number' || property.type === 'integer'"
|
||||
v-model="formData[name]"
|
||||
controls-position="right"
|
||||
:placeholder="t('enter') + ' ' + (property.title || name)"
|
||||
@keydown.enter.prevent="handleExecute"
|
||||
/>
|
||||
<el-input-number
|
||||
v-else-if="property.type === 'number' || property.type === 'integer'"
|
||||
v-model="formData[name]"
|
||||
controls-position="right"
|
||||
:placeholder="t('enter') + ' ' + (property.title || name)"
|
||||
@keydown.enter.prevent="handleExecute"
|
||||
/>
|
||||
|
||||
<el-switch
|
||||
v-else-if="property.type === 'boolean'"
|
||||
v-model="formData[name]"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-scrollbar>
|
||||
<el-switch
|
||||
v-else-if="property.type === 'boolean'"
|
||||
v-model="formData[name]"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-form-item>
|
||||
|
@ -13,7 +13,7 @@
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
<el-scrollbar height="300px">
|
||||
<el-scrollbar height="500px">
|
||||
<div
|
||||
class="output-content"
|
||||
contenteditable="false"
|
||||
@ -95,7 +95,7 @@ const formattedJson = computed(() => {
|
||||
.tool-logger .output-content {
|
||||
border-radius: .5em;
|
||||
padding: 15px;
|
||||
min-height: 300px;
|
||||
min-height: 450px;
|
||||
height: fit-content;
|
||||
font-family: var(--code-font-family);
|
||||
white-space: pre-wrap;
|
||||
|
@ -81,6 +81,7 @@ export function doConnect() {
|
||||
resolve(void 0);
|
||||
}, { once: true });
|
||||
|
||||
// TODO: 增加判断,获取 cwd
|
||||
if (connectionMethods.current === 'STDIO') {
|
||||
|
||||
if (connectionArgs.commandString.length === 0) {
|
||||
@ -124,19 +125,25 @@ export function doConnect() {
|
||||
/**
|
||||
* @description vscode 中初始化启动
|
||||
*/
|
||||
export async function launchConnect() {
|
||||
export async function launchConnect(option: { updateCommandString?: boolean } = {}) {
|
||||
// 本地开发只用 IPC 进行启动
|
||||
// 后续需要考虑到不同的连接方式
|
||||
|
||||
const {
|
||||
updateCommandString = true
|
||||
} = option;
|
||||
|
||||
connectionMethods.current = 'STDIO';
|
||||
const bridge = useMessageBridge();
|
||||
|
||||
pinkLog('请求启动参数');
|
||||
const { commandString, cwd } = await getLaunchCommand();
|
||||
connectionArgs.commandString = commandString;
|
||||
|
||||
if (connectionArgs.commandString.length === 0) {
|
||||
return;
|
||||
if (updateCommandString) {
|
||||
connectionArgs.commandString = commandString;
|
||||
if (connectionArgs.commandString.length === 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
@ -146,9 +153,17 @@ export async function launchConnect() {
|
||||
connectionResult.success = (code === 200);
|
||||
connectionResult.logString = msg;
|
||||
|
||||
const res = await getServerVersion() as { name: string, version: string };
|
||||
connectionResult.serverInfo.name = res.name || '';
|
||||
connectionResult.serverInfo.version = res.version || '';
|
||||
if (code === 200) {
|
||||
const res = await getServerVersion() as { name: string, version: string };
|
||||
connectionResult.serverInfo.name = res.name || '';
|
||||
connectionResult.serverInfo.version = res.version || '';
|
||||
} else {
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: msg
|
||||
});
|
||||
}
|
||||
|
||||
resolve(void 0);
|
||||
}, { once: true });
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
type="primary"
|
||||
size="large"
|
||||
:disabled="!connectionResult"
|
||||
@click="doConnect()"
|
||||
@click="suitableConnect()"
|
||||
>
|
||||
{{ t('connect.appearance.connect') }}
|
||||
</el-button>
|
||||
@ -38,7 +38,7 @@ import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
import { connectionResult, doConnect, doReconnect } from './connection';
|
||||
import { connectionResult, doConnect, doReconnect, launchConnect } from './connection';
|
||||
|
||||
import ConnectionMethod from './connection-method.vue';
|
||||
import ConnectionArgs from './connection-args.vue';
|
||||
@ -46,7 +46,7 @@ import EnvVar from './env-var.vue';
|
||||
|
||||
import ConnectionLog from './connection-log.vue';
|
||||
|
||||
import { useMessageBridge } from '@/api/message-bridge';
|
||||
import { acquireVsCodeApi, useMessageBridge } from '@/api/message-bridge';
|
||||
|
||||
defineComponent({ name: 'connect' });
|
||||
|
||||
@ -58,6 +58,13 @@ bridge.addCommandListener('connect', data => {
|
||||
connectionResult.logString = msg;
|
||||
}, { once: false });
|
||||
|
||||
function suitableConnect() {
|
||||
if (acquireVsCodeApi === undefined) {
|
||||
doConnect();
|
||||
} else {
|
||||
launchConnect({ updateCommandString: false });
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
74
servers/bing-picture.py
Normal file
74
servers/bing-picture.py
Normal 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)
|
@ -5,6 +5,7 @@ description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"bs4>=0.0.2",
|
||||
"mcp[cli]>=1.5.0",
|
||||
"requests>=2.32.3",
|
||||
]
|
||||
|
36
servers/uv.lock
generated
36
servers/uv.lock
generated
@ -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 },
|
||||
]
|
||||
|
||||
[[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]]
|
||||
name = "certifi"
|
||||
version = "2025.1.31"
|
||||
@ -335,12 +360,14 @@ name = "servers"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "bs4" },
|
||||
{ name = "mcp", extra = ["cli"] },
|
||||
{ name = "requests" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "bs4", specifier = ">=0.0.2" },
|
||||
{ name = "mcp", extras = ["cli"], specifier = ">=1.5.0" },
|
||||
{ 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 },
|
||||
]
|
||||
|
||||
[[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]]
|
||||
name = "sse-starlette"
|
||||
version = "2.2.1"
|
||||
|
@ -63,21 +63,9 @@ export class MCPClient {
|
||||
this.transport = new StdioClientTransport({
|
||||
command: this.options.command || '',
|
||||
args: this.options.args || [],
|
||||
cwd: this.options.cwd || process.cwd(),
|
||||
// TODO
|
||||
stderr: 'pipe'
|
||||
cwd: this.options.cwd || process.cwd()
|
||||
});
|
||||
|
||||
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;
|
||||
case 'SSE':
|
||||
if (!this.options.url) {
|
||||
|
@ -15,6 +15,9 @@ let client: MCPClient | undefined = undefined;
|
||||
|
||||
function tryGetRunCommandError(command: string, args: string[] = [], cwd?: string): string | null {
|
||||
try {
|
||||
console.log('current command', command);
|
||||
console.log('current args', args);
|
||||
|
||||
const result = spawnSync(command, args, {
|
||||
cwd: cwd || process.cwd(),
|
||||
stdio: 'pipe',
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"currentIndex": 0,
|
||||
"currentIndex": 2,
|
||||
"tabs": [
|
||||
{
|
||||
"name": "交互测试",
|
||||
@ -43,6 +43,24 @@
|
||||
"systemPrompt": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "工具",
|
||||
"icon": "icon-tool",
|
||||
"type": "blank",
|
||||
"componentIndex": 2,
|
||||
"storage": {
|
||||
"currentToolName": "add"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "资源",
|
||||
"icon": "icon-file",
|
||||
"type": "blank",
|
||||
"componentIndex": 0,
|
||||
"storage": {
|
||||
"currentResourceName": "greeting"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user