How to build your own MCP server

A complete walkthrough — database, schema, config, token, client — that ends with Claude querying your own data. No SDK, no Node project, no server to host.

Step 1 — Get a Postgres database

If you already have one, skip ahead. If not, any of these work and all have a usable free tier:

Step 2 — Create the schema

Three tables. Only one of them is your data:

Step 3 — Set two variables

In the Air Pipe dashboard, under your environment's managed variables (or as ap_vars if you're self-hosting):

Step 4 — Write the config

Here's the whole thing. One file, two tools.

Step 5 — Deploy

Nothing to build and nothing to host. Validate, then deploy:

Step 6 — Mint a token

Once, at jwt.io: algorithm HS256, secret = your SOLO_SECRET, payload:

Step 7 — Verify it before you touch the client

Debugging through an MCP client is miserable — a failure shows up as "the tool didn't work." Check with curl first. MCP is JSON-RPC over HTTP, so you can drive it directly:

Step 8 — Point Claude at it

Claude Desktop keeps this at ~/Library/Application Support/Claude/claudedesktopconfig.json on macOS and %APPDATA%\Claude\claudedesktopconfig.json on Windows. Claude Code: claude mcp add --transport http my-tasks https://your-airpipe-host/<org>/<env>/mcp --header "Authorization: Bearer <token>".

The hole most MCP servers have

Worth knowing regardless of what you build with: tools/call runs your code, tools/list doesn't.

Now the part that matters: your customers

Everything above is one token, one grant — everyone who holds it sees every row. Right for pointing an AI at your own database. Useless the moment you have users.

The shortcut

Everything on this page ships as one pack, both tiers, tested end to end — the schema, the seed endpoint, the single-token tools, the tenant-scoped tools, the discovery gate, the token lifecycle routes, and the OIDC variant. Fork it, set two variables, deploy.

Known limits, so you're not surprised

Tokens are long-lived bearers. MCP clients today authenticate with a static bearer pasted into config — there's no interactive OAuth flow yet. Keep exp short and rely on the denylist for revocation. One statement per action on Postgres — the driver prepares the query, and a prepared statement holds one command. Use multi: true for a multi-statement DDL block (engine ≥ 0.196.0). HTTP responses are wrapped in a {"data":{"<Action>":{"data": …}}} action trace, which is why the curl examples pipe through jq. MCP clients parse the tool result for you.

Related packs