Add mcp-client-typescript
This commit is contained in:
parent
6541c17569
commit
0411bf6975
1 changed files with 847 additions and 1 deletions
167
mcp-client-typescript/index.ts
Normal file
167
mcp-client-typescript/index.ts
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
import { Anthropic } from "@anthropic-ai/sdk";
|
||||
import {
|
||||
MessageParam,
|
||||
Tool,
|
||||
} from "@anthropic-ai/sdk/resources/messages/messages.mjs";
|
||||
|
||||
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
||||
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
||||
import readline from "readline/promises";
|
||||
|
||||
import dotenv from "dotenv";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
|
||||
if (!ANTHROPIC_API_KEY) {
|
||||
throw new Error("ANTHROPIC_API_KEY is not set");
|
||||
}
|
||||
|
||||
class MCPClient {
|
||||
private mcp: Client;
|
||||
private anthropic: Anthropic;
|
||||
private transport: StdioClientTransport | null = null;
|
||||
|
||||
constructor() {
|
||||
this.anthropic = new Anthropic({
|
||||
apiKey: ANTHROPIC_API_KEY,
|
||||
});
|
||||
this.mcp = new Client({ name: "mcp-client-cli", version: "1.0.0" });
|
||||
}
|
||||
|
||||
async connectToServer(serverScriptPath: string) {
|
||||
try {
|
||||
const isJs = serverScriptPath.endsWith(".js");
|
||||
const isPy = serverScriptPath.endsWith(".py");
|
||||
if (!isJs && !isPy) {
|
||||
throw new Error("Server script must be a .js or .py file");
|
||||
}
|
||||
const command = isPy
|
||||
? process.platform === "win32"
|
||||
? "python"
|
||||
: "python3"
|
||||
: process.execPath;
|
||||
this.transport = new StdioClientTransport({
|
||||
command,
|
||||
args: [serverScriptPath],
|
||||
});
|
||||
this.mcp.connect(this.transport);
|
||||
const toolsResult = await this.mcp.listTools();
|
||||
const tools = toolsResult.tools.map((tool) => {
|
||||
return {
|
||||
name: tool.name,
|
||||
description: tool.description,
|
||||
input_schema: tool.inputSchema,
|
||||
};
|
||||
});
|
||||
console.log(
|
||||
"Connected to server with tools:",
|
||||
tools.map(({ name }) => name)
|
||||
);
|
||||
} catch (e) {
|
||||
console.log("Failed to connect to MCP server: ", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async processQuery(query: string) {
|
||||
const messages: MessageParam[] = [
|
||||
{
|
||||
role: "user",
|
||||
content: query,
|
||||
},
|
||||
];
|
||||
|
||||
const toolsResult = await this.mcp.listTools();
|
||||
const tools: Tool[] = toolsResult.tools.map((tool) => {
|
||||
return {
|
||||
name: tool.name,
|
||||
description: tool.description,
|
||||
input_schema: tool.inputSchema,
|
||||
};
|
||||
});
|
||||
const response = await this.anthropic.messages.create({
|
||||
model: "claude-3-5-sonnet-20241022",
|
||||
max_tokens: 1000,
|
||||
messages,
|
||||
tools,
|
||||
});
|
||||
|
||||
const finalText = [];
|
||||
const toolResults = [];
|
||||
|
||||
for (const content of response.content) {
|
||||
if (content.type === "text") {
|
||||
finalText.push(content.text);
|
||||
} else if (content.type === "tool_use") {
|
||||
const toolName = content.name;
|
||||
const toolArgs = content.input as { [x: string]: unknown } | undefined;
|
||||
|
||||
const result = await this.mcp.callTool({
|
||||
name: toolName,
|
||||
arguments: toolArgs,
|
||||
});
|
||||
toolResults.push(result);
|
||||
finalText.push(
|
||||
`[Calling tool ${toolName} with args ${JSON.stringify(toolArgs)}]`
|
||||
);
|
||||
|
||||
messages.push({
|
||||
role: "user",
|
||||
content: result.content as string,
|
||||
});
|
||||
|
||||
const response = await this.anthropic.messages.create({
|
||||
model: "claude-3-5-sonnet-20241022",
|
||||
max_tokens: 1000,
|
||||
messages,
|
||||
});
|
||||
|
||||
finalText.push(
|
||||
response.content[0].type === "text" ? response.content[0].text : ""
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return finalText.join("\n");
|
||||
}
|
||||
|
||||
async chatLoop() {
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
});
|
||||
|
||||
console.log("\nMCP Client Started!");
|
||||
console.log("Type your queries or 'quit' to exit.");
|
||||
|
||||
while (true) {
|
||||
const message = await rl.question("\nQuery: ");
|
||||
if (message === "quit") {
|
||||
break;
|
||||
}
|
||||
const response = await this.processQuery(message);
|
||||
console.log("\n" + response);
|
||||
}
|
||||
}
|
||||
|
||||
async cleanup() {
|
||||
await this.mcp.close();
|
||||
}
|
||||
}
|
||||
async function main() {
|
||||
if (process.argv.length < 3) {
|
||||
console.log("Usage: node index.ts <path_to_server_script>");
|
||||
return;
|
||||
}
|
||||
const mcpClient = new MCPClient();
|
||||
try {
|
||||
await mcpClient.connectToServer(process.argv[2]);
|
||||
await mcpClient.chatLoop();
|
||||
} finally {
|
||||
await mcpClient.cleanup();
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
Loading…
Reference in a new issue