Evernote
Search, edit, organize notes
- Category
- Pending
- Primary Subcategory
- Pending
Integration details
Description
The Evernote MCP server connects your Evernote account to your AI tools, so they can work from the notes, documents, and files you've already collected. Search across years of captured material, edit and organize what's there, and file new work straight back into your notebooks. Key features: • Search by keyword or by meaning - Use Evernote's search grammar to filter by notebook, tag, and date, or search semantically to surface notes by what they're about, even when you can't remember the words you used. • Find what you never typed - Search reaches text inside scanned documents, photos, and images, and inside transcribed audio and video — so handwritten pages, whiteboard shots, receipts, and recorded meetings are all searchable alongside your written notes. Attached files can be downloaded and new ones uploaded. • Read, write, and edit real notes - Fetch a note's full content, tags, and attachments; create new notes in the right notebook; update titles and bodies; and restore anything that ends up in the trash. • Keep your account organized - Move notes between notebooks, add and remove tags, rename and re-parent tags, and create notebooks and Spaces as your work grows. • Manage tasks inside your notes - Create tasks, update status and due dates, and find the ones you own or have been assigned, filtered by priority. Everything happens inside your own account, under your existing permissions.
- Integration type
- Plugin
- Verification status
- Not applicable
- Platform
- ChatGPT
- Category
- Pending
- Primary Subcategory
- Pending
- Secondary Subcategories
- None listed
- Brand
- Unknown
- Access
- Account required
- First tracked
- 2026-09-24
- Tool count
- 27
- Geography
- US
The broad Category that contains the Primary Subcategory.
The Primary Subcategory used for this profile’s headline score.
Other Subcategories where the Integration is listed.
Get alerts for Evernote
Get updates when Evernote’s Discoverability Score or category rank changes.
ChatGPT Plugin Discovery Score
ChatGPT Plugin discovery is coming soon
ChatGPT can surface a Plugin when it matches a user's request.Your Plugin Discovery Score measures how often yours appears.
No spam. Unsubscribe any time.
What discovery looks like

