npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

oh-my-notionmcp

v1.2.0

Published

Fast unified Notion MCP on macOS: local-cache-first reads, official fallback, official writes

Readme

Oh My Notion MCP

Ask your AI agent to read a Notion page. Here's what the official Notion MCP sends back for a paragraph that says "Hello world":

{
  "object": "block",
  "id": "a1b2c3d4-...",
  "parent": { "type": "page_id", "page_id": "..." },
  "created_time": "2025-01-15T10:30:00.000Z",
  "last_edited_time": "2025-01-15T10:30:00.000Z",
  "created_by": { "object": "user", "id": "..." },
  "last_edited_by": { "object": "user", "id": "..." },
  "has_children": false,
  "archived": false,
  "in_trash": false,
  "type": "paragraph",
  "paragraph": {
    "rich_text": [{
      "type": "text",
      "text": { "content": "Hello world", "link": null },
      "annotations": {
        "bold": false, "italic": false, "strikethrough": false,
        "underline": false, "code": false, "color": "default"
      },
      "plain_text": "Hello world",
      "href": null
    }],
    "color": "default"
  }
}

That's ~500 bytes of JSON to say "Hello world". Not bold, not italic, not strikethrough, not underline, not code, not colored -- the API makes sure you know about every single formatting option this text isn't using. Multiply this by 50 blocks on a page, and your agent just consumed 25KB of context to read what a human would scan in three seconds.

Now do that for every page, every block, every database query in a working session. Every call is a fresh API round-trip. No caching. The same page you read 10 seconds ago? Full network request, full metadata payload, all over again.

oh-my-notionmcp fixes both problems: it caches responses so repeated reads are instant, and when enabled, it reads directly from Notion desktop's local SQLite database -- skipping the network entirely.

The Speedup

First read:    Notion API call, response cached.             ~200-500ms
Same read again (within 30s): cache hit.                     <1ms
With local SQLite enabled: Notion desktop DB query.          <10ms

