Harshith Mullapudi 54e535d57d
Feat: v2 (#12)
* Feat: v2

* feat: add chat functionality

* First cut: integrations

* Feat: add conversation API

* Enhance conversation handling and memory management

* Feat: added conversation

---------

Co-authored-by: Manoj K <saimanoj58@gmail.com>
2025-07-08 22:41:00 +05:30

152 lines
3.8 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-explicit-any */
import { logger } from "@trigger.dev/sdk/v3";
import { jsonSchema, tool, type ToolSet } from "ai";
import { type MCPTool } from "./types";
export class MCP {
private Client: any;
private clients: Record<string, any> = {};
private StdioTransport: any;
constructor() {}
public async init() {
this.Client = await MCP.importClient();
this.StdioTransport = await MCP.importStdioTransport();
}
private static async importClient() {
const { Client } = await import(
"@modelcontextprotocol/sdk/client/index.js"
);
return Client;
}
async load(agents: string[], mcpConfig: any) {
await Promise.all(
agents.map(async (agent) => {
const mcp = mcpConfig.mcpServers[agent];
return await this.connectToServer(agent, mcp.command, mcp.args, {
...mcp.env,
DATABASE_URL: mcp.env?.DATABASE_URL ?? "",
});
}),
);
}
private static async importStdioTransport() {
const { StdioClientTransport } = await import("./stdio");
return StdioClientTransport;
}
async allTools(): Promise<ToolSet> {
const clientEntries = Object.entries(this.clients);
// Fetch all tools in parallel
const toolsArrays = await Promise.all(
clientEntries.map(async ([clientKey, client]) => {
try {
const { tools } = await client.listTools();
return tools.map(({ name, description, inputSchema }: any) => [
`${clientKey}--${name}`,
tool({
description,
parameters: jsonSchema(inputSchema),
}),
]);
} catch (error) {
logger.error(`Error fetching tools for ${clientKey}:`, { error });
return [];
}
}),
);
// Flatten and convert to object
return Object.fromEntries(toolsArrays.flat());
}
async tools(): Promise<MCPTool[]> {
const allTools: MCPTool[] = [];
for (const clientKey in this.clients) {
const client = this.clients[clientKey];
const { tools: clientTools } = await client.listTools();
for (const tool of clientTools) {
// Add client prefix to tool name
tool.name = `${clientKey}--${tool.name}`;
allTools.push(tool);
}
}
return allTools;
}
async getTool(name: string) {
try {
const clientKey = name.split("--")[0];
const toolName = name.split("--")[1];
const client = this.clients[clientKey];
const { tools: clientTools } = await client.listTools();
const clientTool = clientTools.find((to: any) => to.name === toolName);
return JSON.stringify(clientTool);
} catch (e) {
logger.error((e as string) ?? "Getting tool failed");
throw new Error("Getting tool failed");
}
}
async callTool(name: string, parameters: any) {
const clientKey = name.split("--")[0];
const toolName = name.split("--")[1];
const client = this.clients[clientKey];
const response = await client.callTool({
name: toolName,
arguments: parameters,
});
return response;
}
async connectToServer(
name: string,
command: string,
args: string[],
env: any,
) {
try {
const client = new this.Client(
{
name,
version: "1.0.0",
},
{
capabilities: {},
},
);
// Conf
// igure the transport for MCP server
const transport = new this.StdioTransport({
command,
args,
env,
});
// Connect to the MCP server
await client.connect(transport, { timeout: 60 * 1000 * 5 });
this.clients[name] = client;
logger.info(`Connected to ${name} MCP server`);
} catch (e) {
logger.error(`Failed to connect to ${name} MCP server: `, { e });
throw e;
}
}
}