Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Next.js Todo MCP Serveradd 'Finish the documentation' to my todo list"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-server
MCP server for integration with a Next.js AI chat. Built with Hono + Node.js, using Streamable HTTP transport (MCP spec 2025-03-26).
Target | Adapter | Persistence |
Render / Fly.io |
| ❌ lost on restart |
Cloudflare Workers |
| ✅ persisted in D1 SQLite |
Quickstart (Node.js / local)
cd mcp-server
npm install
cp .env.example .env # leave everything empty for dev
npm run dev
# → http://localhost:3001/mcpSet this in Next.js .env.local:
MCP_URL=http://localhost:3001/mcpStructure
mcp-server/
├── src/
│ ├── index.ts # Node.js entry point (Render / Fly.io) — InMemoryAdapter
│ ├── worker.ts # Cloudflare Workers entry point — D1Adapter
│ ├── auth.ts # Extract userId from Bearer + JWT headers
│ ├── tools.ts # 5 MCP tools (list, add, complete, update, delete)
│ └── db/
│ ├── adapter.ts # TodoDB interface
│ ├── memory.ts # InMemoryAdapter
│ └── d1.ts # D1Adapter (Cloudflare Workers only)
├── migrations/
│ └── 0001_init.sql # D1 schema
├── wrangler.toml # Cloudflare config
├── Dockerfile # For Render / Fly.io
├── fly.toml
└── render.yamlTools
Tool | Description | Input |
| Display all todos |
|
| Add a new todo |
|
| Mark as completed |
|
| Change the title |
|
| Delete a todo |
|
Auth
Two layers, both optional — leave empty for dev (fallback X-User-Id):
# .env
MCP_TOKEN= # Bearer token — verifies service identity
MCP_JWT_SECRET= # JWT secret — must match Next.jsTypeScript
This project requires "module": "NodeNext" and "moduleResolution": "NodeNext" in tsconfig.json. The MCP SDK exposes McpServer via a package.json exports wildcard — older settings like "moduleResolution": "bundler" with "module": "ESNext" resolve imports in CJS mode and silently skip the exports map, causing:
Cannot find module '@modelcontextprotocol/sdk/server/mcp.js'NodeNext reads the project package.json "type": "module" field, resolves in ESM mode, and correctly follows the exports wildcard.
Developer Guide
Add a new tool
Edit src/tools.ts, add server.registerTool(...) inside the registerTools function.
The tool will automatically be available in all adapters because db: TodoDB is passed from outside.
Manual testing with curl
The MCP Streamable HTTP transport (spec 2025-03-26) requires every POST request to include:
Accept: application/json, text/event-streamThe server needs to know the client can handle either a direct JSON response or an SSE stream — omitting this header returns a -32000 Not Acceptable error.
When MCP_TOKEN and MCP_JWT_SECRET are unset, auth falls back to the plain X-User-Id header.
# Health check
curl http://localhost:3001/
# List tools
curl -s -X POST http://localhost:3001/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "X-User-Id: dev-user" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq
# list_todos (all)
curl -s -X POST http://localhost:3001/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "X-User-Id: dev-user" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list_todos","arguments":{}}}' | jq
# list_todos (filter: pending | done)
curl -s -X POST http://localhost:3001/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "X-User-Id: dev-user" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"list_todos","arguments":{"filter":"pending"}}}' | jq
# add_todo
curl -s -X POST http://localhost:3001/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "X-User-Id: dev-user" \
-d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"add_todo","arguments":{"title":"Learn MCP"}}}' | jq
# complete_todo
curl -s -X POST http://localhost:3001/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "X-User-Id: dev-user" \
-d '{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"complete_todo","arguments":{"id":"1"}}}' | jq
# update_todo
curl -s -X POST http://localhost:3001/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "X-User-Id: dev-user" \
-d '{"jsonrpc":"2.0","id":6,"method":"tools/call","params":{"name":"update_todo","arguments":{"id":"1","title":"Review PR"}}}' | jq
# delete_todo
curl -s -X POST http://localhost:3001/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "X-User-Id: dev-user" \
-d '{"jsonrpc":"2.0","id":7,"method":"tools/call","params":{"name":"delete_todo","arguments":{"id":"1"}}}' | jqAuth header combinations
|
| Required headers |
❌ | ❌ |
|
✅ | ❌ |
|
❌ | ✅ |
|
✅ | ✅ |
|
Or use MCP Inspector:
npx @modelcontextprotocol/inspector http://localhost:3001/mcpAdd a D1 migration
# Create a new file in migrations/
echo "ALTER TABLE todos ADD COLUMN priority INTEGER DEFAULT 0;" \
> migrations/0002_add_priority.sql
# Apply locally
npm run cf:migrate:local
# Apply to production
npm run cf:migrateDeployment
Render
Push the repo to GitHub (make sure
mcp-server/render.yamlexists)Render dashboard → New → Blueprint → connect the repo
Set env vars:
MCP_TOKEN,MCP_JWT_SECRETDeploy
MCP URL:
https://mcp-server.onrender.com/mcp
⚠️ Free tier sleeps after 15 minutes of idle time — the first request may take ~30 seconds to wake up.
Fly.io
# Install flyctl: https://fly.io/docs/hands-on/install-flyctl/
fly auth login
cd mcp-server
# First deployment — reads fly.toml and creates the app
fly launch --no-deploy
# Set secrets
fly secrets set MCP_TOKEN=$(openssl rand -hex 32)
fly secrets set MCP_JWT_SECRET=the-same-value-as-nextjs
# Deploy
fly deployUpdate after changes:
fly deployMCP URL:
https://mcp-server.fly.dev/mcp
Cloudflare Workers + D1
1. Initial setup
npm install
npx wrangler login2. Create a D1 database
npx wrangler d1 create todos-dbThe output will display a database_id. Copy and paste it into wrangler.toml:
[[d1_databases]]
binding = "TODOS_DB"
database_name = "todos-db"
database_id = "PASTE_ID_HERE"3. Run migrations
# Local dev
npm run cf:migrate:local
# Production
npm run cf:migrate4. Set secrets
npx wrangler secret put MCP_TOKEN
# → enter the value when prompted
npx wrangler secret put MCP_JWT_SECRET
# → enter the SAME value as Next.js MCP_JWT_SECRET5. Local development with D1
npm run cf:dev
# → http://localhost:8787/mcp (using local D1 via .wrangler/)6. Deploy
npm run cf:deployMCP URL:
https://mcp-server.your-subdomain.workers.dev/mcp
One-click deploy to Cloudflare
Note: Cloudflare one-click deploy cannot automatically set up D1 — you still need to perform steps 2–4 manually after deployment.
Next.js Integration
# .env.local
# Choose one:
MCP_URL=http://localhost:3001/mcp
MCP_URL=https://mcp-server.onrender.com/mcp
MCP_URL=https://mcp-server.fly.dev/mcp
MCP_URL=https://mcp-server.your-subdomain.workers.dev/mcp
# Auth — must match the MCP server
MCP_TOKEN=your-mcp-token
MCP_JWT_SECRET=your-jwt-secretUpdate to — SSE → Streamable HTTP
This server uses Streamable HTTP. Update the transport:
// Before
transport: { type: "sse", url, headers }
// After
transport: { type: "http", url, headers }Testing checklist
curl http://localhost:3001→{"status":"ok","adapter":"memory"}Chat: “Show all todos” → calls
list_todosChat: “Add todo: learn MCP” → calls
add_todoChat: “Mark todo #1 as done” → calls
complete_todoChat: “Change todo #1 to: review PR” → calls
update_todoChat: “Delete todo #1” → calls
delete_todoCheck Sentry — no
mcp:connectormcp:closeerrors
This server cannot be installed
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.