update
This commit is contained in:
parent
f9ecdf233b
commit
2c643107ee
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@ dist
|
|||||||
node_modules
|
node_modules
|
||||||
.vscode-test/
|
.vscode-test/
|
||||||
*.vsix
|
*.vsix
|
||||||
|
.env
|
@ -1,27 +1,57 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="tools-module">
|
<div class="tool-module">
|
||||||
<h2>工具模块</h2>
|
<div class="left">
|
||||||
<!-- 工具模块内容将在这里实现 -->
|
<h2>
|
||||||
|
<span class="iconfont icon-tool"></span>
|
||||||
|
工具模块
|
||||||
|
</h2>
|
||||||
|
<h3><code>tools/list</code></h3>
|
||||||
|
|
||||||
|
<ToolList
|
||||||
|
:tab-id="props.tabId"
|
||||||
|
></ToolList>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<ToolExecutor
|
||||||
|
:tab-id="props.tabId"
|
||||||
|
></ToolExecutor>
|
||||||
|
|
||||||
|
<ToolLogger
|
||||||
|
:tab-id="props.tabId"
|
||||||
|
></ToolLogger>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent, defineProps } from 'vue';
|
import { defineProps } from 'vue';
|
||||||
|
import ToolList from './tool-list.vue';
|
||||||
defineComponent({ name: 'tool' });
|
import ToolExecutor from './tool-executor.vue';
|
||||||
|
import ToolLogger from './tool-logger.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
storage: {
|
tabId: {
|
||||||
type: Object,
|
type: Number,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.tools-module {
|
.tool-module {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-module .left {
|
||||||
|
width: 45%;
|
||||||
|
max-width: 410px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-module .right {
|
||||||
|
width: 45%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
147
app/src/components/main-panel/tool/tool-executor.vue
Normal file
147
app/src/components/main-panel/tool/tool-executor.vue
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h3>{{ currentTool?.name }}</h3>
|
||||||
|
</div>
|
||||||
|
<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]"
|
||||||
|
:placeholder="t('enter') + ' ' + (property.title || name)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<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)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-switch
|
||||||
|
v-else-if="property.type === 'boolean'"
|
||||||
|
v-model="formData[name]"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-scrollbar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" :loading="loading" @click="handleExecute">
|
||||||
|
{{ t('execute-tool') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="resetForm">
|
||||||
|
{{ t('reset') }}
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent, defineProps, watch, ref, computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import type { FormInstance, FormRules } from 'element-plus';
|
||||||
|
import { tabs } from '../panel';
|
||||||
|
import { toolsManager, ToolStorage } from './tools';
|
||||||
|
import { CasualRestAPI, ToolCallResponse } from '@/hook/type';
|
||||||
|
import { useMessageBridge } from '@/api/message-bridge';
|
||||||
|
|
||||||
|
defineComponent({ name: 'tool-executor' });
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
tabId: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const tab = tabs.content[props.tabId];
|
||||||
|
const tabStorage = tab.storage as ToolStorage;
|
||||||
|
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
const formData = ref<Record<string, any>>({});
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const currentTool = computed(() => {
|
||||||
|
return toolsManager.tools.find(tool => tool.name === tabStorage.currentToolName);
|
||||||
|
});
|
||||||
|
|
||||||
|
const formRules = computed<FormRules>(() => {
|
||||||
|
const rules: FormRules = {};
|
||||||
|
if (!currentTool.value?.inputSchema?.properties) return rules;
|
||||||
|
|
||||||
|
Object.entries(currentTool.value.inputSchema.properties).forEach(([name, property]) => {
|
||||||
|
if (currentTool.value?.inputSchema?.required?.includes(name)) {
|
||||||
|
rules[name] = [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: `${property.title || name} 是必填字段`,
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return rules;
|
||||||
|
});
|
||||||
|
|
||||||
|
const initFormData = () => {
|
||||||
|
formData.value = {};
|
||||||
|
if (!currentTool.value?.inputSchema?.properties) return;
|
||||||
|
|
||||||
|
Object.entries(currentTool.value.inputSchema.properties).forEach(([name, property]) => {
|
||||||
|
formData.value[name] = (property.type === 'number' || property.type === 'integer') ? 0 :
|
||||||
|
property.type === 'boolean' ? false : '';
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
formRef.value?.resetFields();
|
||||||
|
tabStorage.lastToolCallResponse = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleExecute() {
|
||||||
|
if (!currentTool.value) return;
|
||||||
|
|
||||||
|
const bridge = useMessageBridge();
|
||||||
|
|
||||||
|
bridge.addCommandListener('tools/call', (data: CasualRestAPI<ToolCallResponse>) => {
|
||||||
|
console.log(data.msg);
|
||||||
|
|
||||||
|
tabStorage.lastToolCallResponse = data.msg;
|
||||||
|
}, { once: true });
|
||||||
|
|
||||||
|
bridge.postMessage({
|
||||||
|
command: 'tools/call',
|
||||||
|
data: {
|
||||||
|
toolName: tabStorage.currentToolName,
|
||||||
|
toolArgs: formData.value
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => tabStorage.currentToolName, () => {
|
||||||
|
initFormData();
|
||||||
|
resetForm();
|
||||||
|
}, { immediate: true });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.tool-executor-container {
|
||||||
|
background-color: var(--background);
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: .5em;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
</style>
|
154
app/src/components/main-panel/tool/tool-list.vue
Normal file
154
app/src/components/main-panel/tool/tool-list.vue
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
<template>
|
||||||
|
<div class="tool-list-container-scrollbar">
|
||||||
|
<el-scrollbar height="500px">
|
||||||
|
<div class="tool-list-container">
|
||||||
|
<div
|
||||||
|
class="item"
|
||||||
|
:class="{ 'active': tabStorage.currentToolName === tool.name }"
|
||||||
|
v-for="tool of toolsManager.tools"
|
||||||
|
:key="tool.name"
|
||||||
|
@click="handleClick(tool)"
|
||||||
|
>
|
||||||
|
<span>{{ tool.name }}</span>
|
||||||
|
<span>{{ tool.description || '' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
<div class="tool-list-function-container">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="reloadTools({ first: false })"
|
||||||
|
>
|
||||||
|
{{ t('refresh') }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useMessageBridge } from '@/api/message-bridge';
|
||||||
|
import { CasualRestAPI, ToolsListResponse } from '@/hook/type';
|
||||||
|
import { onMounted, onUnmounted, defineProps } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { toolsManager, ToolStorage } from './tools';
|
||||||
|
import { tabs } from '../panel';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
|
const bridge = useMessageBridge();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
tabId: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const tab = tabs.content[props.tabId];
|
||||||
|
const tabStorage = tab.storage as ToolStorage;
|
||||||
|
|
||||||
|
function reloadTools(option: { first: boolean }) {
|
||||||
|
bridge.postMessage({
|
||||||
|
command: 'tools/list'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!option.first) {
|
||||||
|
ElMessage({
|
||||||
|
message: t('finish-refresh'),
|
||||||
|
type: 'success',
|
||||||
|
duration: 3000,
|
||||||
|
showClose: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClick(tool: { name: string }) {
|
||||||
|
tabStorage.currentToolName = tool.name;
|
||||||
|
tabStorage.lastToolCallResponse = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let commandCancel: (() => void);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
commandCancel = bridge.addCommandListener('tools/list', (data: CasualRestAPI<ToolsListResponse>) => {
|
||||||
|
toolsManager.tools = data.msg.tools || [];
|
||||||
|
|
||||||
|
if (toolsManager.tools.length > 0) {
|
||||||
|
tabStorage.currentToolName = toolsManager.tools[0].name;
|
||||||
|
tabStorage.lastToolCallResponse = undefined;
|
||||||
|
}
|
||||||
|
}, { once: false });
|
||||||
|
|
||||||
|
reloadTools({ first: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (commandCancel){
|
||||||
|
commandCancel();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.tool-list-container-scrollbar {
|
||||||
|
background-color: var(--background);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-list-container {
|
||||||
|
height: fit-content;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-list-function-container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-list-function-container button {
|
||||||
|
width: 175px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-list-container > .item {
|
||||||
|
margin: 3px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: .3em;
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-list-container > .item:hover {
|
||||||
|
background-color: var(--main-light-color);
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-list-container > .item.active {
|
||||||
|
background-color: var(--main-light-color);
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-list-container > .item > span:first-child {
|
||||||
|
max-width: 200px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-list-container > .item > span:last-child {
|
||||||
|
opacity: 0.6;
|
||||||
|
font-size: 12.5px;
|
||||||
|
max-width: 200px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
</style>
|
109
app/src/components/main-panel/tool/tool-logger.vue
Normal file
109
app/src/components/main-panel/tool/tool-logger.vue
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
<template>
|
||||||
|
<div class="tool-logger">
|
||||||
|
<span>
|
||||||
|
<span>{{ t('response') }}</span>
|
||||||
|
<span style="width: 200px;">
|
||||||
|
<el-switch
|
||||||
|
v-model="showRawJson"
|
||||||
|
inline-prompt
|
||||||
|
active-text="JSON"
|
||||||
|
inactive-text="Text"
|
||||||
|
style="margin-left: 10px; width: 200px;"
|
||||||
|
:inactive-action-style="'backgroundColor: var(--sidebar)'"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<el-scrollbar height="350px">
|
||||||
|
<div
|
||||||
|
class="output-content"
|
||||||
|
contenteditable="false"
|
||||||
|
>
|
||||||
|
<template v-if="!showRawJson">
|
||||||
|
<template v-if="tabStorage.lastToolCallResponse?.isError">
|
||||||
|
<span style="color: var(--el-color-error)">
|
||||||
|
{{ tabStorage.lastToolCallResponse.content.map(c => c.text).join('\n') }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ tabStorage.lastToolCallResponse?.content.map(c => c.text).join('\n') }}
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ formattedJson }}
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent, defineProps, computed, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { tabs } from '../panel';
|
||||||
|
import { ToolStorage } from './tools';
|
||||||
|
|
||||||
|
defineComponent({ name: 'tool-logger' });
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
tabId: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const tab = tabs.content[props.tabId];
|
||||||
|
const tabStorage = tab.storage as ToolStorage;
|
||||||
|
|
||||||
|
const showRawJson = ref(false);
|
||||||
|
|
||||||
|
const formattedJson = computed(() => {
|
||||||
|
try {
|
||||||
|
return JSON.stringify(tabStorage.lastToolCallResponse, null, 2);
|
||||||
|
} catch {
|
||||||
|
return 'Invalid JSON';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.tool-logger {
|
||||||
|
border-radius: .5em;
|
||||||
|
background-color: var(--background);
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-logger .el-switch__core {
|
||||||
|
border: 1px solid var(--main-color) !important;
|
||||||
|
width: 60px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-logger .el-switch .el-switch__action {
|
||||||
|
background-color: var(--main-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-logger .el-switch.is-checked .el-switch__action {
|
||||||
|
background-color: var(--sidebar);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-logger > span:first-child {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-logger .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.5;
|
||||||
|
background-color: var(--sidebar);
|
||||||
|
}
|
||||||
|
</style>
|
13
app/src/components/main-panel/tool/tools.ts
Normal file
13
app/src/components/main-panel/tool/tools.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { ToolsListResponse, ToolCallResponse } from '@/hook/type';
|
||||||
|
import { reactive } from 'vue';
|
||||||
|
|
||||||
|
export const toolsManager = reactive<{
|
||||||
|
tools: ToolsListResponse['tools']
|
||||||
|
}>({
|
||||||
|
tools: []
|
||||||
|
});
|
||||||
|
|
||||||
|
export interface ToolStorage {
|
||||||
|
currentToolName: string;
|
||||||
|
lastToolCallResponse?: ToolCallResponse;
|
||||||
|
}
|
@ -76,6 +76,16 @@ export interface PromptsGetResponse {
|
|||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ToolCallContent {
|
||||||
|
type: string;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ToolCallResponse {
|
||||||
|
content: ToolCallContent[];
|
||||||
|
isError: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== 请求接口定义 ====================
|
// ==================== 请求接口定义 ====================
|
||||||
export interface BaseRequest {
|
export interface BaseRequest {
|
||||||
method: string;
|
method: string;
|
||||||
@ -97,6 +107,17 @@ export interface PromptsGetRequest extends BaseRequest {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ToolCallRequest extends BaseRequest {
|
||||||
|
method: 'tools/call';
|
||||||
|
params: {
|
||||||
|
name: string;
|
||||||
|
arguments: Record<string, any>;
|
||||||
|
_meta?: {
|
||||||
|
progressToken?: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== 合并类型定义 ====================
|
// ==================== 合并类型定义 ====================
|
||||||
export type APIResponse =
|
export type APIResponse =
|
||||||
| ToolsListResponse
|
| ToolsListResponse
|
||||||
@ -104,9 +125,11 @@ export type APIResponse =
|
|||||||
| ResourceTemplatesListResponse
|
| ResourceTemplatesListResponse
|
||||||
| ResourcesListResponse
|
| ResourcesListResponse
|
||||||
| ResourcesReadResponse
|
| ResourcesReadResponse
|
||||||
| PromptsGetResponse;
|
| PromptsGetResponse
|
||||||
|
| ToolCallResponse;
|
||||||
|
|
||||||
export type APIRequest =
|
export type APIRequest =
|
||||||
| BaseRequest
|
| BaseRequest
|
||||||
| ResourcesReadRequest
|
| ResourcesReadRequest
|
||||||
| PromptsGetRequest;
|
| PromptsGetRequest
|
||||||
|
| ToolCallRequest;
|
@ -112,5 +112,6 @@
|
|||||||
"refresh": "تحديث",
|
"refresh": "تحديث",
|
||||||
"finish-refresh": "تم التحديث",
|
"finish-refresh": "تم التحديث",
|
||||||
"response": "الاستجابة",
|
"response": "الاستجابة",
|
||||||
"read-prompt": "استخراج الكلمات الرئيسية"
|
"read-prompt": "استخراج الكلمات الرئيسية",
|
||||||
|
"execute-tool": "تشغيل الأداة"
|
||||||
}
|
}
|
@ -112,5 +112,6 @@
|
|||||||
"refresh": "Aktualisieren",
|
"refresh": "Aktualisieren",
|
||||||
"finish-refresh": "Aktualisierung abgeschlossen",
|
"finish-refresh": "Aktualisierung abgeschlossen",
|
||||||
"response": "Antwort",
|
"response": "Antwort",
|
||||||
"read-prompt": "Stichwörter extrahieren"
|
"read-prompt": "Stichwörter extrahieren",
|
||||||
|
"execute-tool": "Werkzeug ausführen"
|
||||||
}
|
}
|
@ -112,5 +112,6 @@
|
|||||||
"refresh": "Refresh",
|
"refresh": "Refresh",
|
||||||
"finish-refresh": "Refresh completed",
|
"finish-refresh": "Refresh completed",
|
||||||
"response": "Response",
|
"response": "Response",
|
||||||
"read-prompt": "Extract keywords"
|
"read-prompt": "Extract keywords",
|
||||||
|
"execute-tool": "Run tool"
|
||||||
}
|
}
|
@ -112,5 +112,6 @@
|
|||||||
"refresh": "Rafraîchir",
|
"refresh": "Rafraîchir",
|
||||||
"finish-refresh": "Actualisation terminée",
|
"finish-refresh": "Actualisation terminée",
|
||||||
"response": "Réponse",
|
"response": "Réponse",
|
||||||
"read-prompt": "Extraire des mots-clés"
|
"read-prompt": "Extraire des mots-clés",
|
||||||
|
"execute-tool": "Exécuter l'outil"
|
||||||
}
|
}
|
@ -112,5 +112,6 @@
|
|||||||
"refresh": "更新",
|
"refresh": "更新",
|
||||||
"finish-refresh": "更新が完了しました",
|
"finish-refresh": "更新が完了しました",
|
||||||
"response": "応答",
|
"response": "応答",
|
||||||
"read-prompt": "キーワードを抽出"
|
"read-prompt": "キーワードを抽出",
|
||||||
|
"execute-tool": "ツールを実行"
|
||||||
}
|
}
|
@ -112,5 +112,6 @@
|
|||||||
"refresh": "새로 고침",
|
"refresh": "새로 고침",
|
||||||
"finish-refresh": "새로 고침 완료",
|
"finish-refresh": "새로 고침 완료",
|
||||||
"response": "응답",
|
"response": "응답",
|
||||||
"read-prompt": "키워드 추출"
|
"read-prompt": "키워드 추출",
|
||||||
|
"execute-tool": "도구 실행"
|
||||||
}
|
}
|
@ -112,5 +112,6 @@
|
|||||||
"refresh": "Обновить",
|
"refresh": "Обновить",
|
||||||
"finish-refresh": "Обновление завершено",
|
"finish-refresh": "Обновление завершено",
|
||||||
"response": "Ответ",
|
"response": "Ответ",
|
||||||
"read-prompt": "Извлечь ключевые слова"
|
"read-prompt": "Извлечь ключевые слова",
|
||||||
|
"execute-tool": "Запустить инструмент"
|
||||||
}
|
}
|
@ -112,5 +112,6 @@
|
|||||||
"refresh": "刷新",
|
"refresh": "刷新",
|
||||||
"finish-refresh": "刷新完成",
|
"finish-refresh": "刷新完成",
|
||||||
"response": "响应",
|
"response": "响应",
|
||||||
"read-prompt": "提取提词"
|
"read-prompt": "提取提词",
|
||||||
|
"execute-tool": "运行工具"
|
||||||
}
|
}
|
@ -112,5 +112,6 @@
|
|||||||
"refresh": "重新整理",
|
"refresh": "重新整理",
|
||||||
"finish-refresh": "刷新完成",
|
"finish-refresh": "刷新完成",
|
||||||
"response": "響應",
|
"response": "響應",
|
||||||
"read-prompt": "提取關鍵詞"
|
"read-prompt": "提取關鍵詞",
|
||||||
|
"execute-tool": "執行工具"
|
||||||
}
|
}
|
1
test/.gitignore
vendored
1
test/.gitignore
vendored
@ -21,3 +21,4 @@ pnpm-debug.log*
|
|||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
config.json
|
@ -105,6 +105,11 @@ export class MCPClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 列出所有工具
|
||||||
|
public async listTools() {
|
||||||
|
return await this.client.listTools();
|
||||||
|
}
|
||||||
|
|
||||||
// 调用工具
|
// 调用工具
|
||||||
public async callTool(options: { name: string; arguments: Record<string, any> }) {
|
public async callTool(options: { name: string; arguments: Record<string, any> }) {
|
||||||
return await this.client.callTool(options);
|
return await this.client.callTool(options);
|
||||||
|
@ -182,6 +182,41 @@ export async function readResource(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取工具列表
|
||||||
|
*/
|
||||||
|
export async function listTools(
|
||||||
|
client: MCPClient | undefined,
|
||||||
|
webview: VSCodeWebViewLike
|
||||||
|
) {
|
||||||
|
if (!client) {
|
||||||
|
const connectResult = {
|
||||||
|
code: 501,
|
||||||
|
msg: 'mcp client 尚未连接'
|
||||||
|
};
|
||||||
|
webview.postMessage({ command: 'tools/list', data: connectResult });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const tools = await client.listTools();
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
code: 200,
|
||||||
|
msg: tools
|
||||||
|
};
|
||||||
|
|
||||||
|
webview.postMessage({ command: 'tools/list', data: result });
|
||||||
|
} catch (error) {
|
||||||
|
const result = {
|
||||||
|
code: 500,
|
||||||
|
msg: (error as any).toString()
|
||||||
|
};
|
||||||
|
webview.postMessage({ command: 'tools/list', data: result });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 调用工具
|
* @description 调用工具
|
||||||
*/
|
*/
|
||||||
@ -204,6 +239,7 @@ export async function callTool(
|
|||||||
name: option.toolName,
|
name: option.toolName,
|
||||||
arguments: option.toolArgs
|
arguments: option.toolArgs
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
code: 200,
|
code: 200,
|
||||||
msg: toolResult
|
msg: toolResult
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import { VSCodeWebViewLike } from '../adapter';
|
import { VSCodeWebViewLike } from '../adapter';
|
||||||
import { connect, MCPClient, type MCPOptions } from './connect';
|
import { connect, MCPClient, type MCPOptions } from './connect';
|
||||||
import { callTool, getPrompt, listPrompts, listResources, listResourceTemplates, readResource } from './handler';
|
import { callTool, getPrompt, listPrompts, listResources, listResourceTemplates, listTools, readResource } from './handler';
|
||||||
import { ping } from './util';
|
import { ping } from './util';
|
||||||
|
|
||||||
|
|
||||||
@ -57,6 +57,10 @@ export function messageController(command: string, data: any, webview: VSCodeWeb
|
|||||||
readResource(client, data, webview);
|
readResource(client, data, webview);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'tools/list':
|
||||||
|
listTools(client, webview);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'tools/call':
|
case 'tools/call':
|
||||||
callTool(client, data, webview);
|
callTool(client, data, webview);
|
||||||
break;
|
break;
|
||||||
|
@ -67,6 +67,16 @@ export interface PromptsGetResponse {
|
|||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ToolListItem {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
inputSchema: InputSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ToolsListResponse {
|
||||||
|
tools: ToolListItem[];
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== 请求接口定义 ====================
|
// ==================== 请求接口定义 ====================
|
||||||
export interface BaseRequest {
|
export interface BaseRequest {
|
||||||
method: string;
|
method: string;
|
||||||
|
12
test/src/util.ts
Normal file
12
test/src/util.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
function getConfigurationPath() {
|
||||||
|
// 如果是 vscode 插件下,则修改为 ~/.openmcp/config.json
|
||||||
|
return 'config.json';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadConfig() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveConfig() {
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user