Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AI SDK tool wrapper for MCP #3891

Open
wong2 opened this issue Nov 26, 2024 · 4 comments
Open

AI SDK tool wrapper for MCP #3891

wong2 opened this issue Nov 26, 2024 · 4 comments
Labels
ai/core enhancement New feature or request

Comments

@wong2
Copy link
Contributor

wong2 commented Nov 26, 2024

Feature Description

Today, Anthropic released the Model Context Protocol (MCP) https://modelcontextprotocol.io

Naturally, I think we should be able to turn them into a tool in the AI SDK for easier integration.

@wong2 wong2 added the enhancement New feature or request label Nov 26, 2024
@grabbou
Copy link

grabbou commented Nov 28, 2024

I am currently working on exposing my tool to MCP and I am using AI SDK tools. After I am done, I'll cross-link the snippet here. From what it appears, it should not be too hard!

@grabbou
Copy link

grabbou commented Dec 2, 2024

Here it is: https://github.com/callstackincubator/cali/blob/v0.3.1/packages/mcp-server/src/index.ts

Overall, very easy. Some edge cases yet, but it is looking good. I think I may eventually rewrite all my tools to MCP standard and write "toAISDKTool" helper instead.

@rockywangxiaolei
Copy link

rockywangxiaolei commented Dec 2, 2024

Here it is: https://github.com/callstackincubator/cali/blob/v0.3.1/packages/mcp-server/src/index.ts

Overall, very easy. Some edge cases yet, but it is looking good. I think I may eventually rewrite all my tools to MCP standard and write "toAISDKTool" helper instead.


@grabbou cool, i went through your code and i saw you import all pre-defined tools in MCP-Server/index.ts,and revise them to a MCP-server request and response which follow the protocol.
but just curious, i didnt find any MCP-Client to call this MCP-server, is it not yet finished?

@rgarcia
Copy link

rgarcia commented Dec 24, 2024

Here's some code that takes in a set of MCP servers and spits out a "tool set" that you can feed into the AI SDK:

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { jsonSchema, type CoreTool } from "ai";

type ToolSetConfig = {
  mcpServers: {
    [key: string]: {
      command: string;
      args: string[];
      env?: Record<string, string>;
    };
  };
  // onCallTool is a function that will be called when a tool is called. It will be passed the server name, tool name, arguments, and a function that will resolve to the result of the tool call. Useful for timing the tool calls or doing other instrumentation like logging.
  onCallTool?: (
    serverName: string,
    toolName: string,
    args: any,
    result: Promise<string>
  ) => void;
};

type ToolSet = {
  tools: {
    [key: string]: CoreTool;
  };
  clients: {
    [key: string]: Client;
  };
};

export async function createToolSet(config: ToolSetConfig): Promise<ToolSet> {
  let toolset: ToolSet = {
    tools: {},
    clients: {},
  };

  // could probably speed this up by spinning these up in parallel
  for (const [serverName, serverConfig] of Object.entries(config.mcpServers)) {
    const transport = new StdioClientTransport({
      ...serverConfig,
      stderr: process.stderr,
    });

    const client = new Client(
      {
        name: `${serverName}-client`,
        version: "1.0.0",
      },
      {
        capabilities: {},
      }
    );
    toolset.clients[serverName] = client;
    await client.connect(transport);

    // Get list of tools and add them to the toolset
    const toolList = await client.listTools();
    for (const tool of toolList.tools) {
      let toolName = tool.name;
      if (toolName !== serverName) {
        toolName = `${serverName}_${toolName}`;
      }
      toolset.tools[toolName] = {
        description: tool.description || "",
        parameters: jsonSchema(tool.inputSchema),
        execute: async (args) => {
          const resultPromise = (async () => {
            const result = await client.callTool({
              name: tool.name,
              arguments: args,
            });
            return JSON.stringify(result);
          })();
          if (config.onCallTool) {
            config.onCallTool(serverName, toolName, args, resultPromise);
          }
          return resultPromise;
        },
      };
    }
  }

  return toolset;
}

Usage:

const toolSet = await createToolSet({
  mcpServers: {
    fetch: {
      command: "uvx",
      args: ["mcp-server-fetch@latest"],
    },
    puppeteer: {
      command: "npx",
      args: ["-y", "@modelcontextprotocol/server-puppeteer"],
    },
  },
});

const result =  await generateText({
    model: anthropic("claude-3-5-sonnet-20241022"),
    tools: toolSet.tools,
    prompt: "What's my IP address?",
  })

for (const client of Object.values(toolSet.clients)) {
  await client.close();
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ai/core enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

5 participants