Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 35 additions & 12 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,38 @@ directly in `evernote-api.ts`.

## MCP Tools

The server defines **27 tools**, all prefixed `evernote_` (defined in the
tools array starting at `src/index.ts:316`). They cover notes
(`create_note`, `get_note`, `update_note`, `patch_note`, `delete_note`,
`search_notes`), notebooks (`list_notebooks`, `get_notebook`,
`create_notebook`, `update_notebook`), tags (`list_tags`, `get_tag`,
The server defines **15 tools**, all prefixed `evernote_` (defined in the
`tools` array in `src/index.ts`). They cover notes (`create_note`,
`get_note`, `update_note`, `delete_note`, `search_notes`), notebooks
(`list_notebooks`, `create_notebook`, `update_notebook`), tags (`list_tags`,
`create_tag`, `update_tag`), resources/attachments (`get_resource`,
`list_note_resources`, `add_resource_to_note`, `get_resource_recognition`,
`get_resource_text`), auth/health (`get_user_info`, `revoke_auth`,
`health_check`, `reconnect`), and polling (`start_polling`, `stop_polling`,
`poll_now`, `polling_status`).
`add_resource_to_note`), and two action-dispatched admin tools —
`polling({action})` and `connection({action})`.

**Consolidated surface (was 27).** Read/lifecycle operations are parameterized
rather than multiplied into separate tools:
- `get_resource({guid, as})` projects one attachment through `as: "text"`
(default; PDF/OCR extraction) | `"binary"` (base64) | `"recognition"` (OCR
data) | `"metadata"` (filename/mime/size/hash/hasRecognition). Replaces the
old `get_resource_text` / `get_resource_recognition` tools; a note's
attachment list comes from `get_note`'s `resources[]` (so no
`list_note_resources`).
- `list_notebooks` / `list_tags` return the full list, or one entity when
passed a `name` or `guid` (absorbing the old `get_notebook` / `get_tag`).
- `update_note` takes `replacements[]` for targeted find-and-replace (patch
mode, mutually exclusive with the full-field inputs) — absorbing the old
`patch_note`.
- `polling({action: start|stop|poll|status})` and
`connection({action: status|user|reconnect|revoke})` replace the four
polling command tools and the four `health_check`/`get_user_info`/
`reconnect`/`revoke_auth` tools.

**Backward-compatible aliases.** The 14 retired names stay callable: a
`TOOL_ALIASES` map in `src/tool-aliases.ts` rewrites each to its canonical tool
(and args) before validation in the `CallTool` dispatcher, logging a one-time
deprecation notice. They are hidden from `ListTools` by default; set
`EVERNOTE_LEGACY_TOOLS=true` to re-advertise them during a migration window.
See `MIGRATION.md` for the full old→new mapping.

