From 30de8f26a4c476811d017742ac6d8e94dbd8e4cf Mon Sep 17 00:00:00 2001 From: mx <1669547593@qq.com> Date: Tue, 20 Jan 2026 20:46:13 +0800 Subject: [PATCH] Add MCP TypeScript server and client --- .env.example | 13 +++++++++++ README.md | 58 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 21 +++++++++++++++++ src/client.ts | 47 +++++++++++++++++++++++++++++++++++++ src/server.ts | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++ tsconfig.json | 13 +++++++++++ 6 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 .env.example create mode 100644 package.json create mode 100644 src/client.ts create mode 100644 src/server.ts create mode 100644 tsconfig.json diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..66ebfb4 --- /dev/null +++ b/.env.example @@ -0,0 +1,13 @@ +# MCP server settings +API_BASE_URL=https://api.example.com/ +API_KEY=your_api_key_here + +# MCP client settings +MCP_SERVER_COMMAND=tsx +MCP_SERVER_ARGS=src/server.ts + +# Optional API request run from client +RUN_API_REQUEST=false +API_REQUEST_PATH=/ +API_REQUEST_METHOD=GET +API_REQUEST_BODY= diff --git a/README.md b/README.md index 9184765..47ad21d 100644 --- a/README.md +++ b/README.md @@ -1 +1,57 @@ -# note \ No newline at end of file +# note + +## MCP TypeScript (Node.js) Starter + +This project provides a minimal MCP server and client written in TypeScript. +The server exposes a simple `ping` tool plus an `api_request` tool that forwards +requests to an external API using an API key you provide. + +### Prerequisites + +- Node.js 18+ +- npm + +### Setup + +```bash +npm install +cp .env.example .env +``` + +Update `.env` with your API settings: + +- `API_BASE_URL` - Base URL of the API you want to call. +- `API_KEY` - API key provided by you. + +### Run the MCP server + +```bash +npm run start:server +``` + +### Run the MCP client + +```bash +npm run start:client +``` + +The client will: + +1. Start the MCP server via stdio. +2. List available tools. +3. Call the `ping` tool. + +If you want the client to also call the `api_request` tool, set: + +```bash +RUN_API_REQUEST=true +API_REQUEST_PATH=/v1/whatever +API_REQUEST_METHOD=GET +API_REQUEST_BODY= +``` + +### Build + +```bash +npm run build +``` diff --git a/package.json b/package.json new file mode 100644 index 0000000..b86c3e4 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "mcp-ts-node", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "build": "tsc -p tsconfig.json", + "start:server": "tsx src/server.ts", + "start:client": "tsx src/client.ts", + "lint": "tsc -p tsconfig.json --noEmit" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.0.3", + "dotenv": "^16.4.5", + "zod": "^3.23.8" + }, + "devDependencies": { + "tsx": "^4.19.2", + "typescript": "^5.6.3" + } +} diff --git a/src/client.ts b/src/client.ts new file mode 100644 index 0000000..1a26e84 --- /dev/null +++ b/src/client.ts @@ -0,0 +1,47 @@ +import "dotenv/config"; +import { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; + +const serverCommand = process.env.MCP_SERVER_COMMAND ?? "tsx"; +const serverArgs = + process.env.MCP_SERVER_ARGS?.split(" ") ?? ["src/server.ts"]; + +const transport = new StdioClientTransport({ + command: serverCommand, + args: serverArgs, +}); + +const client = new Client( + { + name: "mcp-ts-node-client", + version: "0.1.0", + }, + { + capabilities: {}, + } +); + +await client.connect(transport); + +const tools = await client.listTools(); +console.log("Available tools:", tools.tools.map((tool) => tool.name)); + +const pingResult = await client.callTool({ + name: "ping", + arguments: {}, +}); +console.log("Ping response:", pingResult.content); + +if (process.env.RUN_API_REQUEST === "true") { + const apiResult = await client.callTool({ + name: "api_request", + arguments: { + path: process.env.API_REQUEST_PATH ?? "/", + method: process.env.API_REQUEST_METHOD ?? "GET", + body: process.env.API_REQUEST_BODY, + }, + }); + console.log("API response:", apiResult.content); +} + +await client.close(); diff --git a/src/server.ts b/src/server.ts new file mode 100644 index 0000000..0221909 --- /dev/null +++ b/src/server.ts @@ -0,0 +1,64 @@ +import "dotenv/config"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { z } from "zod"; + +const apiBaseUrl = process.env.API_BASE_URL; +const apiKey = process.env.API_KEY; + +const server = new McpServer({ + name: "mcp-ts-node-server", + version: "0.1.0", +}); + +server.tool("ping", {}, async () => { + return { + content: [{ type: "text", text: "pong" }], + }; +}); + +server.tool( + "api_request", + { + path: z.string().describe("Relative path to call, e.g. /v1/models"), + method: z.enum(["GET", "POST"]).default("GET"), + body: z.string().optional().describe("Raw JSON string for POST requests"), + }, + async ({ path, method, body }) => { + if (!apiBaseUrl || !apiKey) { + return { + content: [ + { + type: "text", + text: + "Missing API_BASE_URL or API_KEY. Set them in your environment or .env file.", + }, + ], + }; + } + + const url = new URL(path, apiBaseUrl).toString(); + const response = await fetch(url, { + method, + headers: { + Authorization: `Bearer ${apiKey}`, + "Content-Type": "application/json", + }, + body: method === "POST" ? body ?? "" : undefined, + }); + + const responseText = await response.text(); + + return { + content: [ + { + type: "text", + text: `status: ${response.status}\n${responseText}`, + }, + ], + }; + } +); + +const transport = new StdioServerTransport(); +await server.connect(transport); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2c1f0a5 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "outDir": "dist", + "rootDir": "src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true + }, + "include": ["src"] +}