From fa0744c366aac16f7c1813d5d17868527c0b121b Mon Sep 17 00:00:00 2001 From: Allen Zhou <46854522+allenzhou101@users.noreply.github.com> Date: Mon, 17 Feb 2025 16:24:00 -0800 Subject: [PATCH 1/5] Add mcp-client-typescript quickstart guide --- quickstart/client.mdx | 358 ++++++++++++++++++++++++++++++++++++++++++ quickstart/server.mdx | 2 +- 2 files changed, 359 insertions(+), 1 deletion(-) diff --git a/quickstart/client.mdx b/quickstart/client.mdx index a751bed..f7bcfe5 100644 --- a/quickstart/client.mdx +++ b/quickstart/client.mdx @@ -402,6 +402,364 @@ If you see: + + +[You can find the complete code for this tutorial here.](https://github.com/modelcontextprotocol/quickstart-resources/tree/main/mcp-client-typescript) + +## System Requirements + +Before starting, ensure your system meets these requirements: +- Mac or Windows computer +- Node.js 16 or higher installed +- Latest version of `npm` installed +- Anthropic API key (Claude) + +## Setting Up Your Environment + +First, let's create and set up our project: + + +```bash MacOS/Linux +# Create project directory +mkdir mcp-client-typescript +cd mcp-client-typescript + +# Initialize npm project +npm init -y + +# Install dependencies +npm install @anthropic-ai/sdk @modelcontextprotocol/sdk dotenv + +# Install dev dependencies +npm install --save-dev @types/node + +# Initialize TypeScript configuration +npx tsc --init + +# Create source file +touch index.ts +``` + +```powershell Windows +# Create project directory +md mcp-client-typescript +cd mcp-client-typescript + +# Initialize npm project +npm init -y + +# Install dependencies +npm install @anthropic-ai/sdk @modelcontextprotocol/sdk dotenv + +# Install dev dependencies +npm install --save-dev @types/node + +# Initialize TypeScript configuration +npx tsc --init + +# Create source file +new-item index.ts +``` + + +## Setting Up Your API Key + +You'll need an Anthropic API key from the [Anthropic Console](https://console.anthropic.com/settings/keys). + +Create a `.env` file to store it: + +```bash +echo "ANTHROPIC_API_KEY=" > .env +``` + +Add `.env` to your `.gitignore`: +```bash +echo ".env" >> .gitignore +``` + + +Make sure you keep your `ANTHROPIC_API_KEY` secure! + + +## Creating the Client + +### Basic Client Structure + +First, let's set up our imports and create the basic client class in `index.ts`: + +```typescript +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" }); + } + // methods will go here +} +``` + +### Server Connection Management + +Next, we'll implement the method to connect to an MCP server: + +```typescript +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; + } +} +``` + +### Query Processing Logic + +Now let's add the core functionality for processing queries and handling tool calls: + +```typescript +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"); +} +``` + +### Interactive Chat Interface + +Now we'll add the chat loop and cleanup functionality: + +```typescript +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(); +} +``` + +### Main Entry Point + +Finally, we'll add the main execution logic: + +```typescript +async function main() { + if (process.argv.length < 3) { + console.log("Usage: node index.ts "); + return; + } + const mcpClient = new MCPClient(); + try { + await mcpClient.connectToServer(process.argv[2]); + await mcpClient.chatLoop(); + } finally { + await mcpClient.cleanup(); + process.exit(0); + } +} + +main(); +``` + +## Running the Client + +To run your client with any MCP server: + +```bash +# Build TypeScript +npx tsc + +# Run the client +node build/index.js path/to/server.py # python server +node build/index.js path/to/build/index.js # node server +``` + + +If you're continuing the weather tutorial from the server quickstart, your command might look something like this: `node build/index.js .../quickstart-resources/weather-server-typescript/build/index.js` + + +## Key Components Explained + +### 1. Client Initialization +- Uses TypeScript classes for better type safety +- Initializes Anthropic client with API key +- Creates MCP client with version information + +### 2. Server Connection +- Supports both Python and Node.js servers +- Platform-aware Python command selection +- Establishes stdio transport connection + +### 3. Query Processing +- Strong typing for messages and tools +- Handles Claude's tool calls +- Manages conversation context +- Processes tool results + +### 4. Resource Management +- Proper cleanup of resources +- Type-safe error handling +- Graceful shutdown procedures + +## Best Practices + +1. **Type Safety** + - Use TypeScript interfaces for tool schemas + - Leverage type checking for API responses + - Define clear types for message structures + +2. **Error Handling** + - Implement proper try-catch blocks + - Type-check API responses + - Validate server connections + +3. **Security** + - Store API keys in environment variables + - Validate server paths + - Check tool permissions + +## Troubleshooting + +### Common Issues + +1. **TypeScript Build Errors** + - Ensure `tsconfig.json` is properly configured + - Check import paths are correct + - Verify all dependencies are installed + +2. **Runtime Errors** + - Check API key is properly set + - Verify server path is correct + - Ensure proper Node.js version + +3. **Server Connection Issues** + - Verify server script exists + - Check file permissions + - Ensure correct Python/Node command + + + + diff --git a/quickstart/server.mdx b/quickstart/server.mdx index dfa5e29..6203294 100644 --- a/quickstart/server.mdx +++ b/quickstart/server.mdx @@ -316,7 +316,7 @@ This tells Claude for Desktop: Save the file, and restart **Claude for Desktop**. - + Let's get started with building our weather server! [You can find the complete code for what we'll be building here.](https://github.com/modelcontextprotocol/quickstart-resources/tree/main/weather-server-typescript) ### Prerequisite knowledge From 7f3c508c6a7383cad83a1dc0ac6efe5a7b75bc2c Mon Sep 17 00:00:00 2001 From: Allen Zhou <46854522+allenzhou101@users.noreply.github.com> Date: Mon, 17 Feb 2025 16:47:27 -0800 Subject: [PATCH 2/5] Improve client running explanation, best practices, etc. --- quickstart/client.mdx | 106 +++++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 47 deletions(-) diff --git a/quickstart/client.mdx b/quickstart/client.mdx index f7bcfe5..0015f4f 100644 --- a/quickstart/client.mdx +++ b/quickstart/client.mdx @@ -405,13 +405,12 @@ If you see: [You can find the complete code for this tutorial here.](https://github.com/modelcontextprotocol/quickstart-resources/tree/main/mcp-client-typescript) - ## System Requirements Before starting, ensure your system meets these requirements: - Mac or Windows computer - Node.js 16 or higher installed -- Latest version of `npm` installed +- Latest version of `npm` and `npx` installed - Anthropic API key (Claude) ## Setting Up Your Environment @@ -698,64 +697,77 @@ node build/index.js path/to/build/index.js # node server If you're continuing the weather tutorial from the server quickstart, your command might look something like this: `node build/index.js .../quickstart-resources/weather-server-typescript/build/index.js` -## Key Components Explained +**The client will:** +1. Connect to the specified server +2. List available tools +3. Start an interactive chat session where you can: + - Enter queries + - See tool executions + - Get responses from Claude -### 1. Client Initialization -- Uses TypeScript classes for better type safety -- Initializes Anthropic client with API key -- Creates MCP client with version information +## How It Works -### 2. Server Connection -- Supports both Python and Node.js servers -- Platform-aware Python command selection -- Establishes stdio transport connection +When you submit a query: -### 3. Query Processing -- Strong typing for messages and tools -- Handles Claude's tool calls -- Manages conversation context -- Processes tool results +1. The client gets the list of available tools from the server +2. Your query is sent to Claude along with tool descriptions +3. Claude decides which tools (if any) to use +4. The client executes any requested tool calls through the server +5. Results are sent back to Claude +6. Claude provides a natural language response +7. The response is displayed to you -### 4. Resource Management -- Proper cleanup of resources -- Type-safe error handling -- Graceful shutdown procedures +## Best practices + +1. **Error Handling** + - Use TypeScript's type system for better error detection + - Wrap tool calls in try-catch blocks + - Provide meaningful error messages + - Gracefully handle connection issues -## Best Practices +2. **Security** + - Store API keys securely in `.env` + - Validate server responses + - Be cautious with tool permissions -1. **Type Safety** - - Use TypeScript interfaces for tool schemas - - Leverage type checking for API responses - - Define clear types for message structures +## Troubleshooting -2. **Error Handling** - - Implement proper try-catch blocks - - Type-check API responses - - Validate server connections +### Server Path Issues +- Double-check the path to your server script is correct +- Use the absolute path if the relative path isn't working +- For Windows users, make sure to use forward slashes (/) or escaped backslashes (\\) in the path +- Verify the server file has the correct extension (.js for Node.js or .py for Python) -3. **Security** - - Store API keys in environment variables - - Validate server paths - - Check tool permissions +Example of correct path usage: +```bash +# Relative path +node build/index.js ./server/build/index.js -## Troubleshooting +# Absolute path +node build/index.js /Users/username/projects/mcp-server/build/index.js -### Common Issues +# Windows path (either format works) +node build/index.js C:/projects/mcp-server/build/index.js +node build/index.js C:\\projects\\mcp-server\\build\\index.js +``` -1. **TypeScript Build Errors** - - Ensure `tsconfig.json` is properly configured - - Check import paths are correct - - Verify all dependencies are installed +### Response Timing +- The first response might take up to 30 seconds to return +- This is normal and happens while: + - The server initializes + - Claude processes the query + - Tools are being executed +- Subsequent responses are typically faster +- Don't interrupt the process during this initial waiting period -2. **Runtime Errors** - - Check API key is properly set - - Verify server path is correct - - Ensure proper Node.js version +### Common Error Messages -3. **Server Connection Issues** - - Verify server script exists - - Check file permissions - - Ensure correct Python/Node command +If you see: +- `Error: Cannot find module`: Check your build folder and ensure TypeScript compilation succeeded +- `Connection refused`: Ensure the server is running and the path is correct +- `Tool execution failed`: Verify the tool's required environment variables are set +- `ANTHROPIC_API_KEY is not set`: Check your .env file and environment variables +- `TypeError`: Ensure you're using the correct types for tool arguments From f707436cb643e4ca0e7cb6a14132516cf87514c7 Mon Sep 17 00:00:00 2001 From: Allen Zhou <46854522+allenzhou101@users.noreply.github.com> Date: Mon, 17 Feb 2025 16:47:56 -0800 Subject: [PATCH 3/5] Set new python mcp client github link --- quickstart/client.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickstart/client.mdx b/quickstart/client.mdx index 0015f4f..4cd100f 100644 --- a/quickstart/client.mdx +++ b/quickstart/client.mdx @@ -8,7 +8,7 @@ In this tutorial, you'll learn how to build a LLM-powered chatbot client that co -[You can find the complete code for this tutorial here.](https://github.com/modelcontextprotocol/quickstart-resources/tree/main/mcp-client) +[You can find the complete code for this tutorial here.](https://github.com/modelcontextprotocol/quickstart-resources/tree/main/mcp-client-python) ## System Requirements From 046c593da9b63641e64b2ca497d91211774f86f8 Mon Sep 17 00:00:00 2001 From: Allen Zhou <46854522+allenzhou101@users.noreply.github.com> Date: Mon, 17 Feb 2025 17:03:17 -0800 Subject: [PATCH 4/5] Only call list tools at initialization --- quickstart/client.mdx | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/quickstart/client.mdx b/quickstart/client.mdx index 4cd100f..2601760 100644 --- a/quickstart/client.mdx +++ b/quickstart/client.mdx @@ -508,6 +508,7 @@ class MCPClient { private mcp: Client; private anthropic: Anthropic; private transport: StdioClientTransport | null = null; + private tools: Tool[] = []; constructor() { this.anthropic = new Anthropic({ @@ -536,13 +537,15 @@ async connectToServer(serverScriptPath: string) { ? "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) => { + this.tools = toolsResult.tools.map((tool) => { return { name: tool.name, description: tool.description, @@ -551,7 +554,7 @@ async connectToServer(serverScriptPath: string) { }); console.log( "Connected to server with tools:", - tools.map(({ name }) => name) + this.tools.map(({ name }) => name) ); } catch (e) { console.log("Failed to connect to MCP server: ", e); @@ -573,19 +576,11 @@ async processQuery(query: string) { }, ]; - 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, + tools: this.tools, }); const finalText = []; @@ -639,16 +634,20 @@ async chatLoop() { output: process.stdout, }); - console.log("\nMCP Client Started!"); - console.log("Type your queries or 'quit' to exit."); + try { + 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; + while (true) { + const message = await rl.question("\nQuery: "); + if (message.toLowerCase() === "quit") { + break; + } + const response = await this.processQuery(message); + console.log("\n" + response); } - const response = await this.processQuery(message); - console.log("\n" + response); + } finally { + rl.close(); } } From b03bc0d87c8b2ccfa88416d2dfafa6d95833cb6e Mon Sep 17 00:00:00 2001 From: Allen Zhou <46854522+allenzhou101@users.noreply.github.com> Date: Wed, 19 Feb 2025 10:23:44 -0800 Subject: [PATCH 5/5] Add package.json and tsconfig.json setup section --- quickstart/client.mdx | 45 +++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/quickstart/client.mdx b/quickstart/client.mdx index 2601760..a32e969 100644 --- a/quickstart/client.mdx +++ b/quickstart/client.mdx @@ -410,7 +410,7 @@ If you see: Before starting, ensure your system meets these requirements: - Mac or Windows computer - Node.js 16 or higher installed -- Latest version of `npm` and `npx` installed +- Latest version of `npm` installed - Anthropic API key (Claude) ## Setting Up Your Environment @@ -430,10 +430,7 @@ npm init -y npm install @anthropic-ai/sdk @modelcontextprotocol/sdk dotenv # Install dev dependencies -npm install --save-dev @types/node - -# Initialize TypeScript configuration -npx tsc --init +npm install -D @types/node typescript # Create source file touch index.ts @@ -451,16 +448,44 @@ npm init -y npm install @anthropic-ai/sdk @modelcontextprotocol/sdk dotenv # Install dev dependencies -npm install --save-dev @types/node - -# Initialize TypeScript configuration -npx tsc --init +npm install -D @types/node typescript # Create source file new-item index.ts ``` +Update your `package.json` to set `type: "module"` and a build script: + +```json package.json +{ + "type": "module", + "scripts": { + "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"", + } +} +``` + +Create a `tsconfig.json` in the root of your project: + +```json tsconfig.json +{ + "compilerOptions": { + "target": "ES2022", + "module": "Node16", + "moduleResolution": "Node16", + "outDir": "./build", + "rootDir": "./", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["index.ts"], + "exclude": ["node_modules"] +} +``` + ## Setting Up Your API Key You'll need an Anthropic API key from the [Anthropic Console](https://console.anthropic.com/settings/keys). @@ -685,7 +710,7 @@ To run your client with any MCP server: ```bash # Build TypeScript -npx tsc +npm run build # Run the client node build/index.js path/to/server.py # python server