@voyager-0x/agent-mcp
Version:
Voyager MCP Agent - A powerful Model Context Protocol agent
202 lines (201 loc) • 7.19 kB
JavaScript
import { v4 as uuidv4 } from "uuid";
import { log } from "./log";
import { createWebMcpModule, createMcpServerModule, } from "./mcp";
export class McpModulesManager {
constructor() {
this.modules = [];
}
createMcpDslModule({ title, description }, callback) {
const client = createWebMcpModule({
title,
description,
}, callback);
this.modules.push({
title,
description,
client,
uuid: uuidv4(),
source: "user-dsl",
});
return this.modules[this.modules.length - 1];
}
createMcpBuiltinModule({ title, description }, callback) {
const client = createWebMcpModule({
title,
description,
}, callback);
this.modules.push({
title,
description,
client,
uuid: uuidv4(),
source: "builtin",
});
return this.modules[this.modules.length - 1];
}
async createMcpServerModule(config, uuid) {
const client = await createMcpServerModule(config);
const moduleUuid = uuid || uuidv4();
// 根据 serverUrl 判断连接类型
const source = config.serverUrl.startsWith("http://") ||
config.serverUrl.startsWith("https://") ||
config.serverUrl.startsWith("ws://") ||
config.serverUrl.startsWith("wss://")
? "external-http"
: "external-stdio";
this.modules.push({
title: config.title,
description: config.description,
client: client,
uuid: moduleUuid,
source: source,
config: config,
});
return this.modules[this.modules.length - 1];
}
async removeMcpServerModule({ uuid, }) {
try {
const moduleIndex = this.modules.findIndex((module) => module.uuid === uuid);
if (moduleIndex === -1) {
log.warn(`MCP Server Module with uuid ${uuid} not found`);
return false;
}
const module = this.modules[moduleIndex];
// 尝试断开连接
try {
if (module.client && typeof module.client.close === "function") {
await module.client.close();
}
}
catch (error) {
log.warn(`Failed to close client for MCP server module ${uuid}:`, error);
}
// 从数组中移除模块
this.modules.splice(moduleIndex, 1);
log.info(`Successfully removed MCP server module: ${module.title} (${uuid})`);
return true;
}
catch (error) {
log.error(`Failed to remove MCP server module ${uuid}:`, error);
return false;
}
}
async updateMcpServerModule({ uuid, config, }) {
try {
const moduleIndex = this.modules.findIndex((module) => module.uuid === uuid);
if (moduleIndex === -1) {
log.warn(`MCP Server Module with uuid ${uuid} not found`);
return null;
}
const oldModule = this.modules[moduleIndex];
// 关闭旧连接
try {
if (oldModule.client && typeof oldModule.client.close === "function") {
await oldModule.client.close();
}
}
catch (error) {
log.warn(`Failed to close old client for MCP server module ${uuid}:`, error);
}
// 创建新的客户端连接
const newClient = await createMcpServerModule(config);
// 根据 serverUrl 判断连接类型
const source = config.serverUrl.startsWith("http://") ||
config.serverUrl.startsWith("https://") ||
config.serverUrl.startsWith("ws://") ||
config.serverUrl.startsWith("wss://")
? "external-http"
: "external-stdio";
// 更新模块
this.modules[moduleIndex] = {
title: config.title,
description: config.description,
client: newClient,
uuid: uuid, // 保持原有的UUID
source: source,
config: config,
};
log.info(`Successfully updated MCP server module: ${config.title} (${uuid})`);
return this.modules[moduleIndex];
}
catch (error) {
log.error(`Failed to update MCP server module ${uuid}:`, error);
return null;
}
}
listMcpModules() {
return this.modules;
}
getModuleUuidsBySource(source) {
return this.modules
.filter((module) => module.source === source)
.map((module) => module.uuid);
}
getMcpModule({ uuid }) {
const mod = this.modules.find((mcpModule) => mcpModule.uuid === uuid);
if (!mod) {
throw new Error(`McpModule with uuid ${uuid} not found`);
}
return mod;
}
async getTools({ uuids }) {
const _tools = [];
for (const uuid of uuids) {
const { client } = this.getMcpModule({
uuid,
});
try {
const { tools } = await client.listTools();
tools.forEach((tool) => {
_tools.push({
moduleId: uuid,
...tool,
});
});
}
catch (error) {
// 如果是方法未找到错误(-32601),继续处理其他模块
if (error.code === -32601) {
log.warn(`DSL Module ${uuid} does not support listTools method`);
}
else {
// 其他错误重新抛出
throw error;
}
}
}
return _tools;
}
async getResources({ uuids }) {
const _resources = [];
// 处理 DSL 模块
for (const uuid of uuids) {
if (uuid) {
const { client } = this.getMcpModule({
uuid: uuid,
});
try {
const { resources } = await client.listResources();
resources.forEach((resource) => {
_resources.push({
moduleId: uuid,
...resource,
});
});
}
catch (error) {
// 如果是方法未找到错误(-32601),继续处理其他模块
if (error.code === -32601) {
log.warn(`DSL Module ${uuid} does not support listResources method`);
}
else {
// 其他错误重新抛出
throw error;
}
}
}
}
return _resources;
}
}
export const mcpModulesManager = new McpModulesManager();