# Simple MCP Server Example Build a small TypeScript MCP server with one temperature-conversion tool. The temperature converter is intentionally simple. An AI model can usually do this calculation without a tool. The point is to make the complete MCP flow easy to see: define a tool, validate its arguments, connect through a transport, inspect the contract, and let an AI client call it. Real MCP servers are more useful when they expose something the model cannot access by itself, such as private databases, current service data, local development tools, or actions in another system. ## Set up the project ```bash mkdir mcp-crash-course-dev cd mcp-crash-course-dev npm init -y npm pkg set type=module npm install --save-dev typescript tsx @types/node npm install @modelcontextprotocol/server@2 zod@4 mkdir src npm pkg set scripts.dev="tsx src/index.ts" npm pkg set scripts.typecheck="tsc --noEmit" ``` ## TypeScript configuration Create `tsconfig.json`: ```json { "compilerOptions": { "target": "ES2024", "module": "NodeNext", "moduleResolution": "NodeNext", "strict": true, "noEmit": true, "types": ["node"] }, "include": ["src/**/*.ts"] } ``` ## Temperature conversion Create `src/temperature.ts` and define the allowed units: ```ts export const temperatureUnits = ['celsius', 'fahrenheit', 'kelvin'] as const; export type TemperatureUnit = (typeof temperatureUnits)[number]; ``` `as const` preserves the three literal values, so `TemperatureUnit` becomes their exact union instead of `string`. Add the public conversion function: ```ts export function convertTemperature( value: number, from: TemperatureUnit, to: TemperatureUnit, ): number { if (from === to) { return value; } const celsius = toCelsius(value, from); const converted = fromCelsius(celsius, to); return Number(converted.toFixed(2)); } ``` Every value is converted to Celsius first, then from Celsius to the requested unit. Finish the file with the two internal helpers: ```ts function toCelsius(value: number, unit: TemperatureUnit): number { if (unit === 'fahrenheit') { return (value - 32) * (5 / 9); } if (unit === 'kelvin') { return value - 273.15; } return value; } function fromCelsius(value: number, unit: TemperatureUnit): number { if (unit === 'fahrenheit') { return value * (9 / 5) + 32; } if (unit === 'kelvin') { return value + 273.15; } return value; } ``` ## MCP server Create `src/index.ts` with the imports and server metadata: ```ts import { McpServer } from '@modelcontextprotocol/server'; import { StdioServerTransport } from '@modelcontextprotocol/server/stdio'; import * as z from 'zod/v4'; import { convertTemperature, temperatureUnits } from './temperature.js'; const server = new McpServer({ name: 'temperature-server', version: '1.0.0', }); ``` The `.js` extension matches the JavaScript module path Node will run after TypeScript processing. Create the reusable unit schema: ```ts const temperatureUnitSchema = z.enum(temperatureUnits); ``` Register the tool with its description, input contract, and handler: ```ts server.registerTool( 'convert-temperature', { title: 'Convert temperature', description: 'Convert a temperature between Celsius, Fahrenheit, and Kelvin.', inputSchema: z.object({ value: z.number().describe('The temperature value to convert.'), from: temperatureUnitSchema.describe('The current temperature unit.'), to: temperatureUnitSchema.describe('The target temperature unit.'), }), }, async ({ value, from, to }) => { const result = convertTemperature(value, from, to); return { content: [ { type: 'text', text: `${value} ${from} is ${result} ${to}.`, }, ], }; }, ); ``` Finally, connect the server through stdio: ```ts const transport = new StdioServerTransport(); await server.connect(transport); console.error('Temperature MCP server is running over stdio.'); ``` `stdout` carries MCP messages, so send diagnostic logs to `stderr` with `console.error()`. ## Check the server ```bash npm run typecheck ``` ## Use the browser-based MCP Inspector Launch MCP Inspector with the local server command: ```bash npx @modelcontextprotocol/inspector npm run dev ``` Inspector opens in the browser with an `npm` server card. If the card says **Disconnected**, use its toggle to connect. The card should turn green and show **Connected**. Select **Tools** at the top of the page, then select `convert-temperature`. Enter these arguments: ```json { "value": 20, "from": "celsius", "to": "fahrenheit" } ``` Run the tool. The result should report that 20 Celsius is 68 Fahrenheit. To see schema validation, try an unsupported unit. Inspector should reject the request before the conversion function runs. The **Read-only session** banner is expected when Inspector is launched with an ad-hoc server command. It means the server entry cannot be edited or saved in Inspector. It does not prevent connecting to the server or calling its tools. ## Connect the server to Claude Code From the `mcp-crash-course-dev` project folder, add the server to Claude Code: ```bash claude mcp add --transport stdio --scope local temperature-server -- npm run dev ``` The double dash separates Claude's CLI options from the command that starts the MCP server. Local scope makes the server available only when Claude Code is running in this project. Claude saves the launch command, not a permanently running process. When a session needs the server, Claude starts `npm run dev` and communicates with it over stdio. Verify the connection: ```bash claude mcp list ``` Start Claude Code from the same project folder: ```bash claude ``` Inside Claude Code, run `/mcp` and confirm that `temperature-server` is connected with one available tool. Then ask: ```text What is 20 degrees Celsius in Fahrenheit? Use the convert-temperature tool. ``` Review and approve the tool call if Claude requests permission. Claude should send `20`, `celsius`, and `fahrenheit` to the server. The server validates the arguments, runs the conversion function, and returns the text result. To remove the project-local connection later, run: ```bash claude mcp remove temperature-server --scope local ```