diff --git a/quickstart/client.mdx b/quickstart/client.mdx index a751bed..a32e969 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 @@ -402,6 +402,400 @@ 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 -D @types/node typescript + +# 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 -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). + +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; + private tools: Tool[] = []; + + 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(); + this.tools = toolsResult.tools.map((tool) => { + return { + name: tool.name, + description: tool.description, + input_schema: tool.inputSchema, + }; + }); + console.log( + "Connected to server with tools:", + this.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 response = await this.anthropic.messages.create({ + model: "claude-3-5-sonnet-20241022", + max_tokens: 1000, + messages, + tools: this.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, + }); + + 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.toLowerCase() === "quit") { + break; + } + const response = await this.processQuery(message); + console.log("\n" + response); + } + } finally { + rl.close(); + } +} + +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 +npm run build + +# 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` + + +**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 + +## How It Works + +When you submit a query: + +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 + +## 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 + +2. **Security** + - Store API keys securely in `.env` + - Validate server responses + - Be cautious with tool permissions + +## Troubleshooting + +### 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) + +Example of correct path usage: +```bash +# Relative path +node build/index.js ./server/build/index.js + +# Absolute path +node build/index.js /Users/username/projects/mcp-server/build/index.js + +# 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 +``` + +### 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 + +### Common Error Messages + +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 + + + + 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