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

@squirrelnotes.app/mcp

v1.31.2

Published

MCP stdio CLI for Squirrel Notes — bridges AI agents to the encrypted note-taking API

Readme

Squirrel Notes MCP Server

An MCP (Model Context Protocol) server that lets AI agents (Claude Desktop, Cursor, etc.) read and write your Squirrel Notes, with full end-to-end decryption.


⚠️ Security Caveat — Read This First

The MCP server necessarily decrypts your notes — that is its job. The security implications depend on which transport you use:

stdio (local process) — recommended

The passphrase lives in claude_desktop_config.json on your machine and is passed as an env var to the local process. It never leaves your machine, and decryption happens locally — similar to the standard Squirrel Notes app, where the passphrase is entered locally in the browser and not sent to the server. Compared with the Remote HTTP/Lambda transport below, stdio avoids sending your passphrase over the network, at the cost of storing it in a local config file.

| Risk | Mitigation | |------|------------| | Passphrase in config file | Stored on disk in a local config file; protect it with OS user permissions and full-disk encryption | | Passphrase in process memory | Env var; cleared when process exits | | Decrypted content visible to AI | This is the intended behaviour — AI agents need to read your notes |

Remote HTTP (Lambda)

Your passphrase is sent over HTTPS to a Lambda function on every call.

| Risk | Mitigation | |------|------------| | Passphrase sent over the network | HTTPS-only (API Gateway always TLS) | | Passphrase in server RAM during invocation | Stack-scoped variable; never assigned to module state; GC'd after invocation | | Passphrase in CloudWatch logs | Lambda code never logs headers | | Lambda container reuse retaining secrets | Passphrase is NOT module-scope — reused containers hold no residual secret |

You must trust: AWS Lambda, the self-hosted MCP server code, and anyone with access to your CloudWatch logs.

If this trade-off is not acceptable, use stdio transport instead.


Tools

Notes

| Tool | Description | |------|-------------| | list_notes | Returns all notes with decrypted titles, tags, and metadata. Supports optional collectionId, tagId, pinned filters. | | get_recent_notes | Returns the N most-recently updated notes (default 10). Fast — titles only. | | get_note | Returns a single note with decrypted title + full content. | | create_note | Creates a note with encrypted title + content. | | update_note | Updates title, content, or pinnedAt on a note. | | delete_note | Soft-deletes a note (recoverable). | | pin_note | Pins or unpins a note (appears at top of sidebar). | | append_to_note | Appends markdown content to an existing note. Avoids race conditions vs get → update. | | set_note_tags | Replaces all tags on a note. | | search_notes | Fast title-only search across all notes. | | search_note_content | Full-content search (slow — decrypts all notes). Use only when title search fails. | | move_note_to_collection | Moves a note to a collection (or to scratchpad if collectionId is null). |

Saved Searches (Pro)

| Tool | Description | |------|-------------| | list_saved_searches | Returns saved searches with decrypted names and smart-query strings. | | create_saved_search | Creates a saved search from plaintext name + query (encrypted before API call). | | update_saved_search | Updates saved search name and/or query. | | delete_saved_search | Deletes a saved search (soft-delete server-side). |

Attachments

| Tool | Transport | Description | |------|-----------|-------------| | list_attachments | All | Lists all attachments for a note with decrypted filenames and metadata. | | get_attachment_download_url | All | Returns a presigned S3 download URL (expires in 5 minutes). | | delete_attachment | All | Permanently deletes an attachment from S3 and the database. | | upload_attachment_from_path | STDIO only | Uploads a file from a local path — no file bytes in context. Use after write_file (Filesystem MCP) to attach Claude-generated files. See Filesystem MCP companion below. |


Filesystem MCP Companion (STDIO only)

Combining the Squirrel Notes MCP server with the MCP Filesystem server enables a two-step pipeline for attaching files generated by Claude — without putting file bytes in the context window:

Claude generates content
        │
        ▼  write_file (Filesystem MCP) ──► file lands on disk
                                                  │
                                                  ▼  upload_attachment_from_path (Squirrel MCP)
                                                     reads bytes, encrypts, uploads to S3
                                                     (only a path string in context)

Claude Desktop configuration

Add both servers to claude_desktop_config.json:

{
  "mcpServers": {
    "squirrel-notes": {
      "command": "npx",
      "args": ["-y", "@squirrelnotes.app/mcp"],
      "env": {
        "SQUIRREL_API_BASE_URL": "https://api.squirrelnotes.app",
        "SQUIRREL_API_KEY": "sqn_your_api_key",
        "SQUIRREL_PASSPHRASE": "your-passphrase",
        "SQUIRREL_UPLOAD_ROOT": "/tmp"
      }
    },
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
      "env": {}
    }
  }
}

