使用说明
Bear MCP Server
An MCP server that gives Claude Code access to Bear notes on macOS.
Architecture
Reads go straight to Bear's SQLite store, writes go through Bear's URL scheme:
| Operation | Method | Why |
|---|---|---|
| Read (search, get, list) | SQLite | A search is one query, with no round-trip through Bear's UI |
| Write (create, append, prepend, trash, archive, rename-tag, delete-tag) | URL scheme | Bear owns the database, so writes go through its own API |
Reads and writes have different guarantees, and it is worth knowing which is which before trusting a tool response. See Behaviour notes.
Requirements
Installation
cd bear-notes-mpc-server
bun install
Claude Code configuration
Add the MCP server using the Claude Code CLI:
claude mcp add --transport stdio --scope user bear -- bun run /absolute/path/to/bear-mcp-server/src/server.ts
Note: Replace the path with the actual absolute path to your
bear-mcp-serverdirectory.
Scope options:
--scope user- Available in all Claude Code sessions (recommended)--scope local- Only available in the current project directory--scope project- Creates.mcp.jsonfor team sharing via git
Check configured servers:
claude mcp list
View server details:
claude mcp get bear
Starting Claude with Bear
Once the MCP server is configured, start a new Claude session:
claude
Claude starts the Bear MCP server itself, and the Bear tools show up in the session.
Removing the MCP server
The MCP server runs as a subprocess of Claude Code and stops when you exit Claude.
To permanently remove the server:
claude mcp remove bear
Available tools
| Tool | Description |
|---|---|
bear_create_note |
Create a new note with optional tags |
bear_search |
Search notes by text or tag (paged) |
bear_get_note |
Get full content of a note by ID, including its trashed/archived status |
bear_append |
Append text to an existing note |
bear_prepend |
Prepend text to the beginning of an existing note |
bear_replace_content |
Replace the entire content of an existing note |
bear_list_tags |
List all tags with note counts |
bear_list_by_tag |
List notes with a specific tag (paged) |
bear_rename_tag |
Rename an existing tag |
bear_delete_tag |
Delete an existing tag from all notes |
bear_trash_note |
Move a note to trash |
bear_archive_note |
Archive a note |
bear_list_archived |
List archived notes (paged) |
There is no un-archive tool. Bear's x-callback-url API has no unarchive
action, so this server cannot do it. Un-archive in Bear's own UI.
Example usage in Claude
Once the server is running, you can ask Claude things like:
- "Search my Bear notes for 'project ideas'"
- "List all my tags in Bear"
- "Create a new note titled 'Meeting Notes' with today's date"
- "Show me all notes tagged with 'work'"
- "Append this summary to my 'Daily Log' note"
- "Rename my 'old-project' tag to 'archived-project'"
- "Delete the 'temp' tag from all notes"
Database location
The server tries these two locations in order and uses the first that exists:
- iCloud sync:
~/Library/Group Containers/9K33E3U3T4.net.shinyfrog.bear/Application Data/database.sqlite - Local storage:
~/Library/Containers/net.shinyfrog.bear/Data/Documents/Application Data/database.sqlite
Behaviour notes
Writes are sent, not confirmed. Every write goes out as a bear://
URL opened with open -g, which exits as soon as macOS finds a handler for
the scheme. Bear reports nothing back, so the server cannot tell an applied
change from one Bear ignored (an unknown note id, for instance). Write tools
therefore say what was sent, never that it was applied. Two failures the
server does see come back as error results: Bear not installed, and a payload
too large to send. If a write matters, verify it in Bear, or read the note back
with bear_get_note.
The server refuses runaway payloads rather than truncating them. It will
not send a bear:// URL longer than 500000 characters (measured after
percent-encoding, which inflates non-ASCII text up to 3x), and the tool returns
an error naming the size. The number comes from the only ceiling that can be
measured: the argv limit open inherits, ARG_MAX = 1048576. It sits roughly
10x above the largest note observed in a real Bear library. Bear's own limit, if it has one,
is undocumented. Without the guard an oversized write would be silently
truncated, which under bear_replace_content means overwriting a note with a
partial copy. Split very large content across several bear_append calls.
Timestamps are ISO-8601 UTC. createdAt and modifiedAt come back as
2026-07-27T06:09:00Z. The trailing Z is deliberate: an unmarked
2026-07-27 06:09:00 reads as local time and is off by the UTC offset, which
shifts the date for notes written in the first hours of the day.
Search is case-insensitive for all characters, and literal. MÖTE finds
möte, and 50% finds the literal text 50% rather than acting as a
wildcard. Case folding happens in JS because Bun's SQLite only folds ASCII.
The same applies to tag names, so bear_list_tags groups Work and work
into one entry rather than reporting two with split counts.
Tags may be written with or without #. work, #work and Bear's own
multiword form #my tag# all name the same tag, on both the read and the
write path.
List results are paged. bear_search, bear_list_by_tag and
bear_list_archived return { notes, count, limit, offset, hasMore }. count
is the size of that page, never a total; hasMore says whether further notes
matched. Pass limit and offset to page through the rest.
Testing
bun run typecheck
bun test # in-memory SQLite fixture, no Bear needed
Both run in CI on every push to master and every pull request, along with
bun run build. CI runs on Linux, which is fine because nothing in the unit
suite needs macOS or Bear, and a green run says nothing about whether Bear
accepts what this server sends it.
bun test proves the code agrees with its fixture, not with Bear. For changes to
the read queries, the write path, the tool surface or the note rendering, also
walk docs/TEST-PROTOCOL.md, or run it as the repo-local
skill /bear-protocol in Claude Code: it creates, reads, modifies, tags,
archives and trashes one real test note through the connected MCP server, and
verifies every write with a raw read-only query against Bear's database rather
than through this repo's own read path.
Logs
The server writes logs to stderr, and Claude Code captures them. It reads no environment variables and has no log-level setting.
Permissions
On first run, macOS may ask for Automation permissions to allow the server to open Bear URLs.
Troubleshooting
"Bear database not found"
- Make sure Bear is installed and has been opened at least once
Notes not appearing after create
- Bear may take a moment to sync. The note is created via URL scheme and may not immediately appear in SQLite queries.
Permission denied on database
- The database is opened in read-only mode. If you still get errors, check that Bear isn't currently writing to the database.
MCP server not connecting
- Verify the path in your config is correct
- Run
claude mcp listto check if the server is configured - Ensure Bun is installed and available in your PATH
"Unsupported Bear database schema"
- Bear's note/tag join table is named after Core Data's generated entity ids,
and those can change between Bear versions. The server discovers them from
Z_PRIMARYKEYand fails with this message when the tables it needs are not where the discovery says they should be. Please open an issue with the Bear version, since it means the schema moved.