Many tools accept user-friendly names but resolve to Evernote GUIDs
internally (notebook names → GUIDs via `listNotebooks()`, tag names → GUIDs
Expand Down Expand Up @@ -270,7 +292,7 @@ releases reliably.

```text
fix: resolve notebook GUID lookup for nested tags
feat: add evernote_patch_note tool
feat: add format projection to evernote_get_note
docs: clarify standalone OAuth callback port
chore: bump @modelcontextprotocol/sdk
```
Expand All @@ -283,8 +305,9 @@ chore: bump @modelcontextprotocol/sdk
- Sandbox testing: set `EVERNOTE_ENVIRONMENT=sandbox` (requires a separate
account at sandbox.evernote.com). For CI, prefer `EVERNOTE_ACCESS_TOKEN`;
`.evernote-token.json` is a local fallback only.
- Quick connectivity checks: `evernote_get_user_info` verifies auth;
`evernote_list_notebooks` confirms API connectivity.
- Quick connectivity checks: `evernote_connection({action:"status"})` (or
`{action:"user"}`) verifies auth; `evernote_list_notebooks` confirms API
connectivity.

<!-- BEGIN AUTOMEM RULES -->
## Memory-First Development (AutoMem)
Expand Down
91 changes: 91 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Migration: tool-surface consolidation (27 → 15 tools)

This release consolidates the MCP tool surface from **27 tools to 15** with no
loss of capability. Fewer tools means less context loaded on every turn and
more reliable tool selection.

**Nothing breaks immediately.** Every retired tool name still works: the server
rewrites it to its canonical tool (and arguments) and logs a one-time
deprecation notice. Retired names are hidden from the tool list by default; set
`EVERNOTE_LEGACY_TOOLS=true` to re-advertise them during a migration window.
Update your callers to the canonical tools, then drop the flag — the aliases
will be removed in a future major.

## What changed, by bucket

### Resources (5 → 2)

`evernote_get_resource` now projects one attachment through an `as` view.
**Its default changed from binary download to text extraction.**

| Old call | New call |
|---|---|
| `get_resource({guid})` (binary) | `get_resource({guid, as:"binary"})` |
| `get_resource({guid, includeData:true})` | `get_resource({guid, as:"binary"})` |
| `get_resource({guid, includeData:false})` | `get_resource({guid, as:"metadata"})` |
| `get_resource_text({resourceGuid})` | `get_resource({guid, as:"text"})` |
| `get_resource_recognition({resourceGuid})` | `get_resource({guid, as:"recognition"})` |
| `list_note_resources({noteGuid})` | `get_note({guid, includeAttachmentText:false})` → read `resources[]` |

`as:"metadata"` returns `{guid, filename, mimeType, size, hash, hasRecognition}`
for a single attachment — the same per-resource fields the old
`list_note_resources` returned. `get_note`'s `resources[]` lists a note's
attachments with `{guid, filename, mimeType, size}` (no `hash`/`hasRecognition`;
fetch those per-attachment via `as:"metadata"`). `add_resource_to_note` is
unchanged.

### Polling (4 → 1)

| Old | New |
|---|---|
| `start_polling()` | `polling({action:"start"})` |
| `stop_polling()` | `polling({action:"stop"})` |
| `poll_now()` | `polling({action:"poll"})` |
| `polling_status()` | `polling({action:"status"})` |

### Connection / account (4 → 1)

| Old | New |
|---|---|
| `health_check({verbose?})` | `connection({action:"status", verbose?})` |
| `get_user_info()` | `connection({action:"user"})` |
| `reconnect()` | `connection({action:"reconnect"})` |
| `revoke_auth()` | `connection({action:"revoke"})` |

### Notebooks & tags (4 → 3 each)

`list_notebooks` / `list_tags` return the full list, or a single entity when
given a `name` or `guid`.

| Old | New |
|---|---|
| `get_notebook({name?/guid?})` | `list_notebooks({name?/guid?})` |
| `get_tag({name?/guid?})` | `list_tags({name?/guid?})` |

`create_notebook` / `update_notebook` / `create_tag` / `update_tag` are unchanged.

### Notes (6 → 5)

`patch_note` folds into `update_note` as **patch mode**: pass `replacements[]`
(mutually exclusive with `title`/`content`/`tags`/`notebookName`).

| Old | New |
|---|---|
| `patch_note({guid, replacements})` | `update_note({guid, replacements})` |

`create_note` / `get_note` / `delete_note` / `search_notes` are unchanged.

## The 15 canonical tools

Notes: `create_note`, `get_note`, `update_note`, `delete_note`, `search_notes` ·
Notebooks: `list_notebooks`, `create_notebook`, `update_notebook` ·
Tags: `list_tags`, `create_tag`, `update_tag` ·
Resources: `get_resource`, `add_resource_to_note` ·
Admin: `polling`, `connection`.

## Deprecated aliases (14)

`get_resource_text`, `get_resource_recognition`, `list_note_resources`,
`start_polling`, `stop_polling`, `poll_now`, `polling_status`, `health_check`,
`get_user_info`, `reconnect`, `revoke_auth`, `get_notebook`, `get_tag`,
`patch_note` — all still callable; set `EVERNOTE_LEGACY_TOOLS=true` to list them.
108 changes: 45 additions & 63 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,11 @@ When changes are detected, a POST request is sent to your webhook URL:

#### Manual Control

Use these tools to control polling:
- `evernote_start_polling` - Start polling manually
- `evernote_stop_polling` - Stop polling
- `evernote_poll_now` - Check for changes immediately
- `evernote_polling_status` - Get polling configuration and status
Use the `evernote_polling` tool to control polling:
- `polling({action:"start"})` - Start polling manually
- `polling({action:"stop"})` - Stop polling
- `polling({action:"poll"})` - Check for changes immediately
- `polling({action:"status"})` - Get polling configuration and status

### Evernote Webhooks (Real-time)

Expand Down Expand Up @@ -371,6 +371,14 @@ EVERNOTE_ALLOWED_FILE_ROOTS=/Users/you/Documents:/Users/you/Projects

## Available Tools

The server exposes **15 tools** (consolidated from 27). Retired tool names still
work as deprecated aliases and can be re-listed with `EVERNOTE_LEGACY_TOOLS=true`
— see [MIGRATION.md](MIGRATION.md) for the full old→new mapping. Highlights:
`get_resource({guid, as})` projects an attachment (`text`/`binary`/`recognition`/`metadata`);
`list_notebooks`/`list_tags` return one entity when passed a `name`/`guid`;
`update_note` takes `replacements[]` for patch-style edits; and the `polling`
and `connection` tools dispatch on an `action`.

## Markdown Support

This server automatically converts between Markdown and Evernote's ENML format:
Expand Down Expand Up @@ -438,14 +446,20 @@ Retrieve one note (full detail) or a batch of up to 25 (body-focused).
> Returned Markdown represents embedded resources with `evernote-resource:<hash>` URLs. Leave those references intact so attachments stay linked when you edit the note.

#### `evernote_update_note`
Update an existing note.
Update an existing note. Two mutually exclusive modes:

**Parameters:**
**Full-update mode parameters:**
- `guid` (required): Note GUID
- `title` (optional): New title
- `content` (optional): New content
- `content` (optional): New content (Markdown supported)
- `notebookName` (optional): Move the note to this notebook
- `tags` (optional): New tags (replaces existing)

**Patch mode parameter** (replaces the old `evernote_patch_note`):
- `replacements` (optional): Array of `{find, replace, replaceAll?}` find-and-replace
edits applied to the note body, preserving title, tags, notebook, and
attachments. Cannot be combined with the full-update fields above.

#### `evernote_delete_note`
Delete a note.

Expand All @@ -455,7 +469,8 @@ Delete a note.
### Notebook Operations

#### `evernote_list_notebooks`
List all notebooks in your account.
List all notebooks in your account, or get one notebook's full detail by passing
its `name` or `guid` (absorbs the old `evernote_get_notebook`).

#### `evernote_create_notebook`
Create a new notebook.
Expand All @@ -467,7 +482,8 @@ Create a new notebook.
### Tag Operations

#### `evernote_list_tags`
List all tags in your account.
List all tags in your account, or get one tag's full detail by passing its
`name` or `guid` (absorbs the old `evernote_get_tag`).

#### `evernote_create_tag`
Create a new tag.
Expand All @@ -476,75 +492,41 @@ Create a new tag.
- `name` (required): Tag name
- `parentTagName` (optional): Parent tag for hierarchy

### Account Operations

#### `evernote_get_user_info`
Get current user information and quota usage.

#### `evernote_revoke_auth`
Revoke stored authentication token.

### Diagnostic Operations
### Connection & Account

#### `evernote_health_check`
Check the health and status of the Evernote MCP server.
#### `evernote_connection`
Manage the Evernote connection and account. Dispatches on `action`
(replaces the old `health_check`, `get_user_info`, `reconnect`, `revoke_auth`):

**Parameters:**
- `verbose` (optional): Include detailed diagnostic information (default: false)

**Returns:**
- Server status (healthy, unhealthy, needs_auth, etc.)
- Authentication status
- Token information (when verbose)
- Configuration details
- `action:"status"` — health/diagnostic check (server + auth state). Pass
`verbose:true` for detailed diagnostics.
- `action:"user"` — current user information and quota usage.
- `action:"reconnect"` — force reconnection (useful on "Not connected" errors).
- `action:"revoke"` — revoke the stored authentication token.

**Example:**
```
Check Evernote connection health with verbose details
```

#### `evernote_reconnect`
Force reconnection to Evernote. Useful when experiencing "Not connected" errors.

**Use this when:**
- You see "Not connected" errors
- You've just refreshed your token
- The server seems stuck in a failed state

**Example:**
```
Reconnect to Evernote
```

### Polling Operations

#### `evernote_start_polling`
Start polling for Evernote changes. Checks for new/updated/deleted notes and sends notifications to the configured webhook URL.
#### `evernote_polling`
Manage background polling for changes (detected changes are sent to the
configured webhook). Dispatches on `action` (replaces the old `start_polling`,
`stop_polling`, `poll_now`, `polling_status`):

**Example:**
```
Start polling for Evernote changes
```

#### `evernote_stop_polling`
Stop the polling process.

#### `evernote_poll_now`
Check for changes immediately without waiting for the next poll interval. Returns a list of detected changes.
- `action:"start"` — begin polling on the configured interval.
- `action:"stop"` — halt polling.
- `action:"poll"` — check for changes immediately; returns detected changes.
- `action:"status"` — current polling configuration and state (running, interval,
webhook URL, last poll time, error count).

**Example:**
```
Check for Evernote changes now
Start polling for Evernote changes
```

#### `evernote_polling_status`
Get the current polling configuration and status, including:
- Whether polling is running
- Poll interval
- Configured webhook URL
- Last poll time
- Error count

## Search Syntax

Evernote supports advanced search operators:
Expand Down
Loading
Loading