SQUIRREL_UPLOAD_ROOT restricts which directories upload_attachment_from_path may read from. Defaults to os.tmpdir()/tmp on macOS/Linux, %TEMP% equivalent on Windows. Set to a comma-separated list for multiple allowed roots (e.g. "/tmp,/Users/you/Downloads"). This is a security guardrail — without it, a prompt-injection attack could trick Claude into uploading files from anywhere on disk (e.g. ~/.ssh/id_rsa). Note: symlinks inside an allowed root that point outside it are not followed during the check — use OS-level controls if that is a concern.

Example workflows

Attach a Claude-generated CSV report to a note:

"Create a monthly expense summary as a CSV and attach it to my Finances note."

  1. Claude generates the CSV content → write_file("/tmp/expenses.csv", content) (Filesystem MCP)
  2. Claude calls upload_attachment_from_path with localPath="/tmp/expenses.csv" (Squirrel MCP)

Attach an xlsx file generated by a script:

"Write a Python script to generate an xlsx report and attach it to my Analytics note."

  1. Claude writes the script to /tmp/generate_report.py → executes it → report.xlsx is written to /tmp/
  2. Claude calls upload_attachment_from_path with localPath="/tmp/report.xlsx"

Bulk-import Markdown files from disk:

"Import all the markdown files from /tmp/obsidian-export/ as notes in my Archive collection."

  1. Claude calls list_directory("/tmp/obsidian-export/") (Filesystem MCP) to enumerate files
  2. For each .md file: read_file(path)create_note with the content (Squirrel MCP)

Export a note to disk:

"Export my project roadmap note as a Markdown file to /tmp/roadmap.md."

  1. Claude calls get_note(id) (Squirrel MCP) to read the note
  2. Claude calls write_file("/tmp/roadmap.md", content) (Filesystem MCP)

Every request requires two headers:

Authorization: Bearer sqn_<api_key>
X-Passphrase: <your_passphrase>

Generate an API key in Squirrel Notes → Settings → API Key (requires Pro tier).

Neither header is ever stored, cached, or logged by the server.


Setup

Which setup should I use?

  • Claude Desktop → use stdio (recommended) — no OAuth, passphrase stays on your machine
  • API automation / Cursor / other agents → use Remote HTTP (Lambda)

Setup — Claude Desktop (stdio) ✅ Recommended

The stdio transport runs the MCP server as a local process on your machine. No OAuth, no network headers, no AWS account needed. Your passphrase is set as an env var in the config file and never leaves your machine.

Option A — npx (zero install, recommended)

No download or build step required. npx fetches the latest published version automatically on first run and caches it locally.

1. Configure Claude Desktop

Open ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows) and add:

{
  "mcpServers": {
    "squirrel-notes": {
      "command": "npx",
      "args": ["-y", "@squirrelnotes.app/mcp"],
      "env": {
        "SQUIRREL_API_BASE_URL": "https://api.squirrelnotes.app",
        "SQUIRREL_API_KEY": "sqn_<your_api_key>",
        "SQUIRREL_PASSPHRASE": "<your_passphrase>"
      }
    }
  }
}

2. Restart Claude Desktop

Quit and reopen Claude Desktop. The Squirrel Notes MCP server will start automatically.

3. Verify

Ask Claude: "List my Squirrel Notes" — you should see your note titles.

Option B — build from source (local development / pinned version)

Use this if you want to run a specific local build or contribute to the server code.

cd mcp
npm install
npm run build:stdio
# Produces dist/stdio-handler.js

Then reference the built file directly:

{
  "mcpServers": {
    "squirrel-notes": {
      "command": "node",
      "args": ["/absolute/path/to/squirrel-notes/mcp/dist/stdio-handler.js"],
      "env": {
        "SQUIRREL_API_BASE_URL": "https://api.squirrelnotes.app",
        "SQUIRREL_API_KEY": "sqn_<your_api_key>",
        "SQUIRREL_PASSPHRASE": "<your_passphrase>"
      }
    }
  }
}

Windows: Replace the path with C:\\path\\to\\mcp\\dist\\stdio-handler.js (double backslashes).


Setup — Remote HTTP (Lambda)

Use this if you want to connect agents or tools that support remote HTTP MCP endpoints (Cursor, custom automations, etc.). This requires deploying to AWS and sending the passphrase over HTTPS on every call.

1. Deploy with CDK

cd infra
npx cdk deploy SquirrelMcp

This creates:

  • A Lambda function running the MCP server Docker image
  • An HTTP API Gateway at https://<id>.execute-api.<region>.amazonaws.com/mcp