No metadata stripping (Notion's API format is preserved for compatibility), but you stop paying the network tax over and over for the same data. In a typical AI coding session that hits the same pages repeatedly, most reads become cache hits after the first pass.

How It Works

Two backends run behind a single MCP server alias:

oh-my-notionmcp (router)
|
|-- Fast backend (in-process, zero subprocess overhead)
|   |-- Response cache (30s TTL, 300 entries, LRU)
|   |-- Notion desktop SQLite fast-path (optional)
|   '-- Notion API client
|
'-- Official backend (child process, only when needed)
    '-- mcp-remote -> https://mcp.notion.com/mcp

Reads go through the fast backend first. Cache hit? Done. Cache miss? Try local SQLite. Still no? Hit the API and cache the result. Writes always go through the official backend.

The fast backend runs in-process -- not as a child process. No IPC serialization, no startup cost. The official backend only spins up for writes or when the fast backend can't handle a request.

OpenAPI-Driven Tool Generation

Tools aren't hardcoded. The fast backend reads an OpenAPI spec at startup and generates MCP tools automatically. Adding a new Notion API endpoint means updating a JSON file and restarting -- no code changes.

The Local SQLite Trick

Notion desktop is an Electron app. It keeps a SQLite database at ~/Library/Application Support/Notion/notion.db with your synced workspace data. When enabled, oh-my-notionmcp queries this database directly and transforms the result into the official API response format. Downstream consumers can't tell the difference, but the read happened without touching the network.

Quick Start

# 1. Install
npm install -g oh-my-notionmcp

# 2. Add to your project
oh-my-notionmcp install --project /path/to/project --name notion

# 3. Authenticate with official Notion MCP
oh-my-notionmcp login
# Complete browser auth, then Ctrl+C after "Proxy established"

# 4. Verify everything works
oh-my-notionmcp doctor --project /path/to/project --name notion

Requirements

  • macOS (Notion desktop cache path is macOS-specific)
  • Node.js >= 20
  • Notion desktop app installed and opened at least once (for local SQLite fast-path)
  • Official Notion MCP OAuth session for writes and fallback reads (login command)

Routing Policy

| Operation | Route | |---|---| | Read (tool exists in both backends) | Fast backend first, official fallback on error | | Read (official only) | Try fast equivalent, then official | | Write | Official MCP only | | Official unavailable | Degraded read-only mode from fast backend |

Commands

oh-my-notionmcp serve                                    # Router mode (reads + writes)
oh-my-notionmcp serve-fast [--transport <stdio|http>]    # Standalone fast read-only server
oh-my-notionmcp install [--project <dir>] [--name <name>]
oh-my-notionmcp login
oh-my-notionmcp doctor [--project <dir>] [--name <name>] [--allow-missing-auth]
oh-my-notionmcp reauth                                   # Clear OAuth token cache

serve-fast (standalone)

Run just the fast read-only server without the router or official backend:

export NOTION_TOKEN="ntn_****"
oh-my-notionmcp serve-fast

# or with HTTP transport
oh-my-notionmcp serve-fast --transport http --port 3000

Read-Only Tools

Auto-generated from the OpenAPI spec. Currently 13 read operations:

| Tool | Description | |---|---| | get-block-children | Retrieve block children | | get-self | Retrieve your token's bot user | | get-user | Retrieve a user | | get-users | List all users | | list-data-source-templates | List templates in a data source | | post-search | Search by title | | query-data-source | Query a data source | | retrieve-a-block | Retrieve a block | | retrieve-a-comment | Retrieve comments | | retrieve-a-data-source | Retrieve a data source | | retrieve-a-database | Retrieve a database | | retrieve-a-page | Retrieve a page | | retrieve-a-page-property | Retrieve a page property item |

Response Cache

  • 30-second TTL, max 300 entries, LRU eviction
  • Cache key = operation + params + auth fingerprint + base URL
  • Only successful responses are cached
  • Pass __mcpFastForceRefresh: true in tool arguments to bypass cache for a single request
  • Persisted to disk at ~/.cache/oh-my-notionmcp/read-cache-v1.json

Notion Desktop SQLite Fast-Path

Disabled by default. Enable it if you run Notion desktop on the same machine:

export NOTION_MCP_FAST_LOCAL_APP_CACHE_ENABLED=true
export NOTION_MCP_FAST_LOCAL_APP_CACHE_TRUST_ENABLED=true

Supported: retrieve-a-page, retrieve-a-block, get-block-children. Freshness depends on Notion desktop sync state. Incomplete snapshots fall back to the API.

Example .mcp.json

{
  "mcpServers": {
    "notion": {
      "command": "node",
      "args": ["/abs/path/to/oh-my-notionmcp/bin/cli.mjs", "serve"],
      "env": {
        "OHMY_NOTION_OFFICIAL_COMMAND": "node",
        "OHMY_NOTION_OFFICIAL_ARGS_JSON": "[\"/abs/path/to/mcp-remote/dist/proxy.js\",\"https://mcp.notion.com/mcp\",\"--transport\",\"http-first\"]",
        "OHMY_NOTION_OFFICIAL_ENV_JSON": "{}"
      }
    }
  }
}

Environment Variables

Router

| Variable | Purpose | |---|---| | OHMY_NOTION_ALLOW_NPX_FALLBACK | Allow npx fallback for official backend | | OHMY_NOTION_OFFICIAL_COMMAND | Official backend command | | OHMY_NOTION_OFFICIAL_ARGS_JSON | JSON array of official backend args | | OHMY_NOTION_OFFICIAL_ENV_JSON | JSON object env for official backend | | OHMY_NOTION_OFFICIAL_CWD | Working directory for official backend |

Fast Backend

| Variable | Default | Purpose | |---|---|---| | NOTION_TOKEN | | Integration token (for standalone serve-fast) | | OPENAPI_MCP_HEADERS | | JSON string with Notion API headers | | NOTION_MCP_FAST_CACHE_ENABLED | true | Enable/disable response cache | | NOTION_MCP_FAST_CACHE_TTL_MS | 30000 | Cache TTL in milliseconds | | NOTION_MCP_FAST_CACHE_MAX_ENTRIES | 300 | Max cache entries | | NOTION_MCP_FAST_LOCAL_APP_CACHE_ENABLED | false | SQLite fast-path gate | | NOTION_MCP_FAST_LOCAL_APP_CACHE_TRUST_ENABLED | false | SQLite trust gate |

Security

  • --fast-token blocked to prevent secret persistence in .mcp.json
  • Sensitive keys redacted from env JSON before install persists config
  • Child processes run with restricted env allowlist
  • npx fallback disabled by default
  • HTTP transport requires bearer token auth by default

Troubleshooting

OAuth Token Not Found / Expired

FAIL: OAuth token cache not found

Run oh-my-notionmcp login to initialize or refresh the OAuth token. Complete browser authentication, then press Ctrl+C after seeing "Proxy established".

If the token exists but is expired, the server will hint: "Token may be expired — try oh-my-notionmcp login".

mcp-remote Not Installed

npx fallback for official backend is disabled

Install mcp-remote as a local dependency:

npm install mcp-remote

Or enable npx fallback (not recommended for production):

export OHMY_NOTION_ALLOW_NPX_FALLBACK=true

Connection Timeout

If the official backend takes more than 30 seconds to connect, the router enters degraded mode (read-only via fast backend). Check:

  1. Network connectivity to mcp.notion.com
  2. OAuth token validity (oh-my-notionmcp doctor)
  3. mcp-remote installation (oh-my-notionmcp doctor)

Re-authenticating (Token Refresh)

If you need to force a fresh OAuth login (expired tokens, wrong account, scope issues):

Option 1: Via MCP tool (while server is running)

Use the oh-my-notionmcp-reauth tool from your MCP client (e.g., Claude). This clears cached tokens and reconnects automatically.

Option 2: Via CLI

oh-my-notionmcp reauth   # Clear cached tokens
oh-my-notionmcp login     # Re-authenticate with Notion
oh-my-notionmcp doctor    # Verify everything works

Backend Reconnection

If the official backend process crashes during operation, the router automatically attempts one reconnect (10-second timeout). If reconnect fails, subsequent calls return an error with both the original and reconnect failure reasons.

Other Issues

FAIL: missing .mcp.json -- Run install --project /your/project.

Reads seem stale -- Open Notion desktop to refresh local cache. Empty/error local reads fall back to official MCP automatically.

Development

npm ci
npm run build
npm test
node bin/cli.mjs --help

Release notes: CHANGELOG.md

License

MIT