Refactor: Rename entry point to main.ts and update start script.
This commit is contained in:
1 parent
40e71274dd
commit
e9320e438f
7 files changed
+347
-352
No files matched your search
@@ -0,0 +1,129 @@
|
||||
import { DynamicStructuredTool } from '@langchain/core/tools';
|
||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
||||
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
||||
import { Tool as McpTool, TextContent } from '@modelcontextprotocol/sdk/types';
|
||||
import { z } from 'zod';
|
||||
|
||||
export class McpToolsService {
|
||||
private mcpClient: Client | null = null;
|
||||
private transport: SSEClientTransport | null = null;
|
||||
private readonly serverUrl: string;
|
||||
|
||||
constructor(serverUrl: string) {
|
||||
this.serverUrl = serverUrl;
|
||||
}
|
||||
|
||||
async connect(): Promise<void> {
|
||||
if (this.mcpClient && this.transport) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.transport = new SSEClientTransport(new URL(this.serverUrl));
|
||||
this.mcpClient = new Client({
|
||||
name: 'my-langchain-mcp-client',
|
||||
version: '1.0.0',
|
||||
});
|
||||
await this.mcpClient.connect(this.transport);
|
||||
console.log(`[MCP Service] Подключено к MCP серверу: ${this.serverUrl}`);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`[MCP Service] Ошибка подключения к MCP серверу ${this.serverUrl}:`,
|
||||
error,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async disconnect(): Promise<void> {
|
||||
if (this.mcpClient) {
|
||||
await this.mcpClient.close();
|
||||
this.mcpClient = null;
|
||||
this.transport = null;
|
||||
console.log(`[MCP Service] Отключено от MCP сервера: ${this.serverUrl}`);
|
||||
}
|
||||
}
|
||||
|
||||
async getLangchainTools(): Promise<DynamicStructuredTool[]> {
|
||||
if (!this.mcpClient) {
|
||||
throw new Error('MCP клиент не подключен. Сначала вызовите .connect()');
|
||||
}
|
||||
|
||||
const mcpTools: McpTool[] = (await this.mcpClient.listTools()).tools;
|
||||
const langchainTools: DynamicStructuredTool[] = [];
|
||||
|
||||
for (const mcpTool of mcpTools) {
|
||||
const properties: Record<string, z.ZodTypeAny> = {};
|
||||
for (const key in mcpTool.inputSchema.properties) {
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
const prop = mcpTool.inputSchema.properties[key] as any;
|
||||
let schemaType: z.ZodTypeAny;
|
||||
switch (prop.type) {
|
||||
case 'string':
|
||||
schemaType = z.string();
|
||||
break;
|
||||
case 'number':
|
||||
schemaType = z.number();
|
||||
break;
|
||||
case 'boolean':
|
||||
schemaType = z.boolean();
|
||||
break;
|
||||
case 'array':
|
||||
schemaType = z.array(z.any());
|
||||
break;
|
||||
case 'object':
|
||||
schemaType = z.object({});
|
||||
break;
|
||||
default:
|
||||
schemaType = z.any();
|
||||
}
|
||||
if (!(mcpTool.inputSchema.required || []).includes(key)) {
|
||||
schemaType = schemaType.optional();
|
||||
}
|
||||
properties[key] = schemaType.describe(prop.description || '');
|
||||
}
|
||||
const zodSchema = z.object(properties);
|
||||
|
||||
const langchainTool = new DynamicStructuredTool({
|
||||
name: mcpTool.name,
|
||||
description: mcpTool.description || '',
|
||||
schema: zodSchema,
|
||||
func: async (args: Record<string, unknown>) => {
|
||||
console.log(
|
||||
`[MCP Tool Call] Вызов инструмента MCP: ${mcpTool.name} с аргументами:`,
|
||||
args,
|
||||
);
|
||||
const result = await this.mcpClient!.callTool({
|
||||
name: mcpTool.name,
|
||||
arguments: args,
|
||||
});
|
||||
|
||||
if (result.isError) {
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
const errorContent = (result.content as any[])
|
||||
.map((c) => (c as TextContent).text || '')
|
||||
.join('\n');
|
||||
throw new Error(
|
||||
`Ошибка выполнения инструмента ${mcpTool.name}: ${errorContent}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (result.structuredContent) {
|
||||
return result.structuredContent;
|
||||
}
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
return (result.content as any[])
|
||||
.map((c) => {
|
||||
if (c.type === 'text') return (c as TextContent).text;
|
||||
if (c.type === 'image') return `[Изображение: ${c.mimeType}]`;
|
||||
if (c.type === 'audio') return `[Аудио: ${c.mimeType}]`;
|
||||
if (c.type === 'resource') return `[Ресурс: ${c.uri}]`;
|
||||
return JSON.stringify(c);
|
||||
})
|
||||
.join('\n');
|
||||
},
|
||||
});
|
||||
langchainTools.push(langchainTool);
|
||||
}
|
||||
return langchainTools;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user