Amazing Marvin Integration
approvedby open-horizon-labs
Integration with Amazing Marvin (unofficial). Supports exporting AM tasks/projects, creating new tasks and completing tasks.
Amazing Marvin Integration for Obsidian
This plugin brings Amazing Marvin tasks, categories, and projects into Obsidian without treating the vault as disposable. It also includes an MCP server for agents that need Marvin access without mutating an Obsidian vault. That server runs independently of the plugin and is expected to be spun out into its own repository.
Use Marvin, Obsidian, and an agent together
Amazing Marvin remains the task system of record. The Obsidian plugin projects that work into notes you can use for context and execution. The MCP server lets an agent work directly with Marvin: discover a project by stable ID, read its work, create a task, or complete it.
Use the plugin for vault-coupled work such as imports, managed daily-note regions, and source-note associations. Use the MCP for Marvin-only work. The two surfaces share the same client, local-first read behavior, cache rules, and error model. Run the relevant plugin refresh or import to project a task an agent creates through MCP into the vault.
The two are independent: the MCP server works without the plugin and without Obsidian, and the plugin works without the MCP server. They're in one repository today because they share a Marvin client; the server is expected to be spun out on its own once it settles.
Plugin overview
The Amazing Marvin Plugin provides a way to bring your tasks and project structures from Amazing Marvin directly into your Obsidian vault. It respects the Amazing Marvin hierarchy of categories and projects, creating a matching folder and note structure within Obsidian.
Key Features
- Non-destructive imports: Category, project, and Inbox notes refresh only their managed region; adjacent prose and non-plugin frontmatter survive.
- Hierarchy and selective roots: Import everything or selected category/project roots with their descendants while keeping ancestor notes as navigation-only structure.
- Task projection: Render nested Marvin tasks as checklists, with deep links, parent navigation, optional labels-as-tags, and Dataview or Obsidian Tasks-compatible metadata.
- Task creation and completion: Create a task at the cursor, defaulting to the current imported category/project when applicable; optionally mark linked tasks complete in Marvin when checked in Obsidian.
- Refreshable daily notes: A bounded Today region keeps due and scheduled tasks current without rerunning a template or overwriting the rest of a note.
- Agent-ready API: Templater and other in-Obsidian automation can use a typed API with idempotent source/action task creation. The MCP server shares the Marvin client for Marvin-only workflows, and runs with or without the plugin.
Usage Instructions
Sync Direction
Amazing Marvin remains the source of truth for imported data. The plugin does not sync arbitrary Obsidian edits back to Marvin. The deliberate exceptions are creating a Marvin task from Obsidian and, when enabled, marking a linked Marvin task done after its checklist item is checked.
Sync Behavior
Each import updates only the Amazing Marvin-managed region in an existing note.
Custom frontmatter properties and prose before or after that region are
preserved. The first import of an older note adopts its recognizable generated
category/project or Inbox task section; future imports use explicit markers.
The importer also repairs the known malformed legacy list syntax before
writing native YAML arrays such as labelIds.
The managed folder defaults to AmazingMarvin and can be changed in plugin
settings. Existing imported categories, projects, and Inbox notes are moved by
their Marvin ID when possible; empty folders from an earlier location are left
in place. Notes for Marvin items no longer returned by the API are also left in
place rather than deleted automatically.
Imports can include all categories/projects or selected roots. A selected root includes all descendants; its ancestors remain as navigation-only notes so the Marvin hierarchy and backlinks stay intact, while sibling branches and ancestor tasks are excluded. Inbox import is controlled independently. An empty selected-root list intentionally imports no category/project notes, and changing the selection never deletes notes from an earlier import.
Running a Sync
To initiate a sync:
- Open Obsidian's Command Palette with
Ctrl/Cmd + P. - Search for and select the command
Amazing Marvin Integration: Import categories and tasks. - The plugin updates its managed regions from the current Marvin structure and task data.
Once imported, your Obsidian vault will contain the configured managed folder. Inside, you'll find the structured notes corresponding to your categories and projects from Amazing Marvin.
Before importing, use Settings → Amazing Marvin Integration → Category and project import to choose the managed folder, all items or selected roots, and whether Inbox is included. A selected root includes all descendants; changing the selection deliberately leaves older notes in place for review.
Creating a Marvin Task
The task creation dialog is designed to mirror the task input experience in Amazing Marvin closely. It includes the following features:
- Autocomplete for Categories and Projects using
#syntax or a search sub-dialog. - Recognizes shorthand notations for properties like start date (
~), due date (@), and labels (+). - Places a link to the Marvin task as a deep link in Obsidian at the cursor location upon task creation.
- The created Marvin task links back to the Obsidian note that instigated the task.
- The source note records the Marvin task ID and deep link in its
amazing-marvin-actionsfrontmatter property. - The link can use either Advanced URI (the default, for the Advanced URI community plugin) or Obsidian's standard URI format.
When invoked from an imported category or project note, the modal and the selected-text shortcut default to that Marvin item. You can still choose Inbox or another parent in the modal.
To create a task:
- Open Obsidian's Command Palette with
Ctrl/Cmd + P. - Search for and select the command
Amazing Marvin Integration: Create task. - Input the task details and select the appropriate category from the dropdown, which shows suggestions as you type.
- Upon task creation, a markdown checklist item with a link to the Marvin task is inserted at your cursor location in Obsidian.
Keeping Today's Tasks Current
Run Amazing Marvin Integration: Refresh today's tasks from a daily note. On
the first run, the plugin adopts existing Marvin checklist entries under
## Today's tasks as the morning set and surrounds the recognized generated
task content with managed HTML-comment markers. Content outside the recognized
legacy checklist and later managed region is preserved.
Once the managed region exists, completed tasks stay in it: a checked Marvin line is kept even after Marvin's reads stop returning it, so the note records what the day contained rather than only what is still outstanding. The exception is that one-time adoption of an older note, which builds the region from Marvin's current reads and so cannot recover already-completed history — copy that out before the first refresh.
Later scheduled and due tasks appear under ### Added since morning. Results
are deduplicated by Marvin task ID, completion state is rerendered from Marvin,
and a successful empty response remains visibly distinct from a fetch failure.
A failed fetch leaves the existing note untouched.
Once a note has a managed region, the plugin can refresh it on startup, when Obsidian regains focus, and at the configured interval. Automatic refresh does not initialize or adopt an unmarked note; run the command once (or use the API below) to establish the boundary.
Templater and In-Obsidian Automation
The plugin exposes a stable object API at
app.plugins.plugins["cloudatlas-o-am"].api. For example:
const marvin = app.plugins.plugins["cloudatlas-o-am"].api;
const sourcePath = tp.file.path(true);
await marvin.ensureTaskForSource({
sourcePath,
actionKey: "decide-whether-to-pursue",
title: "Decide whether to pursue Titan AI",
day: tp.date.now("YYYY-MM-DD"),
});
await marvin.refreshTodayTasks({
date: tp.date.now("YYYY-MM-DD"),
filePath: tp.file.path(true),
});
actionKey is a caller-owned stable identity for one action in one source
note. Do not derive it from the mutable task title. Repeating the same
sourcePath and actionKey returns the existing Marvin association.
The API writes a pending source association before creating the Marvin task.
If a connection drops at an ambiguous point, a repeat is stopped rather than
silently creating a duplicate. After inspecting Marvin, callers can use
resolvePendingSourceAction({ sourcePath, actionKey, taskId }) or explicitly
clearPendingSourceAction({ sourcePath, actionKey }).
Additional object-returning methods are available for automation:
getToday(date)getDue(date)getTodayAndDue(date)getCategories()getChildren(parentId)getLabels()createTask(task)ensureTaskForSource(input)refreshTodayTasks(input)
Task formatting and labels
The default projection remains the existing Dataview format. In settings, tasks can instead use Obsidian Tasks' Dataview fields or emoji date format. Tasks-compatible presets always put the readable title first; the current Dataview preset has a separate title-first option.
Dataview date links use a configurable Moment format. For example,
YYYY-[W]WW renders 2026-07-23 as [[2026-W30|2026-07-23]], which lets a
daily date alias resolve to a weekly note. An optional task tag supports an
Obsidian Tasks global filter.
Marvin labels can be projected as namespaced Obsidian tags such as
#marvin/Knowledge-work. Label IDs are resolved through the limited /labels
API and cached for an hour; unknown IDs are not exposed as opaque tags. If
labels are enabled and cannot be read or recovered from the stale cache, the
managed projection is left unchanged rather than silently removing tags.
Auto-Mark as Done Feature
One of the highlights in this version is the ability to auto-mark tasks as done in Amazing Marvin when they are checked off in Obsidian. When this feature is enabled in the plugin settings, checking a task off in your Obsidian note will automatically update the task status in Amazing Marvin.
Here's how to enable this feature:
- Go to
Settings > Amazing Marvin Integration. - Check the option
Attempt to mark tasks as done in Amazing Marvin when checked off in Obsidian. - Save your settings.
Now, when you check off a task with an Amazing Marvin Link in an Obsidian note, a request will be sent to Amazing Marvin to mark the task as done there as well.
Important Considerations
- Managed regions: Changes inside an Amazing Marvin-managed category, project, or Inbox region are refreshed on the next import. Keep lasting notes outside the marked region.
- Conflicting moves: If a destination file already exists or multiple notes claim the same Marvin item, import stops rather than overwriting either note.
- Recoverable stale notes: Notes for removed or hidden Marvin items are not automatically deleted. Review and remove them manually.
By following these guidelines, you can ensure your Amazing Marvin data is accurately reflected in Obsidian while being mindful of the plugin's current limitations.
Installing
Using BRAT
- Install the BRAT plugin
- Open
Settings->Community Plugins - Disable safe mode, if enabled
- Browse, and search for "BRAT"
- Install the latest version of Obsidian42 - BRAT
- Open
- Open BRAT settings (
Settings->BRAT)- Scroll to the
Beta Plugin Listsection Add Beta Plugin- Specify this repository:
open-horizon-labs/obsidian-am
- Scroll to the
- Enable Amazing Marvin Integration (
Settings->Community Plugins)
Manually
- If you haven't enabled community plugins in Obsidian, follow these instructions to do so.
- From the desired release, download
main.js,manifest.json, andstyles.css. - Copy those files into
<vault>/.obsidian/plugins/cloudatlas-o-am. - Restart Obsidian and enable Amazing Marvin Integration under Community Plugins.
- Add your limited Marvin API token in the plugin settings. Find it in the Amazing Marvin API page.
Trust boundaries
The plugin reads and writes only the vault files needed for its configured workflows: imported Marvin notes and their managed regions, initialized Today regions, and task lines or source associations created through its commands or automation API. It does not delete arbitrary vault files; when an imported Marvin item disappears, its existing note is left for you to review.
The plugin uses Marvin's limited API token for its public API and can use the
local Marvin desktop API for reads when enabled. Marvin links and help links
open app.amazingmarvin.com and help.amazingmarvin.com; API requests use
serv.amazingmarvin.com or the configured local server. Keep the token in
plugin settings or a local secret mechanism, never in a shared note.
The MCP server is a separate local stdio process, usable without the plugin. It can operate on Marvin, but it does not edit the vault; use the plugin's in-Obsidian API when a workflow must both create a task and record its source note.
Development
- Ensure NodeJS and npm are installed on your system.
- Clone this repository.
- Run
npm installto install the dependencies. - Make your desired changes.
- Use
npm run devto watch for changes and compile the plugin todist/main.js. - Run
npm testfor the shared-client, plugin-adapter, and MCP contract tests. - Run
npm run buildto build the shared client, Obsidian plugin, and MCP server.
For more detailed development instructions, refer to the sample plugin provided by Obsidian.
MCP server
The repository includes a local stdio MCP server for direct agent access to Marvin. It is the right surface for reading Marvin, discovering a parent ID, creating a task, or completing a task. It never edits an Obsidian vault. Use the plugin API above when an operation must also persist a source/action association or update a managed note.
This server does not require the Obsidian plugin, or Obsidian at all. It talks to Amazing Marvin with the limited API token and nothing else. Reading the plugin's incremental cache (below) is an optional optimization for people who happen to run both; without it the server works exactly the same, just with a REST call where a cached read would have served.
It will be spun out into its own repository. It lives here for now because the plugin and the server share one Marvin client, and developing them together kept that contract honest while it settled. Expect it to move once it's stable enough to version independently. Nothing about how you configure or use it should change, but the clone path will.
Build and register it
npm ci
npm run build
The executable is:
<repository>/packages/marvin-mcp/dist/server.js
This is a Node stdio process, so it runs wherever your MCP host runs — a desktop or a server, never a phone. Everything below describes that machine; the Obsidian plugin's own mobile support is a separate matter.
The server requires AMAZING_MARVIN_API_TOKEN, Marvin's limited API token.
Do not copy that token into an MCP-host configuration file. Prefer a local
launcher that reads your existing secret at startup and then executes the
server. For example, configure the host with the launcher rather than the
token:
{
"mcpServers": {
"amazing-marvin": {
"command": "/absolute/path/to/run-amazing-marvin-mcp.mjs",
"args": []
}
}
}
That launcher should set AMAZING_MARVIN_API_TOKEN only in the spawned
process's environment and execute:
node /absolute/path/to/obsidian-am/packages/marvin-mcp/dist/server.js
For a temporary shell-only run, export the token in that shell instead of
persisting it in configuration. AMAZING_MARVIN_USE_LOCAL=true enables
local-first reads through the Amazing Marvin desktop API. The optional
AMAZING_MARVIN_LOCAL_API_URL and AMAZING_MARVIN_PUBLIC_API_URL override
their endpoints.
Reading the plugin's incremental cache (optional)
If the Obsidian plugin's experimental incremental sync (below) is enabled
for the same Amazing Marvin account, the MCP server can read the same
persisted cache file so marvin_categories/marvin_children skip a REST
round trip when it's fresh enough. This is read-only and best-effort: the
MCP server never needs the database credentials, and it falls back to REST
on any failure. Leave it unset for the existing REST-only behavior.
The MCP server and the Obsidian plugin are separate installs. Installing
or updating the plugin (via BRAT or the community store) does not update this
repository's checkout, which is what packages/marvin-mcp/dist/server.js is
built from. A checkout older than the release that introduced this feature has
no code reading the variable at all, so setting it there is silently
ignored — no warning, no error, it just keeps using REST. Before setting it:
git -C /path/to/obsidian-am fetch --tags
git -C /path/to/obsidian-am checkout <release-tag> # e.g. 0.11.0-beta3
npm --prefix /path/to/obsidian-am ci
npm --prefix /path/to/obsidian-am run build
Then set:
AMAZING_MARVIN_INCREMENTAL_CACHE_PATH— absolute path to<vault>/.obsidian/plugins/<plugin-id>/marvin-incremental-cache-v1.json. The file only exists after the plugin has run incremental sync at least once; check that it exists before pointing at it.AMAZING_MARVIN_INCREMENTAL_CACHE_MAX_AGE_MS(optional) — how stale the cache may be before REST is preferred instead. Default 10 minutes.
To confirm it's actually working, call marvin_categories and check the
result envelope: a cache hit reports "freshness": "cached" with
"origin": "local", while a REST read reports "freshness": "fresh" with
"origin": "public". If you see fresh/public with the variable set,
either the path is wrong, the cache is stale, or the build predates this
feature.
Asking for a fresh cache on a specific call
By default this reading is passive: the cache is used if the plugin already
happened to sync it, and otherwise the read falls through to REST. When a
particular question needs current data, marvin_categories and
marvin_children accept an optional refresh: true parameter. That asks the
running plugin to sync first and waits briefly before answering.
It works by dropping a small request file next to the cache file, which the
plugin polls for and services — so the plugin keeps sole custody of the
database credentials, and nothing here opens a network listener or needs a
shared secret. Set AMAZING_MARVIN_REFRESH_TIMEOUT_MS to change how long a
refresh-requesting read waits (default 5 seconds).
The parameter defaults to false, and it is a best-effort nicety rather than
a guarantee: if Obsidian isn't running, the plugin is disabled, or the sync
fails, the wait ends and the read answers from cache or REST as it otherwise
would. Repeated calls don't stack up waiting on a plugin that isn't
answering — an unclaimed request file is treated as evidence that nothing is
listening, and the next call skips the wait until the plugin picks up again.
Because that means refresh: true can succeed, time out, or decline to try,
a read that requested one carries a refresh object saying which happened:
"refresh": { "requested": true, "outcome": "synced", "waitedMs": 820 }
outcome is synced when the plugin's checkpoint advanced, timed_out when
the request was written but no sync was observed inside the window, or
skipped when nothing was attempted — with a reason explaining why, such as
no cache being configured or an earlier request still sitting unclaimed. The
surrounding freshness/origin fields still describe where the answer came
from; this describes what the refresh did. The object is absent entirely when
refresh wasn't requested.
A refresh is serviced by the Obsidian plugin on this same machine, so
refresh: true reports timed_out when Obsidian isn't running here, or when
it is running but the plugin has incremental sync disabled.
Tool workflow
| Tool | Use it for |
|---|---|
marvin_categories | Discover stable category/project IDs and parent hierarchy. |
marvin_children | Read direct tasks/projects under a discovered parent ID. |
marvin_labels | Discover stable label IDs before task creation. |
marvin_today / marvin_due | Read scheduled or due work for an optional YYYY-MM-DD date. |
marvin_create_project | Create a project to hold tasks, before creating them. |
marvin_create_task | Create a task, optionally with parentId, dates, labels, note, and estimate. |
marvin_mark_done | Complete a task or project by stable ID. |
An agent should discover the parent with marvin_categories before supplying
parentId to marvin_create_task; it should not guess an ID from a title.
marvin_children then lets it inspect one branch without loading everything.
Read responses include origin, freshness, fetchedAt, ageMs, and
warnings. A second equivalent read may report freshness: "cached". With
local-first enabled, an unavailable or unsupported local read falls back to
the public API; a valid local empty result does not. Writes use the public API
and invalidate relevant cached reads.
Every tool returns JSON text plus structuredContent. Runtime and semantic
input errors use isError: true and a structured envelope such as:
{
"error": {
"kind": "input",
"field": "date",
"message": "Use YYYY-MM-DD"
}
}
The MCP uses the limited API token only. It does not use Marvin's full-access CouchDB database credentials.
See
docs/architecture/marvin-client-and-mcp.md
for package boundaries and the #51 extension seam.
Testing
While you're testing, you're going to send a lot of requests to the Amazing Marvin API. To avoid hitting the rate limit, you can use the Desktop local API server. See Desktop Local API Server for more information. Once setup, you can specify the local API server in the plugin settings.
The desktop API implements a subset of the public API. Unsupported local read
endpoints, historically including /api/children, fall back to the public
API. A valid empty local response does not trigger fallback.
For plugin developers
Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.