Skip to content

sample: Add a sample using a group of MCP clients. #486

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions src/examples/client/clientGroupSample.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { Tool } from "../../types.js";
import { Client } from "../../client/index.js";
import { InMemoryTransport } from "../../inMemory.js";
import { McpServer, ToolCallback } from "../../server/mcp.js";
import { Transport } from "../../shared/transport.js";

async function main(): Promise<void> {
console.log("MCP Client Group Example");
console.log("============================");

const clientTransports = await spinUpServers();

const client1 = new Client({
name: "client-1",
version: "1.0.0",
});
client1.connect(clientTransports[0]);

const client2 = new Client({
name: "client-2",
version: "1.0.0",
});
client2.connect(clientTransports[1]);

const client3 = new Client({
name: "client-3",
version: "1.0.0",
});
client3.connect(clientTransports[2]);

const allClients = [client1, client2, client3];
const toolToClient: { [key: string]: Client } = {};
const allTools = [];

for (const client of allClients) {
for (const tool of (await client.listTools()).tools) {
if (toolToClient[tool.name]) {
console.warn(
`Tool name: ${tool.name} is available on multiple servers, picking an arbitrary one`,
);
}
toolToClient[tool.name] = client;
allTools.push(tool);
}
}

const allResources = [];
allResources.push(...(await client1.listResources()).resources);
allResources.push(...(await client2.listResources()).resources);
allResources.push(...(await client3.listResources()).resources);

const toolName = simulatePromptModel(allTools);

console.log(`Invoking tool: ${toolName}`);
const toolResult = await toolToClient[toolName].callTool({
name: toolName,
});

console.log(toolResult);

for (const client of allClients) {
await client.close();
}
}

// Start the example
main().catch((error: unknown) => {
console.error("Error running MCP Client Group example:", error);
process.exit(1);
});

async function spinUpServer(
name: string,
cb: ToolCallback,
): Promise<Transport> {
const server = new McpServer({
name: name,
version: "1.0.0",
});

server.tool(name, cb);

server.resource("greeting", "greeting://hello", async (uri) => ({
contents: [
{
uri: uri.href,
text: `Hello from ${name}!`,
},
],
}));

const transports = InMemoryTransport.createLinkedPair();
await server.connect(transports[0]);

return transports[1];
}

async function spinUpServers(): Promise<Transport[]> {
const clientTransports = [];
clientTransports.push(
await spinUpServer("ping", async () => ({
content: [{ type: "text", text: "pong" }],
})),
);

clientTransports.push(
await spinUpServer("pong", async () => ({
content: [{ type: "text", text: "ping" }],
})),
);

// We deliberately spin up 2 servers with the same tool name to
// demonstrate dealing with that edge case in the sample.
clientTransports.push(
await spinUpServer("ping", async () => ({
content: [{ type: "text", text: "pong2" }],
})),
);

return clientTransports;
}

function simulatePromptModel(tools: Tool[]): string {
console.log(`Model was prompted with the following tools:`);
for (const tool of tools) {
console.log(` - ${tool.name}`);
}
return "ping";
}