Follow Discord
Sweep 22 Oct 2026 · 21:04Z Build v2.1.309 528 read Stable v2.1.308 Latest v2.1.309 Next v2.1.309 Feeds RSS JSON llms.txt llms-full.txt Unofficial
One change · claude-docs

Build your first MCP server for Claude changedconnectors/building/quickstart

Nearest release: v2.1.283, published under an hour after upstream edited the page. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.

Upstream edited this page at 25 Sep 2026 18:00 UTC, give or take a minute or two: the time comes from Anthropic’s own sitemap rather than from a commit. This site recorded the change at 20 Oct 2026 18:07 UTC.

Upstream edited
Recorded here
Lines+160added
Lines−166removed
From line 6 where the diff opens
First seen 25 Sep 2026 this site's first read of the page
Recorded edits3to this page, all time

The whole hunk

from line 6, old and new numbered
/
lines
from line 6
66 
77This quickstart is for developers who haven't built an MCP server before and want to see every piece working before they add authentication, hosting, and real tools.
88 
9<Note>
10 * If you already have a server and want to know what Claude's client supports, see [Build an MCP server for Claude](/docs/connectors/building/index)
11 * If your server is running and you want to try it in Claude, see [Test your connector](/docs/connectors/building/testing)
12 * If you want to package skills and an existing connector rather than write a server, see [Build your first plugin](/docs/plugins/quickstart)
13</Note>
9> [!NOTE]
10>
11> * If you already have a server and want to know what Claude's client supports, see [Build an MCP server for Claude](/docs/connectors/building/index)
12> * If your server is running and you want to try it in Claude, see [Test your connector](/docs/connectors/building/testing)
13> * If you want to package skills and an existing connector rather than write a server, see [Build your first plugin](/docs/plugins/quickstart)
1414 
1515## Before you begin
1616 
from line 25
2525 
2626The project is a folder with a `package.json` and two dependencies: the [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk), which also works from plain JavaScript, and `zod`, which the SDK uses to describe tool inputs.
2727 
28<Steps>
29 <Step title="Make a folder for the server">
30 In your terminal, create a folder named `mcp-quickstart` and move into it. Every later command on this page runs from this folder:
28**1. Make a folder for the server**
3129 
32 ```bash theme={null}
33 mkdir mcp-quickstart
34 cd mcp-quickstart
35 ```
36 </Step>
30In your terminal, create a folder named `mcp-quickstart` and move into it. Every later command on this page runs from this folder:
3731 
38 <Step title="Create package.json">
39 Create a default `package.json`, then set `"type": "module"` in it so Node.js treats the project's files as ES modules:
32```bash theme={null}
33mkdir mcp-quickstart
34cd mcp-quickstart
35```
4036 
41 ```bash theme={null}
42 npm init -y
43 npm pkg set type=module
44 ```
45 </Step>
37**2. Create package.json**
4638 
47 <Step title="Install the SDK">
48 Install the SDK and `zod`. You don't install a web framework separately, because the SDK depends on Express and gives you a ready-made app for it:
39Create a default `package.json`, then set `"type": "module"` in it so Node.js treats the project's files as ES modules:
4940 
50 ```bash theme={null}
51 npm install @modelcontextprotocol/sdk zod
52 ```
41```bash theme={null}
42npm init -y
43npm pkg set type=module
44```
5345 
54 To confirm what installed, run `npm ls --depth=0`. The output lists the two packages and their versions:
46**3. Install the SDK**
5547 
56 ```text theme={null}
57 [email protected] /path/to/mcp-quickstart
58 +-- @modelcontextprotocol/[email protected]
60 ```
48Install the SDK and `zod`. You don't install a web framework separately, because the SDK depends on Express and gives you a ready-made app for it:
6149 
62 This page was verified with those versions.
63 </Step>
64</Steps>
50```bash theme={null}
51npm install @modelcontextprotocol/sdk zod
52```
6553 
54To confirm what installed, run `npm ls --depth=0`. The output lists the two packages and their versions:
55 
56```text theme={null}
57[email protected] /path/to/mcp-quickstart
58+-- @modelcontextprotocol/[email protected]
60```
61 
62This page was verified with those versions.
63 
6664## Write the server
6765 
6866The whole server is one file. Create `server.mjs` in the `mcp-quickstart` folder and paste in the code below.
from line 129
131129 
132130Before you involve Claude, start the server and send it MCP requests yourself with `curl`, so you know it works on its own.
133131 
134<Steps>
135 <Step title="Start the server">
136 In your first terminal, from the `mcp-quickstart` folder, start the server:
132**1. Start the server**
137133 
138 ```bash theme={null}
139 node server.mjs
140 ```
134In your first terminal, from the `mcp-quickstart` folder, start the server:
141135 
142 It prints the address it's listening on and keeps running:
136```bash theme={null}
137node server.mjs
138```
143139 
144 ```text theme={null}
145 quickstart-server listening on http://localhost:3000/mcp
146 ```
140It prints the address it's listening on and keeps running:
147141 
148 Leave this terminal open. If you see `failed to listen on 3000` instead, another program is using the port. Stop that program and run the command again.
149 </Step>
142```text theme={null}
143quickstart-server listening on http://localhost:3000/mcp
144```
150145 
151 <Step title="List the server's tools">
152 In your second terminal, ask the server what tools it has. This is the same `tools/list` request Claude sends when it connects. The `Accept` header is required, and the server answers `406 Not Acceptable` without it:
146Leave this terminal open. If you see `failed to listen on 3000` instead, another program is using the port. Stop that program and run the command again.
153147 
154 ```bash theme={null}
155 curl -sS -X POST http://localhost:3000/mcp \
156 -H 'Content-Type: application/json' \
157 -H 'Accept: application/json, text/event-stream' \
158 -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
159 ```
148**2. List the server's tools**
160149 
161 The reply is one server-sent event whose `data` line lists `roll_dice` with the title, description, and input schema you wrote. The `inputSchema` object is shortened to `...` here:
150In your second terminal, ask the server what tools it has. This is the same `tools/list` request Claude sends when it connects. The `Accept` header is required, and the server answers `406 Not Acceptable` without it:
162151 
163 ```text theme={null}
164 event: message
165 data: {"result":{"tools":[{"name":"roll_dice","title":"Roll dice","description":"Roll `count` dice with `sides` sides each. Returns each roll and the total.","inputSchema":{...},"execution":{"taskSupport":"forbidden"}}]},"jsonrpc":"2.0","id":2}
166 ```
167 </Step>
152```bash theme={null}
153curl -sS -X POST http://localhost:3000/mcp \
154 -H 'Content-Type: application/json' \
155 -H 'Accept: application/json, text/event-stream' \
156 -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
157```
168158 
169 <Step title="Call the tool">
170 Call `roll_dice` directly with three 20-sided dice, which is the `tools/call` request Claude sends when it uses the tool:
159The reply is one server-sent event whose `data` line lists `roll_dice` with the title, description, and input schema you wrote. The `inputSchema` object is shortened to `...` here:
171160 
172 ```bash theme={null}
173 curl -sS -X POST http://localhost:3000/mcp \
174 -H 'Content-Type: application/json' \
175 -H 'Accept: application/json, text/event-stream' \
176 -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"roll_dice","arguments":{"sides":20,"count":3}}}'
177 ```
161```text theme={null}
162event: message
163data: {"result":{"tools":[{"name":"roll_dice","title":"Roll dice","description":"Roll `count` dice with `sides` sides each. Returns each roll and the total.","inputSchema":{...},"execution":{"taskSupport":"forbidden"}}]},"jsonrpc":"2.0","id":2}
164```
178165 
179 The `data` line carries the text your handler returned, with your own random rolls:
166**3. Call the tool**
180167 
181 ```text theme={null}
182 event: message
183 data: {"result":{"content":[{"type":"text","text":"Rolled 3d20: [16, 10, 20] total=46"}]},"jsonrpc":"2.0","id":3}
184 ```
185 </Step>
186</Steps>
168Call `roll_dice` directly with three 20-sided dice, which is the `tools/call` request Claude sends when it uses the tool:
187169 
170```bash theme={null}
171curl -sS -X POST http://localhost:3000/mcp \
172 -H 'Content-Type: application/json' \
173 -H 'Accept: application/json, text/event-stream' \
174 -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"roll_dice","arguments":{"sides":20,"count":3}}}'
175```
176 
177The `data` line carries the text your handler returned, with your own random rolls:
178 
179```text theme={null}
180event: message
181data: {"result":{"content":[{"type":"text","text":"Rolled 3d20: [16, 10, 20] total=46"}]},"jsonrpc":"2.0","id":3}
182```
183 
188184## Connect the server to Claude Code
189185 
190186With the server answering on its own, register it with Claude Code and have Claude call the tool from a prompt. Run these commands in your second terminal from the `mcp-quickstart` folder, because `claude mcp add` saves the server for the current project folder by default, and Claude Code only sees it when you run from that same folder.
191187 
192<Steps>
193 <Step title="Add the server to Claude Code">
194 Register the server under the name `quickstart`, with the HTTP transport and the local URL:
188**1. Add the server to Claude Code**
195189 
196 ```bash theme={null}
197 claude mcp add --transport http quickstart http://localhost:3000/mcp
198 ```
190Register the server under the name `quickstart`, with the HTTP transport and the local URL:
199191 
200 Claude Code confirms where it saved the entry:
192```bash theme={null}
193claude mcp add --transport http quickstart http://localhost:3000/mcp
194```
201195 
202 ```text theme={null}
203 Added HTTP MCP server quickstart with URL: http://localhost:3000/mcp to local config
204 ```
205 </Step>
196Claude Code confirms where it saved the entry:
206197 
207 <Step title="Check the connection">
208 List your servers. Claude Code connects to each one to check its health:
198```text theme={null}
199Added HTTP MCP server quickstart with URL: http://localhost:3000/mcp to local config
200```
209201 
210 ```bash theme={null}
211 claude mcp list
212 ```
202**2. Check the connection**
213203 
214 A working server shows as connected:
204List your servers. Claude Code connects to each one to check its health:
215205 
216 ```text theme={null}
217 Checking MCP server health…
206```bash theme={null}
207claude mcp list
208```
218209 
219 quickstart: http://localhost:3000/mcp (HTTP) - ✔ Connected
220 ```
210A working server shows as connected:
221211 
222 If it shows an error instead, check that `node server.mjs` is still running in the first terminal.
223 </Step>
212```text theme={null}
213Checking MCP server health…
224214 
225 <Step title="Ask Claude to roll dice">
226 Send Claude one prompt with [`claude -p`](https://code.claude.com/docs/en/headless), which runs a single prompt without opening an interactive session and prints the answer. Claude Code names MCP tools `mcp__<server>__<tool>`, so your tool is `mcp__quickstart__roll_dice`, and `--allowedTools` lets Claude call it without stopping to ask you:
215quickstart: http://localhost:3000/mcp (HTTP) - ✔ Connected
216```
227217 
228 ```bash theme={null}
229 claude -p "Roll three 20-sided dice using the quickstart server and tell me each roll and the total." \
230 --allowedTools "mcp__quickstart__roll_dice"
231 ```
218If it shows an error instead, check that `node server.mjs` is still running in the first terminal.
232219 
233 Claude calls your server and reports the rolls it got back:
220**3. Ask Claude to roll dice**
234221 
235 ```text theme={null}
236 Rolled 3d20 via the quickstart server:
222Send Claude one prompt with [`claude -p`](https://code.claude.com/docs/en/headless), which runs a single prompt without opening an interactive session and prints the answer. Claude Code names MCP tools `mcp__<server>__<tool>`, so your tool is `mcp__quickstart__roll_dice`, and `--allowedTools` lets Claude call it without stopping to ask you:
237223 
238 - Roll 1: **12**
239 - Roll 2: **4**
240 - Roll 3: **5**
224```bash theme={null}
225claude -p "Roll three 20-sided dice using the quickstart server and tell me each roll and the total." \
226 --allowedTools "mcp__quickstart__roll_dice"
227```
241228 
242 **Total: 21**
243 ```
244 </Step>
229Claude calls your server and reports the rolls it got back:
245230 
246 <Step title="Confirm the tool ran">
247 To see the tool call itself rather than Claude's summary of it, run the same prompt with streaming JSON output. `--output-format stream-json` needs `--verbose` alongside it:
231```text theme={null}
232Rolled 3d20 via the quickstart server:
248233 
249 ```bash theme={null}
250 claude -p "Roll three 20-sided dice using the quickstart server and tell me each roll and the total." \
251 --allowedTools "mcp__quickstart__roll_dice" \
252 --output-format stream-json --verbose
253 ```
234- Roll 1: **12**
235- Roll 2: **4**
236- Roll 3: **5**
254237 
255 The output is one JSON object per line, and other tool calls can appear before yours. Look for the `tool_use` block whose `name` is `mcp__quickstart__roll_dice`, carrying the arguments Claude chose, and the `tool_result` block after it carrying your server's text, shown here with the surrounding fields removed:
238**Total: 21**
239```
256240 
257 ```text theme={null}
258 {"type":"tool_use","name":"mcp__quickstart__roll_dice","input":{"count":3,"sides":20}, ...}
259 {"type":"tool_result","content":[{"type":"text","text":"Rolled 3d20: [9, 14, 5] total=28"}], ...}
260 ```
261 </Step>
262</Steps>
241**4. Confirm the tool ran**
242 
243To see the tool call itself rather than Claude's summary of it, run the same prompt with streaming JSON output. `--output-format stream-json` needs `--verbose` alongside it:
244 
245```bash theme={null}
246claude -p "Roll three 20-sided dice using the quickstart server and tell me each roll and the total." \
247 --allowedTools "mcp__quickstart__roll_dice" \
248 --output-format stream-json --verbose
249```
250 
251The output is one JSON object per line, and other tool calls can appear before yours. Look for the `tool_use` block whose `name` is `mcp__quickstart__roll_dice`, carrying the arguments Claude chose, and the `tool_result` block after it carrying your server's text, shown here with the surrounding fields removed:
252 
253```text theme={null}
254{"type":"tool_use","name":"mcp__quickstart__roll_dice","input":{"count":3,"sides":20}, ...}
255{"type":"tool_result","content":[{"type":"text","text":"Rolled 3d20: [9, 14, 5] total=28"}], ...}
256```
263257 
264258In an interactive `claude` session you can ask the same thing in your own words. Without `--allowedTools`, Claude Code asks for your permission before it calls `roll_dice`.
265259 
Feedback