The deployed URL is printed as a CDK output (McpApiUrl).

2. Configure your client

{
  "mcpServers": {
    "squirrel-notes": {
      "url": "https://<your-mcp-api-url>/mcp",
      "headers": {
        "Authorization": "Bearer sqn_<your_api_key>",
        "X-Passphrase": "<your_passphrase>"
      }
    }
  }
}

Note: Newer versions of Claude Desktop enforce OAuth 2.0 for remote HTTP servers and will reject this config. Use the stdio setup above for Claude Desktop.

3. Verify

Ask Claude: "List my Squirrel Notes" — you should see decrypted note titles.


Common Workflows

Once configured, you can speak to Claude naturally. Some examples:

| What you say | What Claude does | |---|---| | "What have I been working on lately?" | get_recent_notes → lists last 10 updated notes | | "Show me everything in my Work collection" | list_collectionslist_notes(collectionId: ...) | | "Search my notes for 'project roadmap'" | search_notes (title match) or search_note_content | | "Create a note called 'Meeting Notes' in my Work collection" | list_collectionscreate_note(collectionId: ...) | | "Add today's standup to my Daily Log note" | search_notes("Daily Log")append_to_note(id, ...) | | "Pin my TODO note" | search_notes("TODO")pin_note(id, true) | | "Tag my 'Project Roadmap' note as urgent" | list_tagssearch_notesset_note_tags | | "Show me all my pinned notes" | list_notes(pinned: true) | | "Show me all notes tagged 'urgent'" | list_tagslist_notes(tagId: ...) |

Tip for Claude Desktop: Add a system prompt in your Claude Project describing your note structure (e.g. "I use a collection called 'Work' for work notes and 'Personal' for personal notes, and tags like 'urgent' and 'later' for priority"). This reduces the number of lookup calls Claude needs to make.


Local Development

cd mcp
npm install
npm run dev

This starts an HTTP MCP server at http://localhost:3002 pointing at the local backend (http://localhost:3001). No build step needed — tsx runs TypeScript directly.

Smoke test with curl:

curl -s http://localhost:3002 -X POST \
  -H "Authorization: Bearer sqn_<your_api_key>" \
  -H "X-Passphrase: <your_passphrase>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | jq .

Or use the MCP Inspector for a browser UI:

npx @modelcontextprotocol/inspector http://localhost:3002

Debugging / Getting Logs

stdio transport (Claude Desktop)

The MCP protocol uses stdout for JSON-RPC messages, so all diagnostic output goes to stderr instead. The MCP server writes errors and key upload steps to stderr automatically.

Where to find the logs

macOS / Linux Claude Desktop captures stderr from each MCP server in a dedicated log file:

~/Library/Logs/Claude/mcp-server-squirrel-notes.log   # macOS

Open this file in any text editor or tail it in a terminal:

tail -f ~/Library/Logs/Claude/mcp-server-squirrel-notes.log

Windows

%APPDATA%\Claude\logs\mcp-server-squirrel-notes.log

Write logs to a custom file

Set SQUIRREL_LOG_FILE to an absolute path in your claude_desktop_config.json to capture all stderr output to a file of your choice:

{
  "mcpServers": {
    "squirrel-notes": {
      "command": "npx",
      "args": ["-y", "@squirrelnotes.app/mcp"],
      "env": {
        "SQUIRREL_API_BASE_URL": "https://api.squirrelnotes.app",
        "SQUIRREL_API_KEY": "sqn_<your_api_key>",
        "SQUIRREL_PASSPHRASE": "<your_passphrase>",
        "SQUIRREL_LOG_FILE": "/tmp/squirrel-mcp.log"
      }
    }
  }
}

Then in a terminal:

tail -f /tmp/squirrel-mcp.log

What's logged

  • S3 PUT failures (HTTP status + response body from S3)
  • Fatal startup errors (missing env vars, wrong passphrase)

Environment Variables

stdio transport

| Variable | Required | Description | |----------|----------|-------------| | SQUIRREL_API_BASE_URL | ✅ | Base URL of the Squirrel Notes backend API (e.g. https://api.squirrelnotes.app) | | SQUIRREL_API_KEY | ✅ | Your API key from Settings → API Key (requires Pro tier) | | SQUIRREL_PASSPHRASE | ✅ | Your encryption passphrase — never leaves your machine | | SQUIRREL_LOG_FILE | — | Absolute path to a log file. When set, all stderr output is also appended to this file. |

Lambda (HTTP) transport

| Variable | Description | |----------|-------------| | SQUIRREL_API_BASE_URL | Base URL of the Squirrel Notes backend API |

For Lambda, the API key and passphrase are passed per-request via Authorization and X-Passphrase headers — not environment variables.