Competitive lineup
27 tools agents can invoke
Creates a new Space (workspace) — a top-level container for notebooks — and returns its GUID as `workspaceId`. Pass the returned `workspaceId` to `create_notebook` (`workspaceId`) to place a notebook directly in the new Space, or use it with `get_space`. Works for personal and business / team accounts: a business user (admin or member) creates the Space under their business and gets full (manage) access to it; a personal user creates a personal Space. `type` defaults to INVITE_ONLY. OPEN Spaces require `defaultPrivilege` (READ, EDIT or EDIT_AND_MANAGE). Spaces are subject to the account's space count limit; creating one past the limit returns an error.
create_space
Creates a new task entity in a note. Composite: handles the tasks-service write AND, for a new group, the ENML placeholder insertion in one call. You never author task ENML — the server builds the placeholder. **No note specified?** Omit `noteId` to add the task to the user's default task note — the note note-less tasks collect in, created on first use, just like a note goes to the default notebook (this is also the Assistant's behavior). The task lands in that note's existing task group (or a fresh one if it has none); do NOT pass `mode`/`find`/`taskGroupNoteLevelId` in this case. Only pass `noteId` when the user names a specific note. **Two modes (when targeting a specific `noteId`):** 1. **Add to an existing group** — pass `taskGroupNoteLevelId` (from `get_note.structuredContent.tasks[]`). No ENML change; the placeholder is already in the note. 2. **Create a new group** — omit `taskGroupNoteLevelId`. The server mints the group id, builds the placeholder div, and positions it with `mode` (`append` = end, default; `prepend` = start; `after`/`before` = next to the unique `find` anchor). This only inserts the group — it never replaces note content. **Setting task attributes.** `create_task` only creates the task — its `label` and placement. It does NOT take due date, priority, flag, description, reminder, time zone, or recurrence as parameters; passing any of them (e.g. `dueDate`, `priority`, `flag`, `recurrence`) is rejected as an unrecognized argument. To set them, call `update_task` with the returned `taskId` once the task exists. **Ordering & partial failure (new group).** The placeholder insertion runs BEFORE the task entity is created — it materializes the note's collaborative (Yjs) doc so tasks-service's real-time notification reaches open editors immediately. Consequences: - If the placement fails (e.g. `find` not found / ambiguous, note not found), **no task is created** — safe to retry. - If placement succeeds but the entity create fails, the response has `enmlEdited: true, taskCreated: false` plus the `noteId` and `taskGroupNoteLevelId` now in the note. **Retrying (NOT idempotent — every call mints fresh ids).** Decide from the result: - **error** → nothing created, retry verbatim. - **`taskCreated: false`** → placeholder is in the note; recover by calling again with the `noteId` AND `taskGroupNoteLevelId` from the result (add-to-existing) — pass `noteId` explicitly even if your first call omitted it (default task note), NOT verbatim (which adds a second placeholder). - **`taskId`** → success; do not retry. Prerequisites: `get_note(noteId)` to find existing groups, and to copy a `find` anchor when using `mode: "after"` or `"before"`.
create_task
Creates a new, EMPTY note and returns its GUID. This tool does NOT accept body content — the new note's body is created and controlled by the server. Body content is **not** a parameter here: passing `content`/`enmlContent` (or any other undeclared field) is rejected as an unrecognized argument — add text afterward with `edit_note`. **To add content, call `edit_note` afterwards with the returned GUID** (one or more times): `edit_note` takes small ENML fragments and the server splices them into the note safely, so you never have to write a full ENML document or its `<?xml>`/`<!DOCTYPE>` prolog. Typical flow: 1. `create_note({title:"My note"})` → returns `noteId`. 2. `edit_note({noteId, mode:"append", content:"<p>First paragraph.</p>"})`. 3. repeat `edit_note` for more content. Placement: pass `notebookId` to create the note in a specific notebook, or `workspaceId` to create it directly inside a Space with no notebook — not both. Omit both to use the default notebook. To resolve a notebook name to a `notebookId`, or to obtain a `workspaceId` (the `workspaceId` field on any notebook in that Space), call `search_notebooks` first; use `search_tags` for tag GUIDs. The returned GUID is also usable with `delete_note`, `move_notes`, and `update_note_tags`.
create_note
Creates a new notebook. Returns the new notebook's GUID — pass it to `create_note` (`notebookId`), `move_notes` (`destinationNotebookId`), or use it in a `search_notes` query as `nbGuid:"<id>"`. Omit `workspaceId` for a personal notebook (the common case). Pass it to create the notebook inside a Space — get the `workspaceId` from `search_notebooks`.
create_notebook
Creates a new tag, flat (no parent). To nest it under another tag, call `edit_tag` afterwards with `parentId`. Returns the new tag's GUID — use it with `create_note` (`tagIds`), `update_note_tags` (`tagIdsToAdd`), or in a `search_notes` query as `tagGuid:"<id>"`. Constraints: business / vault users may be rejected — tags are personal-account scope.
create_tag
Deletes a task entity, and — when it was the **last task in its group** — automatically removes the now-empty task-group placeholder div from the note body. You don't pass or edit any ENML; the server does the cleanup surgically by group id. If the deleted task still has siblings in its group, the placeholder stays (it's still in use) and no ENML change is made. **Partial failure.** If the entity delete succeeds but the placeholder cleanup fails (rte-service error), the response has `enmlEdited: false` plus a warning — the task is already gone; retry `delete_task` (idempotent) or remove the placeholder via `edit_note`. Idempotent at the storage layer (tasks-service uses `deleted_at` markers, not hard delete) — repeated calls converge. Prerequisites: call `get_note(noteId).structuredContent.tasks[]` to learn the taskId. The tool looks up the parent noteId internally.
delete_task
Moves a note to trash. Reversible: call `restore_note` to bring it back, or the user can restore it from the trash in the Evernote app. Permanent deletion (expunge) is not available — only trash. Prerequisites: call `search_notes`, `semantic_search`, or `get_note` to resolve a note to its GUID.
delete_note
Edits a note's body with a small, targeted change — you do NOT resend the whole note. The server preserves everything you don't touch byte-for-byte; you only describe the change. **This is also how you add content to a note made by `create_note`** (which always creates an empty note): call `edit_note` with the returned GUID and `mode:"append"` to fill it in. **Body modes** (set `mode` + `content`): - `append` — add `content` to the end of the note. - `prepend` — add `content` to the start of the note. - `replace` — substitute the single occurrence of `find` with `content`. `content` is an ENML fragment (Evernote's XML dialect), not the full body and not Markdown. Examples: - Append a paragraph: `{mode:"append", content:"<p>See you tomorrow.</p>"}` - Prepend a heading: `{mode:"prepend", content:"<h1>Summary</h1>"}` - Fix a line: `{mode:"replace", find:"<li>Buy milk</li>", content:"<li>Buy oat milk</li>"}` **`content` must be well-formed XML — escape bare `&` and `<`.** Write `&` for `&` and `<` for `<`, including inside text and headings: a heading like Q&A must be `<h2>Q&A</h2>`, not `<h2>Q&A</h2>`. An unescaped `&`/`<` is the most common cause of a "not valid ENML and could not be merged" rejection. **`replace` needs a unique anchor.** Copy `find` byte-for-byte from `get_note` (tags, attributes, and entity encoding included). Editor-created headings and blocks often carry a `style="--en-nodeId:…"` attribute — include it verbatim in `find` (don't simplify `<h2 style="--en-nodeId:…">Title</h2>` down to `<h2>Title</h2>`, or it won't match). If it matches zero places you get a not-found error; if it matches more than once you get an ambiguous error — lengthen `find` with surrounding markup until it's unique. `append`/`prepend` need no anchor and no `get_note` first. **Title.** Pass `title` to set the note title (independent of the body; can be combined with a mode, or used alone for a title-only edit). Edits are safe even while the note is open in another Evernote client — changes appear live to other editors. **Not for tasks.** Task groups appear in ENML only as a placeholder div; the task entities live in tasks-service. Use `create_task`, `update_task`, or `delete_task` — editing a task-group span here is rejected. **Attachments.** To add a file, run `start_attachment_upload` → `finalize_attachment` first (that registers the resource and returns the `<en-media>` tag), then `append` or `replace` that tag into the body here. Don't invent an `<en-media>` tag yourself — an unregistered hash renders as a broken attachment. To **remove** an image, `replace` its whole `<en-media … />` tag (copied byte-for-byte from `get_note`) with content:"". To **swap** an image, `replace` the whole tag with another finalized `<en-media>` tag. You must match the entire tag — a `find` that cuts partway into an `<en-media>` tag is rejected. **ENML errors.** `content` must be valid ENML. If an edit keeps failing with an ENML error (e.g. `Note content is not valid ENML` or "not valid ENML and could not be merged"), fetch and read the ENML formatting guide at `evernote://enml-guide` before retrying — it lists the permitted/forbidden tags and attributes (no `class`/`id`) and how to fix the specific failure.
edit_note
Renames a tag and/or moves it in the tag hierarchy. Provide at least one of `name` (new label) or `parentId` (new parent — pass `null` to detach to root, or a tag GUID to nest under another tag). Constraints: business / vault users cannot rename personal tags. Only the owner can rename a shared tag. Prerequisites: call `search_tags` to resolve a name to a GUID.
edit_tag
**Step 3 of 3 for adding a file as an attachment to an Evernote note.** Registers already-uploaded bytes as a resource entity on the note and returns a ready-to-paste `<en-media>` tag. ## When to call this tool After `start_attachment_upload` returned, AND either: - You have completed the two-step GCS resumable upload that `start_attachment_upload` describes (init POST → PUT bytes to the session URL), OR - `start_attachment_upload` returned `alreadyUploaded: true` (no upload was needed). ## What it does Calls api-gateway's `POST /v1/attachments`, which writes an ATTACHMENT entity in note-service keyed to `(noteId, hash, sizeBytes)` and returns a stable resource GUID. The resource is now part of the note in the same sense any other resource is — it shows up in `get_note.resources[]`, and the bytes are downloadable via `get_attachment`. ## What you still have to do (step 4) This call does NOT yet make the attachment visible inside the note body. ENML references resources via `<en-media hash=... type=... />` tags, and you must insert that tag into the note's content yourself by calling `edit_note` with the full new ENML. The `enMediaTag` field in this tool's response gives you the exact string to splice in — append it just before `</en-note>` to place the attachment at the end of the note, or splice it in line at the position the user asked for. ## Inputs must match the upload Pass the same `hash`, `sizeBytes`, `mimeType` you used (or planned to use) with `start_attachment_upload`. The server doesn't re-hash; if `hash` and `sizeBytes` don't match what was uploaded, downloads will appear corrupted to other clients. `filename` and `sourceUrl` are optional metadata stored on the resource — recommended for PDFs/documents, less important for inline images. ## When called on a live-edited note This is safe regardless of whether the note is currently open in another client's collaborative editor. The resource entity is created via api-gateway (rte-service-independent), and step 4 (`edit_note`) goes through rte-service's CRDT mutation path so the inserted `<en-media>` tag broadcasts live to any open editor room.
finalize_attachment
Fetches a single Space (workspace) by GUID: name, description, `type` (INVITE_ONLY / DISCOVERABLE / OPEN), default privilege (for OPEN spaces), the backing notebook GUID, member count, and creation/update timestamps. Self-scoped: it succeeds only if you can access the Space (a member, its owner, or a business admin); otherwise it reports the Space as not found or inaccessible. For the caller's own privilege in the Space, use `search_spaces` (each hit carries it). Prerequisites: call `search_spaces` to resolve a Space name to a GUID.
get_space
Returns a short-lived, pre-signed HTTPS URL to download an Evernote attachment (referenced by `<en-media hash=...>` in note ENML). The URL points straight at the file bytes and can be fetched directly — no Evernote auth header, no follow-up call. It expires after a short window (see `expiresAt`), so fetch it promptly rather than storing it. Works on owned, shared, and business notes — the URL is only issued if you can read the note. Tip: `get_note`'s `structuredContent.resources` already lists every attachment on the note with its hash, mime, and filename, so you typically know which hash you want before calling this tool.
get_attachment
Fetches a note by GUID. Returns the title, body (ENML), parent notebook GUID, version, timestamps, trash state (`active` / `deleted`), plus the satellite payloads ENML cannot express on its own — tasks, tags, resource manifest, and selected note attributes — in `structuredContent`. The body is returned in ENML (Evernote's XML dialect). To change the body, call `edit_note` with a `mode` (append/prepend/replace) and a small ENML `content` fragment — you do not resend the whole body. For `replace`, copy a byte-exact, unique snippet of this ENML as the `find` anchor. Important — ENML is not the full story: - Task groups appear in ENML only as a per-group placeholder div (`--en-task-group:true`); the task entities themselves live in `structuredContent.tasks[]` as a lightweight summary (`id`, `label`, `status`, `dueDate`). Read those fields from there, not from the ENML. For a task's `description`, `priority`, `flag`, `recurring` state, or assignee, call `get_task(taskId)`. To **edit** tasks, use the dedicated tools — `create_task`, `update_task` (status changes are an `update_task` field), `delete_task` — never via `edit_note` and ENML splicing. - `<en-media>` references carry only the resource hash. Filename, mime type, and size live in `structuredContent.resources` — use those to decide whether the bytes are worth fetching via `get_attachment`. Prerequisites: call `search_notes` or `semantic_search` first if you don't already have a note GUID.
get_note
Fetches full detail for a single task by GUID — the complement to `get_note` and `search_tasks`, which only return a lightweight task summary. Returns `id`, `label`, `status`, `description`, `dueDate` (ISO-8601 or null), `priority` (`high`/`medium`/`low`/`none`), `flagged`, `recurring` (true for a base recurring task, false for a one-shot task or a recurring outlier), `assigneeEmail` (who the task is assigned to, or null when unassigned), and `noteId` (feed into `get_note` to see the task in context). Prerequisites: call `get_note(noteId).structuredContent.tasks[]` or `search_tasks` first to find a task's GUID.
get_task
Moves an existing notebook into a Space, between Spaces, or out of its current Space. Destination: pass `destinationSpaceId` with a Space GUID to move the notebook into that Space (works both for a loose notebook and for one already in another Space), or `null` to move it out of its current Space to the account level. Behavior: metadata-only — notes inside the notebook are untouched and move with it. Permissions are enforced server-side: moving requires edit access to the notebook and to both the source and destination Spaces; removing a notebook from a Space additionally requires full access to the notebook. A denied move comes back as a clean permission error. Prerequisites: call `search_notebooks` for the notebook GUID (and its current `workspaceId`) and `search_spaces` for the destination Space GUID.
move_notebook
Moves one or more notes into a different notebook, or directly into a Space, 1–5 notes per call. The same destination is applied to every note in the batch. Destination: pass `destinationNotebookId` to move the notes into a notebook, or `destinationWorkspaceId` to move them loose into a Space — exactly one. Behavior: metadata-only — note content is unchanged and any open editors are unaffected. Notes are moved sequentially; some may succeed while others fail — inspect `results[]` for per-note status. **Same-account only.** This re-parents a note; it cannot change which account owns it, so a note can only move to a destination in its own account. Moving a note into a notebook or Space owned by a *different* account (e.g. one someone shared with you) is NOT supported — that note comes back in `results[]` as an error. A cross-account move would copy the note into the other account and trash the original (what the Evernote app does); this tool does not perform that copy. Prerequisites: call `search_notes` for the note GUIDs and `search_notebooks` for the destination notebook GUID (or, in its `workspaceId` field, a Space GUID).
move_notes
Renames a notebook. The new label propagates to all of the notebook's memberships, matching the Evernote app's behavior. Constraints: only the owner can rename a shared notebook. Prerequisites: call `search_notebooks` to resolve a name to a GUID.
rename_notebook
Restores a trashed note, moving it back out of the trash to its original notebook. The inverse of `delete_note`. Edge case: if the note's original notebook was itself deleted, there is no home to restore into and the call fails with a clean error — recreate or pick a notebook first (the user can do this in the Evernote app). Prerequisites: call `search_notes` with `intrash:true` (or `intrash:*`) to resolve a trashed note to its GUID.
restore_note
Lists or searches the Spaces (workspaces) the current user belongs to. Empty `query` returns all of them; pass a substring to filter by name (case-insensitive, ranked exact > prefix > substring; ties alphabetical). Returns each Space's GUID, name, and the caller's `privilege` in it (READ, EDIT, or EDIT_AND_MANAGE). Use a Space's GUID as the `workspaceId` for `create_notebook` or `create_note` to place content directly in that Space, or with `move_notes`. Call `get_space` for a Space's full detail. Self-scoped: it lists the Spaces you own or are a member of.
search_spaces
Lists or searches the user's notebooks by name. Empty `query` returns all notebooks; pass a substring to filter (case-insensitive, ranked exact > prefix > substring; ties alphabetical). Pass `defaultOnly: true` to return only the user's formal default notebook (the one a note created without a target lands in) — at most one hit, and an empty result means no formal default is configured. With `defaultOnly`, `query`, `maxResults`, and `sortBy` are ignored. Returns each notebook's GUID, label, `stack` (or null), `primaryAccess` (OWNED / SHARED / WORKSPACE / BUSINESS), `workspaceId` + `workspaceName` (the Space the notebook belongs to, or null when it isn't in one), and a ready-to-use `searchFilter` (`nbGuid:"<id>"`) that can be dropped into `search_notes`. Use `workspaceId` / `workspaceName` to tell which Space a notebook lives in and to group notebooks by Space; the `workspaceId` is also what `create_notebook` and `create_note` take to place a new notebook/note inside that Space. Use this to resolve a notebook name to a GUID before calling `create_note`, `move_notes`, or `rename_notebook`. To search note **content**, use `search_notes` (deterministic) or `semantic_search` (meaning-based).
search_notebooks
Searches notes using Evernote's search grammar — the same query language as the Evernote app's search bar. Use for deterministic, structured queries; for meaning-based queries use `semantic_search` instead. Returns note GUIDs, titles, snippets, and timestamps — follow up with `get_note(noteId)` for full content. Empty `query` lists all active notes (trash excluded). Pass `intrash:true` to list only trashed notes, or `intrash:false` for active only. `intrash:` accepts only `true`/`false` — there is no single-query way to list active and trashed together. Common operators: `tag:work`, `notebook:"My Notebook"`, `intitle:meeting`, `created:day-7`, `updated:day-1`, `any: a b` (OR across the following terms — the space after `any:` is required). Quote multi-word values (`tag:"to read"`). Combine with spaces (AND) or `OR` (when `fullBooleanSearch` is true). Date-relative operators (`created:day-7`, `updated:day-1`) need `clientTimeZone` to resolve correctly.
search_notes
Lists or searches the user's tags by name. Empty `query` returns all tags; pass a substring to filter — matched case-insensitively server-side across your whole tag set, then ranked exact > prefix > substring (ties alphabetical). Returns each tag's GUID, label, and a ready-to-use `searchFilter` (`tagGuid:"<id>"`) that can be dropped into `search_notes`. At most 500 matching tags are fetched per call (a hard upstream cap, no paging). When the query matches more than 500 tags, the tail is dropped, `totalResultCount` saturates at 500, and `isLastPage` is `false` — so `totalResultCount: 500` with `isLastPage: false` means "500 or more", not exactly 500. To see tags beyond the cap, refine `query` to a narrower substring so it matches fewer than 500. Use this to resolve a tag name to a GUID before calling `create_note`, `edit_note`, `update_note_tags`, or `edit_tag`. To search notes carrying a tag, use `search_notes` with `tag:"<name>"`.
search_tags
Searches your tasks across your notes. **Scope (important): this returns only tasks you OWN or are ASSIGNED TO.** A task in a note you don't own is returned **only if it is assigned to you** — you will NEVER see a task just because its note or notebook was shared with you. Unassigned tasks, and tasks assigned to other people, in notes you don't own are never returned. (Tasks assigned to you that live in someone else's note ARE included.) If the user asks about "all tasks in a shared project", tasks owned by others, or tasks they assigned to other people, tell them this tool only covers tasks they own or are assigned. Filters: - `status`: `"open"` (default) or `"completed"`. - `assigneeScope` (which of your tasks to include): `me_or_unassigned` (default — assigned to you plus your tasks assigned to no one), `me` (only tasks assigned to you), `unassigned` (only your tasks assigned to no one), `assigned_to_others` (tasks in your notes you've assigned to someone else), `not_me` (the previous two combined — tasks in your notes not assigned to you), `all` (everything in your scope). Every mode stays within tasks you own or are assigned. - `dueDate` (`{ from?, to? }`, ISO-8601 datetimes that MUST include an offset, e.g. `2026-06-01T00:00:00Z`): for **open** tasks it filters by **due date**; for **completed** tasks it filters by **completion date**. Completed search defaults to the **last 30 days** by completion date when no range is given, and **includes tasks that have no due date**. - `priority`: `high` / `medium` / `low` / `none`. - `flagged`: boolean. - `recurring`: boolean — `true` returns base recurring tasks only (individual generated occurrences/outliers are not expanded). - `searchString`: case-insensitive substring match on the task label. - `noteId`: restrict to a single note. (There is no notebook filter.) Each task returns exactly `id`, `label`, `status`, `description`, `dueDate` (ISO-8601 or null), `priority` (one of `high`/`medium`/`low`/`none`), `flagged`, `recurring`, and `noteId` (use `noteId` to follow up with `get_note` or the edit tools `update_task` / `delete_task`; use `id` with `get_task` for the assignee or any other full-detail field this search result omits). Returns up to **50 tasks per page**; paginate by passing the returned `nextCursor` back as the opaque `cursor`.
search_tasks
Searches notes by meaning rather than keywords. Use for natural-language questions ("notes about the new pricing model", "what did I write about Berlin") where the user's wording may not match the words in the note. For exact-keyword or structured queries (tag / notebook / date filters), use `search_notes` instead. Returns note GUIDs with relevance scores (0–1) and snippets of the matching passages — call `get_note(noteId)` to read full content.
semantic_search
**Step 1 of 3 for adding a file (PDF, image, audio, etc.) as an attachment to an Evernote note.** Mints a pre-signed Google Cloud Storage URL that you (the LLM, or your runtime) will use to upload the file bytes directly to storage. The MCP server never sees the bytes — keeping the file out of the LLM's context window — so this flow works for files of any practical size, not just the few hundred KB an inline base64 tool argument could carry. ## The full flow 1. **Call this tool** with the file's `noteId`, `hash` (lowercase MD5 of the bytes), and `sizeBytes`. Receive `uploadUrl` (a signed *resumable-upload initiation URL* — see how to use it below) and `alreadyUploaded` (true if the server already has these bytes). 2. **If `alreadyUploaded` is false**, perform a **two-step GCS resumable upload** to the `uploadUrl`. The detailed protocol and a copy-pastable curl pattern are in the next section. Skip this step entirely if `alreadyUploaded` was true. 3. **Call `finalize_attachment`** with the same `noteId`, `hash`, `sizeBytes`, plus the `mimeType` (and optional `filename`). That call registers the bytes as a resource entity on the note and returns a ready-to-paste `<en-media>` tag plus the resource GUID. 4. **Call `edit_note`** with the note's full new ENML, with the `<en-media>` tag spliced in at the position you want. The tag has the shape `<en-media hash="..." type="..." />` and is the canonical way ENML references attachments; it works alongside any other inline content. If you want it at the end, append it just before `</en-note>`. ## How to perform the upload (step 2) The `uploadUrl` is a *resumable-upload initiation URL* — the same kind the official Evernote clients use, since Evernote's storage layer always issues these to support large files and unreliable networks. **A single direct PUT against `uploadUrl` will be rejected by GCS with a signature error.** You must perform TWO HTTP calls: **Call A — initiate the session.** `POST` to `uploadUrl` with these headers and an empty body: - `x-goog-resumable: start` - `x-upload-content-length: <sizeBytes>` (matching the value you passed to this tool) - `content-length: 0` GCS responds with HTTP `201` (empty body) and a `Location:` response header. **The value of that `Location:` header is the session URL** — that's where the bytes actually go. Save it. **Call B — upload the bytes.** `PUT` the file's raw bytes to the session URL you just captured, with: - `Content-Type: application/octet-stream` - `Content-Length: <sizeBytes>` (matching exactly — mismatch is rejected) Body is the raw bytes. On success GCS responds `200`/`201` with an empty body. **curl pattern** (Claude Code or any shell-capable runtime; substitute your values for `<uploadUrl>`, `<sizeBytes>`, `<file-path>`): ```bash SESSION_URL=$(curl -s -X POST \ -H 'x-goog-resumable: start' \ -H 'x-upload-content-length: <sizeBytes>' \ -H 'content-length: 0' \ -D - '<uploadUrl>' | awk 'tolower($1) == "location:" { print $2 }' | tr -d '\r') curl -X PUT \ -H 'Content-Type: application/octet-stream' \ --data-binary @<file-path> "$SESSION_URL" ``` ## When `alreadyUploaded` is true Returned when these exact bytes (same hash) are already in this user's GCS bucket — typically because the user previously attached the same file to another note. **Skip both Call A and Call B**, go straight to `finalize_attachment`. The resource still needs to be registered against THIS note even if the bytes are reused. ## Common pitfalls - **The `hash` MUST be the lowercase hex MD5 of the actual bytes** you are about to upload. There is no server-side re-hash; a mismatched hash silently breaks downloads for every client. - **The Call A response is `201`, not `200`,** and has an empty body. The session URL is in the `Location` response header — read it from there, not from the body. Don't follow the redirect blindly; the body is intentionally empty. - **Don't attempt a single PUT against `uploadUrl`.** That URL is signed for the resumable initiation request; GCS will reject any PUT directly to it. - **Don't use the file's real MIME type on Call B's `Content-Type`** (e.g. `application/pdf`). The session URL is bound to `application/octet-stream` and GCS rejects mismatches. The file's real mime type goes into `finalize_attachment`, not the upload itself. - **Both URLs are time-limited.** `uploadUrl` expires within minutes; the session URL it produces lives longer (~1 week per GCS defaults) but is still finite. Re-call this tool if either expires before the upload finishes. - If you already know (e.g. from `get_note.resources[]`) that the same hash is already attached to THIS note, skip the entire upload flow — reuse the existing resource's `<en-media>` tag directly.
start_attachment_upload
Updates an existing task — either its status (open / completed) OR one or more of its properties (label, due date, priority, flag, description, sort weight, recurrence). One call updates one or the other, not both. **Property updates** — pass any of `label`, `dueDate`, `dueDateUIOption`, `priority`, `flag`, `description`, `sortWeight`, `timeZone`, `recurrence`. Omitted fields are left unchanged. Pass `idClock` for concurrent-edit safety (per-field last-write-wins ordering). **Status updates** — pass `status: "open"` or `status: "completed"`. **Recurring tasks behave specially when completed:** the server creates a *completed outlier* for this occurrence and advances the base task's `dueDate` to the next scheduled time — the underlying task stays `open` so the next occurrence is still due. The response carries the new `dueDate` (next occurrence) and `updatedOutlier` (this completed occurrence). Convey this to the user: "Marked today's standup done; next on …". Non-recurring tasks behave as expected — `status` flips and `updatedOutlier` is absent. Calling `status` and any property field in the same invocation returns `ToolInputError` — make two separate calls. **Does not move tasks between groups or notes.** Property and status changes don't touch the ENML placeholder, so no `edit_note` call is needed. To insert a task into a note or remove the last task from a group, use `create_task` / `delete_task`. Prerequisites: call `get_note(noteId)` and read `structuredContent.tasks[]` to discover task GUIDs.
update_task
Adds and/or removes tags on one or more notes (1–50 notes per call). The same tag delta is applied to every note in `noteIds`. Provide at least one of `tagIdsToAdd` or `tagIdsToRemove`; the same GUID cannot appear in both. Idempotent: already-applied tags and already-absent tags are silently skipped. Some notes may succeed while others fail — inspect `results[]` for per-note status. Each successful note reports `addedTagIds` and `removedTagIds` — the tags actually changed (no-ops are omitted). Prerequisites: call `search_tags` to resolve tag names to GUIDs, and `search_notes` for note GUIDs.
update_note_tags
How do I improve a ChatGPT Plugin's discoverability?
The levers are the listing surface agents actually read: names, descriptions, keywords, tool metadata, and registry health. Which lever matters depends on where discovery breaks, which is what continuous measurement shows.
Where is this profile measured?
This profile uses the geography attached to the latest public registry snapshot: US. Locale tags are intentionally omitted.