# Build your own MCP server
For when nothing in the directory does the job. This is the one guide here that asks you to open a terminal.
**Time:** 25 minutes

**What you'll have when you're done:** A small program running on your own machine that gives Claude one tool you defined, plus enough understanding of its shape to change what that tool does.

**Before you start:**
- You've added a server the normal way first. See the guide on adding one
- Claude Desktop, and a Claude account you can ask to write code
- A willingness to type four commands into a terminal
---This one is different from the rest of the site, so here's the warning before you spend any time on it.

You will open a terminal. Four commands, all copy and paste, but a terminal. If that's a line you don't want to cross, close this tab. Nothing else here requires it, and the guide on [adding an existing server](/guides/connecting-tools/add-an-mcp-server/) gets most people what they need.

Still here? Good. It's less alarming than it looks, and the code is not something you write.

---

## First, be sure you need this

The official filesystem server already reads folders, searches them, and creates files. If that's what you want, you're done in ten minutes with the other guide and none of this.

Build your own when you want a tool shaped to a job only you have. Not "read my files" but "check these notes for the term I give you, and hand back the date and the line around it, newest first." One narrow question, asked your way, answered the same way every time.

That's the difference. A general server gives Claude a capability. Your own server gives it a **procedure**.

<figure class="diagram">
<svg viewBox="0 0 620 300" role="img" aria-labelledby="own-title own-desc" xmlns="http://www.w3.org/2000/svg">
<title id="own-title">Which part you are writing</title>
<desc id="own-desc">The same three boxes as the previous guide. Claude Desktop asks an MCP server, which reaches one specific thing, here a folder of your own notes. The middle box is marked as the part you write, containing one tool with one job that you define.</desc>
<defs>
<marker id="ownTip" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="6" markerHeight="6" orient="auto">
<path d="M0.5 0.5 L 7 4 L 0.5 7.5" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
</marker>
</defs>
<g font-family="Inter Tight, sans-serif" fill="currentColor">
<rect x="20" y="16" width="580" height="40" fill="none" stroke="currentColor" stroke-opacity="0.4" stroke-dasharray="3 3" rx="2"/>
<text x="310" y="41" font-size="13" font-weight="500" text-anchor="middle">Same three boxes. You are only replacing the middle one.</text>
<rect x="20" y="96" width="168" height="72" fill="none" stroke="currentColor" stroke-opacity="0.22" rx="2"/>
<rect x="20" y="96" width="3" height="72" fill="currentColor" fill-opacity="0.4"/>
<text x="38" y="124" font-size="14.5" font-weight="600" fill-opacity="0.55">Claude Desktop</text>
<text x="38" y="146" font-size="12" fill-opacity="0.45">Unchanged</text>
<line x1="188" y1="132" x2="222" y2="132" stroke="currentColor" stroke-opacity="0.45" stroke-width="1.2" marker-end="url(#ownTip)"/>
<rect x="226" y="88" width="168" height="88" fill="none" stroke="currentColor" stroke-opacity="0.85" stroke-width="1.6" rx="2"/>
<rect x="226" y="88" width="3" height="88" fill="currentColor" fill-opacity="0.85"/>
<text x="244" y="112" font-size="10" font-weight="600" letter-spacing="0.08em" fill-opacity="0.5">YOU WRITE THIS</text>
<text x="244" y="136" font-size="14.5" font-weight="600">One tool</text>
<text x="244" y="158" font-size="12" fill-opacity="0.6">One job, defined by you</text>
<line x1="394" y1="132" x2="428" y2="132" stroke="currentColor" stroke-opacity="0.45" stroke-width="1.2" marker-end="url(#ownTip)"/>
<rect x="432" y="96" width="168" height="72" fill="none" stroke="currentColor" stroke-opacity="0.22" rx="2"/>
<rect x="432" y="96" width="3" height="72" fill="currentColor" fill-opacity="0.4"/>
<text x="450" y="124" font-size="14.5" font-weight="600" fill-opacity="0.55">A folder of notes</text>
<text x="450" y="146" font-size="12" fill-opacity="0.45">Yours, on this machine</text>
<rect x="226" y="212" width="374" height="48" fill="none" stroke="currentColor" stroke-opacity="0.22" rx="2"/>
<text x="244" y="234" font-size="12" font-weight="600">Claude writes the code. You describe the job.</text>
<text x="244" y="252" font-size="12" fill-opacity="0.6">Your work is the description and the testing.</text>
<path d="M310 176 V 196 H 244 V 212" fill="none" stroke="currentColor" stroke-opacity="0.45" stroke-width="1.2" marker-end="url(#ownTip)"/>
</g>
</svg>
<figcaption>You are not learning to program. You are describing one job precisely enough that the description can be tested.</figcaption>
</figure>

---

## What a terminal is

A window where you type an instruction and press enter, instead of clicking. That's the whole idea. It looks severe because it has no buttons, not because it's dangerous.

It's called Terminal on a Mac and Command Prompt on Windows. Open it from Spotlight or the Start menu by name.

Two things make it much less nerve-racking:

- **You can drag a folder onto the window** and it types the full path for you. No typing out long paths and getting them wrong.
- **Nothing happens until you press enter.** A half-typed command sitting there does nothing at all.

---

## Step 1: make a home for it

Create a new folder somewhere ordinary. Documents is fine. Call it `my-mcp-server`.

In the terminal, type `cd ` (with the space), then drag your new folder onto the window, then press enter. `cd` means change directory, and you have now told the terminal to work inside that folder.

Then run these two:

```
npm init -y
npm install @modelcontextprotocol/sdk
```

The first creates a small file that marks this folder as a project. The second downloads the toolkit that MCP servers are built from. Both print a wall of text. That's normal and none of it needs reading unless it says `ERR`.

---

