devonthink-mcp
DEVONthink MCP server for Claude Code. Zero-config setup — one command and you're done.
Extends mcp-server-devonthink with additional tools for smart groups, smart rules, EML header parsing, and column layout management.
Quick start
npx @tekmidian/devonthink-mcp setup
The wizard checks prerequisites, configures ~/.claude.json, and enables the server. Restart Claude Code and DEVONthink tools are immediately available.
What it provides
With this server running, Claude Code can:
- Search and browse all open DEVONthink databases
- Read document content (PDFs, Markdown, plain text, HTML, rich text)
- Create, update, and delete records
- Add and remove tags, classify documents, manage metadata
- Move, replicate, and duplicate records across groups
- Cross-reference emails with archived documents
- Ask AI about documents (DEVONthink's built-in AI)
- List and navigate database groups
- List smart groups — enumerate all smart groups (not accessible via AppleScript)
- List smart rules — enumerate all smart rules (not accessible via AppleScript)
- Parse EML headers — extract Message-ID, In-Reply-To, References, Subject, etc.
- Read/copy column layouts — inspect and copy view column configurations
Requirements
- macOS (DEVONthink is macOS-only)
- DEVONthink 3 or 4 installed and running
- Node.js >= 18
- Claude Code
Installation
Option 1: npx (no install required)
npx @tekmidian/devonthink-mcp setup
Option 2: Global install
npm install -g @tekmidian/devonthink-mcp
devonthink-mcp setup
Option 3: Clone and build
git clone https://github.com/mnott/devonthink-mcp ~/dev/ai/devonthink-mcp
cd ~/dev/ai/devonthink-mcp
npm install
npm run build
node dist/index.js setup
Manual configuration
If you prefer to configure Claude Code manually, add this to the mcpServers section of ~/.claude.json:
"devonthink": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@tekmidian/devonthink-mcp", "serve"],
"env": {}
}
Or if you have it installed locally:
"devonthink": {
"type": "stdio",
"command": "node",
"args": ["/path/to/devonthink-mcp/dist/index.js", "serve"],
"env": {}
}
Restart Claude Code after editing ~/.claude.json.
Usage
Once configured, Claude Code has access to all DEVONthink tools automatically. DEVONthink must be running with at least one database open.
Example prompts:
- "Search my DEVONthink databases for notes about the Q3 budget"
- "Find the email from John about the contract and show me related documents"
- "Create a new markdown note in my Inbox with today's meeting notes"
- "List all documents tagged 'todo' in my Ablegen database"
- "Read the content of the PDF I imported yesterday"
- "List my smart groups"
- "Parse the headers from this .eml file to find its thread ID"
Custom tools
These five tools extend the upstream mcp-server-devonthink capabilities. They are not part of the original package.
Key limitation: Smart groups and smart rules are not accessible via the DEVONthink AppleScript scripting dictionary. The list_smart_groups and list_smart_rules tools are the only programmatic way to enumerate them. They work by reading the plist files directly.
list_smart_groups
Parses ~/Library/Application Support/DEVONthink/SmartGroups.plist and returns all smart groups with their name, UUID, sync date, and UseUUIDKey flag.
Parameters: none
Returns:
success | boolean | Whether the operation succeeded |
smartGroups | array | List of smart group entries |
totalCount | number | Total number of smart groups found |
Each entry in smartGroups:
name | string | Display name of the smart group |
uuid | string | UUID from the sync.UUID field — use this with the search tool |
syncDate | string | null | Last sync date (ISO 8601) |
useUuidKey | boolean | null | Whether DEVONthink uses UUID as the key internally |
Example:
list_smart_groups
{
"success": true,
"totalCount": 4,
"smartGroups": [
{
"name": "Jobs - To Review",
"uuid": "4A469368-94FD-46D3-9A62-ED7C24D822D8",
"syncDate": "2024-11-15T10:23:00Z",
"useUuidKey": true
}
]
}
Use the uuid with the search tool's groupUuid parameter to query the contents of that smart group.
list_smart_rules
Parses ~/Library/Application Support/DEVONthink/SmartRules.plist and returns all smart rules with name, UUID, enabled state, execution metadata, and sync date.
Parameters: none
Returns:
success | boolean | Whether the operation succeeded |
smartRules | array | List of smart rule entries |
totalCount | number | Total number of smart rules found |
Each entry in smartRules:
name | string | Display name of the smart rule |
uuid | string | UUID from the sync.UUID field |
enabled | boolean | null | Whether the rule is currently enabled |
indexOffset | number | null | Order index within the rules list |
lastExecution | number | null | CFAbsoluteTime timestamp of last execution |
syncDate | string | null | Last sync date (ISO 8601) |
useUuidKey | boolean | null | Whether DEVONthink uses UUID as the key internally |
Example:
list_smart_rules
{
"success": true,
"totalCount": 2,
"smartRules": [
{
"name": "Auto-tag Invoices",
"uuid": "B1234567-ABCD-EF01-2345-67890ABCDEF0",
"enabled": true,
"indexOffset": 0,
"lastExecution": 756000000,
"syncDate": "2024-12-01T08:00:00Z",
"useUuidKey": false
}
]
}
Reads an RFC 2822 .eml file and extracts the MIME headers needed for email thread correlation.
Handles CRLF and LF line endings, folded headers (continuation lines), and RFC 2047 encoded words in Subject, From, and To fields.
Only reads the first 64 KB of the file since headers are always at the start.
Parameters:
filePath | string | yes | Absolute path to the .eml file |
Returns:
success | boolean | Whether parsing succeeded |
filePath | string | The path that was read |
messageId | string | null | The Message-ID header value |
inReplyTo | string | null | The In-Reply-To header value |
references | string[] | Array of message IDs parsed from the References header |
subject | string | null | Decoded subject line |
from | string | null | Sender address(es) |
to | string | null | Recipient address(es) |
cc | string | null | CC address(es) |
date | string | null | Date string from the header |
Example:
{
"filePath": "/path/to/email.eml"
}
{
"success": true,
"filePath": "/path/to/email.eml",
"messageId": "<abc123@mail.example.com>",
"inReplyTo": "<xyz789@mail.example.com>",
"references": [
"<original@mail.example.com>",
"<xyz789@mail.example.com>"
],
"subject": "Re: Contract renewal discussion",
"from": "Jane Smith <jane@example.com>",
"to": "John Doe <john@example.com>",
"cc": null,
"date": "Mon, 15 Jan 2024 14:32:00 +0100"
}
get_column_layout
Reads the column layout for a named smart group or smart rule from ~/Library/Preferences/com.devon-technologies.think.plist.
Returns the ordered visible columns, all table view columns (visible and hidden), and column widths. Supports partial name matching — if only one partial match exists, it is used automatically.
DEVONthink stores layouts under three related plist keys per smart group: the visible column order, all columns, and column widths.
Parameters:
name | string | yes | Display name of the smart group or smart rule |
uuid | string | no | UUID of the smart group (fallback if name lookup fails — DEVONthink sometimes stores layouts under the UUID) |
Returns:
success | boolean | Whether a layout was found |
name | string | The name that was searched |
resolvedKey | string | The actual plist key used (may be UUID if name was not found directly) |
columns | string[] | null | Visible columns in display order |
tableViewColumns | string[] | null | All column identifiers (visible + hidden) |
widths | object | null | Map of column identifier to width (float) |
keysFound | string[] | Which of the three plist keys were present |
On failure, the response includes exampleNames (a list of names with saved layouts) and partialMatches when multiple fuzzy matches are found.
Example:
{
"name": "Jobs - To Review"
}
{
"success": true,
"name": "Jobs - To Review",
"resolvedKey": "Jobs - To Review",
"columns": ["name", "date", "from", "subject"],
"tableViewColumns": ["name", "date", "from", "subject", "tags", "size"],
"widths": { "name": 280.0, "date": 120.0, "from": 180.0, "subject": 320.0 },
"keysFound": [
"ListColumnsHorizontal-Jobs - To Review",
"TableView Columns ListColumnsHorizontal-Jobs - To Review",
"TableView Column Widths ListColumnsHorizontal-Jobs - To Review"
]
}
copy_column_layout
Copies the column layout (visible columns, all columns, and column widths) from one smart group or smart rule to another. All three layout keys are written atomically using Python's plistlib so the binary plist format is preserved correctly.
Supports partial name matching for both source and target. DEVONthink must be restarted (or the smart group window closed and reopened) for the change to take effect.
Parameters:
sourceName | string | yes | Name of the source smart group or smart rule (must have a saved layout) |
targetName | string | yes | Name of the target smart group or smart rule |
sourceUuid | string | no | UUID fallback for the source (used if name lookup fails) |
targetUuid | string | no | UUID of the target — if supplied, the layout is written under the UUID key |
Returns:
success | boolean | Whether the copy succeeded |
sourceName | string | The source name provided |
targetName | string | The target name provided |
resolvedSourceKey | string | The plist key used for the source |
resolvedTargetKey | string | The plist key used for the target |
keysCopied | string[] | The plist keys that were written |
message | string | Human-readable summary including the column names that were copied |
Example:
{
"sourceName": "Archivieren - Jobs",
"targetName": "Jobs - To Review"
}
{
"success": true,
"sourceName": "Archivieren - Jobs",
"targetName": "Jobs - To Review",
"resolvedSourceKey": "Archivieren - Jobs",
"resolvedTargetKey": "Jobs - To Review",
"keysCopied": [
"ListColumnsHorizontal-Jobs - To Review",
"TableView Columns ListColumnsHorizontal-Jobs - To Review",
"TableView Column Widths ListColumnsHorizontal-Jobs - To Review"
],
"message": "Copied column layout from \"Archivieren - Jobs\" to \"Jobs - To Review\". Columns: [name, date, from, subject]. Restart DEVONthink or close/reopen the smart group window for the change to take effect."
}
Workflows
Smart group discovery and content querying
Smart groups are virtual views defined by search criteria — they have no physical location in the database. Because they are not part of the AppleScript scripting dictionary, you cannot list them through normal MCP or JXA calls. Use this two-step pattern instead:
Step 1: Enumerate all smart groups to find the UUID of the one you want.
list_smart_groups
Response includes each smart group's name and uuid.
Step 2: Query the contents of that smart group using the search tool with groupUuid.
search
query: ""
groupUuid: "4A469368-94FD-46D3-9A62-ED7C24D822D8"
This returns all records that match the smart group's criteria. The search tool sorts by relevance by default; add a sortBy parameter if you need date ordering.
Important: list_group_content with a smart group UUID returns email Message-IDs in the uuid field (not DEVONthink record UUIDs). Use search with groupUuid instead — it returns proper records with dates and correct UUIDs that work with get_record_properties and get_record_content.
Email thread correlation
To link a live Gmail thread back to its archived copy in DEVONthink, use a three-tier matching strategy:
Tier 1 — Thread ID match (highest precision)
Get the file path for the archived record:
get_record_properties
uuid: <record_uuid>
Extract the path field, then parse the EML headers:
parse_eml_headers
filePath: "/path/to/archived/email.eml"
Use the messageId, inReplyTo, and references values to correlate precisely with Gmail thread headers.
Tier 2 — Subject and sender match
search
query: "kind:email subject:\"Contract renewal\" from:jane@example.com"
Strip Re:, Fwd:, AW:, WG: prefixes before searching.
Tier 3 — Subject only (broadest)
search
query: "kind:email subject:\"Contract renewal\""
Always try Tier 1 first when the EML path is available. Report which tier matched so confidence is clear.
Column layout management
When you create a new smart group and want it to display the same columns as an existing one:
Step 1: Read the layout of the source smart group to see what columns it uses.
get_column_layout
name: "Archivieren - Jobs"
Step 2: Copy that layout to the new smart group.
copy_column_layout
sourceName: "Archivieren - Jobs"
targetName: "New Smart Group"
If DEVONthink stores layouts by UUID rather than display name, supply the UUID as a fallback:
copy_column_layout
sourceName: "Archivieren - Jobs"
targetName: "New Smart Group"
targetUuid: "4A469368-94FD-46D3-9A62-ED7C24D822D8"
Close and reopen the smart group window (or restart DEVONthink) after copying for the change to take effect.
DEVONthink search syntax
The upstream search tool supports these operators:
kind: | kind:email | Filter by record type |
name: | name:"offer letter" | Match filename or subject |
subject: | subject:"interview" | Email subject field |
from: | from:recruiter@co.com | Sender address |
to: | to:user@example.com | Recipient address |
text: | text:"stock options" | Full-text content search |
tags: | tags:jobs | Tagged records |
date: | date:2024-01-01~ | Date range (~ = after) |
| Quotes | "exact phrase" | Exact phrase match |
| AND/OR | from:x OR from:y | Boolean operators |
Combining operators:
kind:email from:@company.com subject:"compensation" date:2023-01-01~2024-12-31
How it works
devonthink-mcp runs as an in-process MCP server built on @modelcontextprotocol/sdk. It imports all upstream tool objects from mcp-server-devonthink and adds the five custom tools, then serves them through a single unified ListTools / CallTool handler.
The upstream tools communicate with DEVONthink via JXA (JavaScript for Automation) over the macOS Scripting Bridge. The custom tools use plutil, PlistBuddy, and Python's plistlib to read DEVONthink preference and data files directly.
Troubleshooting
"DEVONthink not found"
Make sure DEVONthink 3 or 4 is installed in /Applications and running.
"No databases found"
Open at least one DEVONthink database before using the MCP tools.
Tools not appearing in Claude Code
- Verify
~/.claude.json has the devonthink entry
- Restart Claude Code (not just a new session — fully quit and reopen)
- Check that DEVONthink is running
AppleScript errors
Grant Claude Code (or Terminal) Automation permissions in System Settings > Privacy & Security > Automation.
list_smart_groups returns no results or error
The plist format varies between DEVONthink versions. If the tool cannot parse the structure, use plutil -p ~/Library/Application\ Support/DEVONthink/SmartGroups.plist to inspect the raw format and report an issue.
get_column_layout returns "no layout found"
The smart group does not yet have a custom column layout saved — it uses DEVONthink defaults. Use copy_column_layout to copy a layout from another smart group that already has one configured.
Credits
The core DEVONthink MCP implementation is by dvcrn. This package adds setup tooling, packaging, and the custom extensions above.
License
MIT