notion-mcp-hono
Provides tools to search, retrieve, create, and update Notion pages and databases, enabling AI agents to manage Notion content programmatically.
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., "@notion-mcp-honosearch for pages about project roadmap"
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.
notion-mcp-hono
A remote MCP server exposing Notion as tools — TypeScript, Hono, the official MCP TypeScript SDK over Streamable HTTP, deployed on Railway.
Notion ships an official MCP server; this project is not trying to replace it. It is a portfolio piece demonstrating how to build a remote MCP server properly: focused scope, test-driven, authenticated, observable, and deployed.
Tools
Tool | What it does |
| Search pages across the workspace by title |
| Retrieve one page as clean markdown |
| Create a page under a parent page or database |
| Append markdown to the end of an existing page |
| Query a database with Notion filters and sorts, rows simplified to flat JSON |
All tool inputs are validated with Zod (with size limits), all descriptions are written for the LLM consuming them, and all Notion failures come back as actionable, in-band error messages — never raw API errors.
Related MCP server: MCP Notion Server (@suncreation)
Architecture
┌──────────────────────────────────────────────┐
MCP client │ notion-mcp-hono │
(claude.ai / │ │
Claude Code) │ Transport layer MCP layer │
│ │ ┌───────────────┐ ┌───────────────┐ │
│ POST /mcp │ │ Hono app │ │ McpServer │ │ Notion API
├─────────────────►│ │ bearer auth ├──────►│ 5 tools │ │
│ Authorization: │ │ body limit │ │ zod schemas │ │
│ Bearer <key> │ │ req logging │ │ tool runner │ │
│ │ └───────────────┘ └───────┬───────┘ │
│ GET /health │ │ │ │
├─────────────────►│ 200, no auth ┌────────▼────────┐ │
│ │ │ Notion layer │ │ ┌─────────┐
│ │ │ NotionGateway ├──┼──►│ Notion │
│ │ │ (interface) │ │ └─────────┘
│ │ │ SDK v5 adapter │ │
│ │ └─────────────────┘ │
└──────────────────┴──────────────────────────────────────────────┘Three layers, dependency arrows pointing inward:
Transport (
src/app.ts,src/auth.ts) — Hono routes, bearer auth, body limit, request logging.MCP (
src/mcp/) — server setup, the 5 tool registrations, the tool runner (error handling + per-call logging).Notion (
src/notion/) — theNotionGatewayinterface (domain types only) and its@notionhq/clientadapter. The MCP layer never imports the Notion SDK, so the integration is swappable without touching tool code.
Quickstart (local)
git clone <this repo> && cd notion-mcp-hono
npm install
cp .env.example .env # then fill in the two variables below
npm run dev # http://localhost:3000You need two secrets in .env (or exported):
Variable | What it is |
| An internal integration token from notion.so/my-integrations. Share the pages/databases you want accessible with that integration (page → ⋯ → Connections). |
| The bearer key clients of this server must present. Generate one: |
Check it's alive:
curl http://localhost:3000/health
# {"status":"ok"}
curl -s -X POST http://localhost:3000/mcp \
-H "Authorization: Bearer $MCP_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Authentication
Every request to /mcp requires:
Authorization: Bearer <MCP_API_KEY>Comparison is timing-safe (both sides are SHA-256-hashed, then
crypto.timingSafeEqual— neither content nor length leaks through response timing).Missing/wrong token →
401with a JSON body naming the expected header.GET /healthis public (it's the deployment health check).
Connecting from Claude
claude.ai custom connector
Settings → Connectors → Add custom connector:
URL:
https://<your-deployment>.up.railway.app/mcpAdvanced settings → HTTP headers: add
Authorization=Bearer <MCP_API_KEY>
Claude Code
claude mcp add --transport http notion-hono \
https://<your-deployment>.up.railway.app/mcp \
--header "Authorization: Bearer <MCP_API_KEY>"Then /mcp inside Claude Code shows the connection; the five tools appear as
mcp__notion-hono__*.
Deploy on Railway
Create a project from this repo (Railway dashboard → New Project → Deploy from GitHub repo). The default Nixpacks build works as-is:
npm ci && npm run build && npm start.Set the environment variables on the service:
NOTION_TOKEN— your Notion internal integration tokenMCP_API_KEY— the bearer key for clients (generate:openssl rand -hex 32)
PORTis injected by Railway automatically; the server reads it.Set the service health check path to
/health.Generate a public domain (service → Settings → Networking) and use
https://<domain>/mcpas the connector URL.
The server fails fast at boot with a clear message if either env var is missing.
Smoke test (manual, not CI)
Exercises a running server end-to-end against a real Notion workspace:
MCP_URL=https://<your-deployment>.up.railway.app \
MCP_API_KEY=<key> \
SMOKE_PARENT_PAGE_ID=<page id> # optional: enables create_page + append_blocks \
SMOKE_DATABASE_ID=<database id> # optional: enables query_database \
npm run smokeIt verifies /health, the 401 on unauthenticated /mcp, the initialize handshake,
tools/list, and each tool (write tools only when their env var is set — pages it
creates are titled Smoke test <timestamp> and safe to delete).
Development
npm test # Vitest, all Notion calls mocked — no network
npm run test:watch
npm run typecheck # strict, includes tests and scripts
npm run build # compiles src/ to dist/Tests run at three altitudes: pure functions (property simplification, auth, logger),
the MCP layer via InMemoryTransport with a fake gateway, and full HTTP integration
using the real MCP SDK client against the Hono app on an ephemeral port.
Design decisions
Streamable HTTP, stateless mode. This is a remote server, so stdio is out. Of Streamable HTTP's two modes we run stateless: a fresh
McpServer+ transport per request, noMcp-Session-Id, no session map. Every tool is pure request/response, so sessions would only buy horizontal-scaling problems. ConsequentlyGET/DELETE /mcpreturn405(nothing to stream or terminate).Markdown conversion is delegated to Notion. Notion's 2025-09-03 API has native markdown endpoints (
pages.retrieveMarkdown,pages.updateMarkdown). Hand-rolling a blocks↔markdown converter is an entire class of fidelity bugs this project chose not to own.NotionGatewayinterface as the seam. MCP tools depend on a 5-method interface speaking domain types (markdown strings, flat property values). The@notionhq/clientadapter — including thedatabase_id→ data-source resolution the 2025 API requires — is invisible to tool code and replaceable without touching it.Errors are a UX surface for the model. Zod validation failures and Notion errors return in-band (
isError: true) with messages that say what to fix ("share the page with the integration via ⋯ → Connections"), so the calling LLM can self-correct. Unexpected errors return a generic in-band message and are logged server-side — raw internals never reach the client.Auth is boring on purpose. One static bearer key, timing-safe compare, injected via
createApp({ mcpApiKey })so tests never touchprocess.env. OAuth is the spec's long-term answer for multi-user servers; for a single-workspace personal server it's complexity without benefit.Structured logs to stdout. One JSON line per HTTP request and per tool call (name, outcome, duration — never arguments, which can contain page content). Railway captures stdout;
/healthis excluded so the poller doesn't drown the signal.
License
MIT
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ldamoredev/notion-mcp-hono'
If you have feedback or need assistance with the MCP directory API, please join our Discord server