## Step 2: have Claude write it

This is the payload of this guide. Open Claude, in the app or the browser, and paste this. Change the bracketed parts to describe your job, not mine.

```
Write me a complete MCP server in Node.js, using the official
@modelcontextprotocol/sdk package.

WHAT IT SHOULD DO
It exposes exactly one tool, called search_notes.
The tool takes one input: a search term.
It looks through every .md and .txt file in this folder:
  [paste the full path to your notes folder]
It returns, for each file that contains the term:
  - the file name
  - the date the file was last changed
  - the one line the term appears on, with a little context
Sort the results newest first. Cap it at 20 results.

CONSTRAINTS
- Read only. It must never write, move, or delete a file.
- If the folder is missing or unreadable, return a clear message
  saying so rather than crashing.
- If nothing matches, say nothing matched. Don't return an empty
  result that looks like an error.
- Use stdio transport, so Claude Desktop can start it.
- One file. No build step, no TypeScript, no framework.

OUTPUT
Give me the complete contents of server.js, ready to save.
Then tell me, separately, the exact claude_desktop_config.json
entry I need, and the exact command to test it by hand first.
```

The constraints block is where the quality comes from. "Read only" is the line that matters most, and stating it in the prompt is more reliable than hoping.

Save what it gives you as `server.js`, inside the folder you made.

<a class="grab" href="/downloads/mcp-server-prompt.txt" download>
  <strong>Download the prompt &#8594;</strong>
  <span>The blank version, the config entry, and the failure modes in one plain-text file. No signup.</span>
</a>

**A note on comparing.** Don't check the code line by line against anything, including any example you find online. This toolkit changes, and a version mismatch looks identical to a mistake. Judge it on whether it runs and whether it answers correctly. That's the only test that means anything here.

---

## Step 3: run it once, by hand

Back in the terminal, in the same folder:

```
node server.js
```

**Working looks like** nothing much. A line saying it's running, or a cursor sitting there. An MCP server waits to be spoken to, so silence is success. Press Ctrl-C to stop it.

**Broken looks like** a block of red text. Copy all of it, paste it back to Claude, and say "this is the error, fix it." Two rounds of that resolves most of them.

Do not skip this step. Finding out here that the file has a typo takes seconds. Finding out after wiring it into Claude Desktop takes twenty minutes of looking in the wrong place.

---

## Step 4: wire it in

Same config file as the other guide, same restart, same place to check. Add your server alongside anything already there:

```json
{
  "mcpServers": {
    "my-notes": {
      "command": "node",
      "args": ["/Users/username/Documents/my-mcp-server/server.js"]
    }
  }
}
```

`command` is `node` this time rather than `npx`, because the program is already on your disk and doesn't need fetching. The path must be the full one, all the way from the top.

Quit Claude Desktop completely, reopen, and look under **Manage connectors** for `my-notes`.

---

## Step 5: prove it works

Ask for something you can check by hand:

- "Search my notes for budget."
- "Search my notes for a word that appears in nothing." You should get a clean "nothing matched", not an error.
- "Search my notes for a term that's in a file you know is old." Check the date it reports.

That third one is the real test. Anything can return results. Returning the right date on the right file is what tells you the tool works.

---

## Step 6: change one thing

Go back to Claude with your working file and ask for one small change. Raise the cap from 20 to 50. Add `.rtf` to the file types. Have it return the two lines either side instead of one.

Save, restart Claude Desktop, test again.

This is the step people skip, and it's the one that matters. Making a change and watching it take effect is the difference between having a server and being able to keep one.

---

## When it breaks

**`node` isn't recognised.** Node isn't installed, or the terminal was open before you installed it. Close the terminal, open a new one, try again.

**`Cannot find module`.** You're in the wrong folder, or `npm install` didn't finish. Run `npm install @modelcontextprotocol/sdk` again in the folder holding `server.js`.

**It runs by hand but Claude Desktop won't show it.** Almost always the path in the config. Use the full path, no `~`, no shortcuts, and check the JSON for a comma after the last entry.

**It shows up but every call fails.** Check the server's own log, at `~/Library/Logs/Claude/mcp-server-my-notes.log` on macOS or under `%APPDATA%\Claude\logs` on Windows. Paste what's there back to Claude.

**It returns nothing when you know there's a match.** Usually the folder path in the code, not the search. Ask Claude to add a message reporting how many files it scanned. If it says zero, it's looking in the wrong place.

**It worked yesterday and doesn't today.** Did the folder move, or get renamed, or sync to a cloud drive that keeps files online rather than on disk? All three look like a broken server and none of them are.

**Claude wrote something that won't run and won't get fixed.** After three rounds, start over with a fresh conversation rather than continuing. Long threads accumulate wrong assumptions, and it starts patching rather than reconsidering.

---

## What you've got, and what you haven't

You have one tool, running locally, doing a job you specified. You can change it. You can build a second one by copying the folder and rewriting the prompt.

You don't have something to give a colleague. It runs on your machine, against your paths, with no installer and no error handling worth the name. Sending someone the file will mostly produce a support request.

You also don't have anything reviewed. The one-click directory has a check behind it. Yours has you. That's fine for a read-only tool pointed at your own notes, and it's the reason to keep it read-only.

If it turns into something a team depends on, that's the point to hand it to somebody who does this properly. Knowing where that line sits is worth as much as the server.

<div class="cta">
<h2>Next</h2>
<ul>
  <li><a href="/guides/connecting-tools/add-an-mcp-server/">Adding an existing server</a> <span>The shorter path, and the config file explained</span></li>
  <li><a href="/guides/">Browse the other guides</a> <span>Same format, different task</span></li>
  <li><a href="/rss.xml">Subscribe by RSS</a> <span>New guides as they publish</span></li>
</ul>
</div>