Setting up MCP servers to bridge Claude and your local data

ycombinator70 Beginner 2d ago 301 views 10 likes 5 min read

Anthropic's Model Context Protocol (MCP) isn't just another abstraction layer; it's a way to stop copy-pasting your database schema into a chat box every time you want to run a query. I spent about three hours last Friday afternoon fighting with a broken Python environment just to get a simple SQLite MCP server running, and it taught me that the setup process is where most people trip up.

Model Context Protocol tutorial

If you want to build tools that actually interact with your filesystem or local APIs, you need to move past simple prompting and into structured context injection.

Getting your first MCP server running locally

You can't just "use" MCP. You need a host—usually Claude Desktop for now—and a server that speaks the protocol. The easiest way to see the magic is to use the pre-built Google Maps or Filesystem servers, but let's build something slightly more customized using the TypeScript SDK.

First, ensure you have Node.js v18 or higher installed. I checked my version with node -v before starting because the older v16 builds kept throwing weird ESM errors during the build step.

Step 1: Initialize the project

Open your terminal and run these commands. Don't skip the initialization; the package.json needs the correct type definition.

mkdir my-mcp-server
cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk

Edit your package.json immediately to include "type": "module". If you forget this, your imports will break, and you'll spend twenty minutes wondering why import isn't recognized.

Step 2: Coding a basic tool

Create a file named index.ts. We aren't building a complex AI; we are building a bridge. The goal is to provide a "tool" that the LLM can call.

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";

const server = new Server(
{ name: "local-time-server", version: "1.0.0" },
{ capabilities: { tools: {} } }
);

// Define what the tool does
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "get_current_time",
description: "Returns the current system time",
inputSchema: { type: "object", properties: {} },
},
],
}));

// Handle the actual logic
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "get_current_time") {
return {
content: [{ type: "text", text: new Date().toISOString() }],
};
}
throw new Error("Tool not found");
});

async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}

main().catch(console.error);

This looks simple, but the key is the StdioServerTransport. MCP works over standard input/output. This means your server doesn't run a web server on a port; it communicates through the process stream.

Configuring Claude Desktop to recognize your code

Model Context Protocol tutorial

This is where most people fail. You can't just run npm start and expect Claude to see it. You have to tell the Claude Desktop app where your executable lives.

On macOS, your config file is located at:
~/Library/Application Support/Claude/claude_desktop_config.json

On Windows:
%APPDATA%\Claude\claude_desktop_config.json

You need to add your server to the mcpServers object. It looks like this:

{
"mcpServers": {
"my-custom-server": {
"command": "node",
"args": ["/absolute/path/to/my-mcp-server/index.js"]
}
}
}

Warning: Use absolute paths. I tried using ~/ in the config and Claude just ignored the server entirely. It doesn't resolve tilde shortcuts.

Testing the connection

Restart Claude Desktop. Completely quit the app (Cmd+Q) and reopen it. Look for a small hammer icon in the chat interface. If that icon isn't there, your JSON is likely malformed or your path is wrong.

| Server Type | Connection Method | Latency (Local) | Best Use Case |
| :--- | :--- | :--- | :--- |
| Stdio (Local) | Standard Input/Output | < 10ms | Local files, SQLite, Scripts |
| SSE (Remote) | HTTP/Server-Sent Events | 50ms - 200ms | Cloud APIs, Shared DBs |
| Custom SDK | Direct Integration | Variable | Specialized enterprise tools |

Debugging the "Silent Fail"

If you're working on complex AI Coding tasks, you'll eventually run into a situation where the tool is listed but fails when called. This usually happens because of environment variables.

When Claude runs your MCP server, it doesn't inherit your terminal's .zshrc or .bashrc settings. If your code needs an API_KEY, you must pass it inside the env object in the claude_desktop_config.json:

"my-custom-server": {
"command": "node",
"args": ["/path/to/index.js"],
"env": {
"MY_SECRET_TOKEN": "sk-12345"
}
}

If you don't do this, your server will crash silently, or worse, return a generic "Internal error" to the LLM, leaving you guessing.

Scaling with custom Workflows

Once you have a working server, the real fun begins. You shouldn't just use it for single commands. You can chain these tools together. For instance, if you develop a server that can read your local Jira tickets and another that can read your local code, you can create Workflows that allow an LLM to bridge the gap between project management and actual implementation.

I've found that the most effective way to use this is to build "Read-Only" servers first. If you give an LLM "Write" access to your filesystem without strict constraints, it might decide to delete your node_modules folder just to save space.

If you want to see how others are structuring these connections, checking out the PromptCube homepage can give you a sense of the broader ecosystem. The community there is less about "prompt engineering" and more about actual architectural implementation.

A quick sanity check list for your MCP setup:


  • Is your Node version compatible?

  • Did you use absolute paths in the JSON config?

  • Did you restart Claude (not just close the window)?

  • Are your environment variables explicitly defined in the config?

  • Is your package.json set to "type": "module"?
  • If you've checked all these and it's still broken, check the Claude logs. On macOS, they are tucked away in ~/Library/Logs/Claude/. There is usually a very specific error message there about a missing module or a permission denied error that the UI simply won't show you.

    All Replies (0)

    No replies yet — be the first!

    Write a Reply

    Markdown supported