# Marvin Mcp

> An MCP server for the Amazing Marvin task manager — read and write tasks, projects, and time tracking from any MCP-compatible AI assistant.

- **Type:** MCP server
- **Install:** `agentstack add mcp-recon2026-marvin-mcp`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Recon2026](https://agentstack.voostack.com/s/recon2026)
- **Installs:** 0
- **Category:** [Integrations](https://agentstack.voostack.com/c/integrations)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Recon2026](https://github.com/Recon2026)
- **Source:** https://github.com/Recon2026/marvin-mcp

## Install

```sh
agentstack add mcp-recon2026-marvin-mcp
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Marvin MCP

`SPDX-License-Identifier: MIT` — see [LICENSE](LICENSE).

An MCP server putting Claude in front of [Amazing Marvin](https://amazingmarvin.com) —
see [docs/SPEC.md](docs/SPEC.md) for the full design (start there, not
here, if you're implementing or reviewing behavior). This file is a
macOS/Claude quickstart; see [docs/SETUP.md](docs/SETUP.md) for every
platform and MCP client this project documents, including what's not
yet verified.

**Not affiliated with, endorsed by, or sponsored by Amazing Marvin.**
This project uses Amazing Marvin's public API; "Marvin" and "Amazing
Marvin" appear here only to describe compatibility, not as an
endorsement or as this project's own branding — no logo, wordmark, or
other visual identity is borrowed, and the name is used descriptively
only. Behavior documented in this repo was verified against the live
API as of August 2026 and may drift as Marvin changes. Provided with no
warranty of any kind — see [LICENSE](LICENSE).

19 tools, matching SPEC.md §4 in full: 10 reads, 9 writes.

**Recurring tasks are read-only.** This server reads and reasons about
recurring tasks and their generated instances correctly (SPEC.md §3.12)
but never writes recurrence: it never creates, edits, or deletes a
`RecurringTasks` generator document, and never creates or deletes a
generated instance. Set up and manage recurrence in the Marvin app —
see CONTEXT.md for why this is a deliberate scope boundary rather than
an unimplemented feature.

**This server never writes reminders, habits, goals, events, or time
blocks.** Set them up, change them, or remove them in the Marvin app.
Reminders were probed live and found to need two independent writes (a
task-document field cluster plus a separate server-side record via
`POST /api/reminder/set`) that only the app's own UI keeps in sync — see
SPEC.md §3.13 for the reproduced failure mode, including an orphaned
server-side reminder that reading (not writing) reminders was the only
way to find. Habits and Goals are strategies this account has disabled
entirely (SPEC.md §7, Appendix B). Events (calendar items, a separate
document type from tasks) are readable only by an already-known ID —
nothing in the live API enumerates them — so a write there could never
be verified through this server; see SPEC.md §3.14. Time blocks are
unbuilt for the same reason as Habits/Goals: the strategy is disabled,
so the endpoint has nothing to return.

## What it looks like

One realistic session — invented tasks, not anything from a real
account:

> **You:** dump these before I forget — call the dentist, order more
> filter cartridges, look into the co-op board election, and text Priya
> back about Saturday
>
> **Claude:** Captured 4 to Inbox.
> · call the dentist
> · order more filter cartridges
> · look into the co-op board election
> · text Priya back about Saturday
>
> **You:** what should I do today
>
> **Claude:** Proposing three, not the full list:
>
> 1. **Finish Q3 budget review** (due today, ~45m) — the only hard
>    deadline today.
> 2. **Call the dentist** (from this morning) — quick, keeps it from
>    aging into next week.
> 3. **Draft the co-op board election writeup** (~30m) — no due date,
>    but it's been sitting a while and blocks two other people.
>
> Excluded on purpose: 6 recurring daily-checklist instances (filtered
> by default — say the word to see them); **order filter cartridges**
> (no urgency); **renew passport** (due in 11 weeks, not today's
> problem).
>
> Estimated load: ~1h15m against your usual free time today.
> Comfortable.
>
> **You:** anything stale I should deal with?
>
> **Claude:** One: **"Draft the co-op board election writeup"** — first
> scheduled 23 days ago, still open. Break it down, backburner it, or
> drop it?
>
> **You:** backburner it
>
> **Claude:** Done — backburnered and moved into your Backburner
> project.

## How the documentation fits together

| File | What it's for | Claude Code | Claude Desktop |
|---|---|---|---|
| [docs/SETUP.md](docs/SETUP.md) | Install, per-platform/per-client config, troubleshooting with exact error text | — | — |
| [docs/SPEC.md](docs/SPEC.md) | Index into the full design (`spec/*.md`) — data model, tool surface, guardrails, decisions already settled (§11) | Read the index at session start; read a specific `spec/*.md` file when the work touches it | **Not visible at all** |
| [CONTEXT.md](CONTEXT.md) | Why the non-obvious choices were made — read before "fixing" anything that looks like a bug | Read in full at session start | **Not visible at all** |
| [WORKFLOWS.md](WORKFLOWS.md) | The seven workflow patterns (§6) — single canonical copy, self-contained (no `§` references, no file paths) | Read in full — nothing about workflows lives in CLAUDE.md anymore | Visible **only if you paste it** into that client's system prompt / project / custom instructions field — see docs/SETUP.md §4 |
| [CLAUDE.md](CLAUDE.md) | Claude-Code-specific session behavior (read SPEC/CONTEXT first, ask before editing, venv test rule) — points to WORKFLOWS.md for everything else | Read as project instructions at session start | **Not visible at all** |
| [DESKTOP_INSTRUCTIONS.md](DESKTOP_INSTRUCTIONS.md) | One-line stub — WORKFLOWS.md is the canonical text now, this file only exists so old links don't break | Can open it, but it's not one of Code's own session-start files | Not needed — paste [WORKFLOWS.md](WORKFLOWS.md) instead |
| `CONVENTIONS.md` (gitignored — copy from [`CONVENTIONS.example.md`](CONVENTIONS.example.md)) | User-specific preferences that accumulate over time (§9.3) — empty on a fresh install | Read at session start | Not visible unless pasted |
| `logs/decisions.jsonl` | Append-only decision log (§9.2) — most recent 100 lines read back in each session | Read at session start | **Not visible** — Desktop has no file access, so there's nothing to paste even if you wanted to |

**The critical asymmetry:** a Claude Desktop chat sees none of this repository — only the 19 tools' names, schemas, and descriptions over MCP, plus whatever text you've manually pasted into that client's instructions field. That's why §4.4 requires every behavioral constraint that must hold in daily use to be written into the tool description strings themselves, not just documented here. If a rule only exists in SPEC.md or CLAUDE.md, Claude Code will follow it and a Desktop session never will.

## Safety notice

This server writes to your real Marvin task database. There is no
delete tool and no rollback built into this project — a bad write has
no undo path here beyond what you can fix by hand in the app. Take a
manual backup in Marvin (Settings → Backup, or your own export
routine) before first use, and again before trying anything unfamiliar
or run at scale, like a large Inbox consolidation.

Both `MARVIN_API_TOKEN` and `MARVIN_FULL_ACCESS_TOKEN` grant broad
account access — they are not scoped to read-only or to specific data.
Keep them only in your gitignored `.env`; never commit them, paste them
into a chat, or share them anywhere.

## Quickstart (macOS, Claude)

**Check your Python version before anything else:**

```bash
python3 --version
```

You need **3.10 or newer** — macOS ships 3.9 by default, which is too
old. The MCP SDK requires 3.10+; that's the binding constraint, not
this codebase. If yours is older:

```bash
brew install python@3.12
```

**Skip this check and you won't get a clean error — you'll get a
confusing one, later.** Creating the venv with an old `python3`
succeeds without complaint. The failure shows up one step later, at
`pip install -r requirements.txt`, as:

```
ERROR: Could not find a version that satisfies the requirement mcp[cli]==2.0.0
```

That's a Python-version problem, not a broken package or a network
issue — nothing in that message says so. If you searched for that
exact text and landed here, this is why: rerun `python3 --version`
first.

```bash
git clone 
cd marvin-mcp

python3.12 -m venv .venv
.venv/bin/pip install --upgrade pip
.venv/bin/pip install -r requirements.txt

cp .env.example .env
```

Edit `.env` — both API tokens (Marvin → Strategies → API → Settings →
View credentials) and `MARVIN_TZ` are required; everything else is
optional and only gates the one feature it belongs to. See
`.env.example`'s own comments, or [docs/SETUP.md](docs/SETUP.md) §3 for
the full walkthrough.

Verify:

```bash
.venv/bin/python src/client.py
```

Then connect a client — Claude Code or Claude Desktop on macOS are the
two verified paths, in [docs/SETUP.md](docs/SETUP.md) §4, which also
covers Windows/Linux and other MCP clients with an honest verified/
unverified marking for each. **For any client other than Claude Code**,
paste [WORKFLOWS.md](WORKFLOWS.md) into its instructions field — without
it, the assistant has the tools but none of the judgment for combining
them (docs/SETUP.md §4 explains why in full).

Hit an error? [docs/SETUP.md](docs/SETUP.md) §5 has the exact text every
config failure produces and where it shows up.

## Requirements

- Python **3.10+**. The MCP SDK requires it; the rest of the codebase
  targets 3.9 syntax but the SDK is the binding constraint. macOS ships
  3.9 by default — see [docs/SETUP.md](docs/SETUP.md) §2 for install
  commands per platform.
- A Marvin account with API access (Marvin → Strategies → API).

## Running tests

```bash
.venv/bin/python -m unittest discover tests
```

**Use the venv's Python, not a bare `python3`.** `tests/test_server.py`
needs the `mcp` package to import at all — under a system Python that
doesn't have it, all of its tests are silently skipped, not run, and the
suite still reports green. `server.py`'s own wiring (tool signatures,
delegation to `src/tools/*`) is exactly what that file covers, so a
skip-and-pass run gives no coverage of the one layer most likely to drift
when a tool's signature changes — confirmed 2026-08-11, when a stale
`test_server.py` assertion sat silently skipped through several unrelated
commits until a venv run finally caught it. `python3 -m unittest discover
tests` still works and is useful for a fast partial check, but treat it
as partial, not as "tests pass" — run the venv before reporting results.

Stdlib `unittest`, not `pytest` — kept the dependency list to exactly
what the MCP SDK needs. Unit tests run against a mocked HTTP boundary
and synthetic fixtures (§12.1); they don't touch the network or your
Marvin data. Live verification against the real API happens by hand, per
§12.2's `zzz-`-prefixed convention — there's no `doc/delete` tool, so
anything created that way needs manual cleanup in the app.

## Project layout

```
src/
├── server.py     # MCP server, tool registration — thin wiring only
├── client.py     # HTTP layer: token routing, timezone, rate limiting, retries
├── project.py    # §4.0 projection levels, applied to every read
├── fields.py     # whitelist, pairing, emptiness helpers — pure logic, no I/O
├── create.py     # two-phase creation (§4.3) + the shared existing-document write primitive
├── backup.py     # §9.4 local backup archive reader — independent of client.py, no network
├── drafts.py     # resolve_path + format_ms_as_human (§6.2) — Claude Code only, not an MCP tool
├── timeutil.py   # the one clock read every write module needs
└── tools/        # one module per write tool, plus reads.py for the remaining read tools
```

`CONVENTIONS.md` accumulates user-specific preferences over time (§9.3)
— empty on a fresh install, and that's expected. Gitignored, since it
fills with your own real preferences; copy
[`CONVENTIONS.example.md`](CONVENTIONS.example.md) to get started.

## What's untested

Everything below is unit-tested against mocked HTTP or synthetic
fixtures, but has never fired against the live API or real account data.
Not a defect list — most of these need a real-world state this account
doesn't currently have (a recurring task, a task with a clock time set
through the app's own UI, an actual rate-limit response). Listed so a
future reader doesn't mistake "passes the test suite" for "confirmed
against reality."

- **The `clock_time` disagreement states** (`fields.parse_title_time_prefix`
  and its use in `update_item`/`project.py`) are unit-tested against
  synthetic documents covering all three states — prefix and `taskTime`
  agreeing, prefix present with `taskTime` empty, `taskTime` set with no
  prefix — but never against a document the Marvin app itself produced.
  All real `clock_time` writes in this account so far went through this
  server, which always keeps both fields in sync; whether the app's own
  UI actually produces the "prefix present, `taskTime` empty" or
  "`taskTime` set, no prefix" states the way Finding 2's live probing
  suggested is unconfirmed against a document this server didn't write
  itself.
- **The §4.9 recurring-task refusal's primary path is live-confirmed**
  (2026-08-11, once real recurring tasks existed in the account — see
  §3.12): calling `mark_done` on a generated instance refused correctly,
  citing both `recurring: true` and the matching `recurringTaskId`, with
  no write attempted. The **fallback path** — `recurringTaskId` present
  without `recurring: true` — is **unit-tested only**, not live-fired:
  every generated instance actually observed carries both signals
  together, so there's no real document to exercise the fallback against
  without fabricating one, which would test this server's handling
  against a state Marvin itself doesn't produce. The non-refusal path (a
  normal task with `recurringTaskId: ""`) remains live-confirmed too.
- **`get_inbox`/`get_today`/`get_due`'s recurring-instance filter is
  live-confirmed for the response shape and two of its three paths, not
  the third.** Live-confirmed: `include_recurring=True` returning real
  generated instances with `excludedRecurringCount: 0` (the bypass
  path), and the default call returning `excludedRecurringCount: 0` when
  no recurring instances exist (the nothing-to-exclude path) — both
  against the real account. **Not live-fired: the default call actually
  excluding a nonzero count while recurring instances exist.** The
  window to check this closed mid-session — by the time the bypass path
  had been exercised, the account's recurring-task instances were
  cleaned up (§3.12.3), leaving nothing left to filter for the third
  check. Unit-tested only (`TestRecurringFilter` in `test_reads.py`).
- **429/5xx retry-with-backoff** has never been exercised — every real
  call this session either succeeded or failed with a definitive 4xx.
  The retry loop's behavior under an actual rate limit or server error is
  unverified in practice.
- **`MarvinNoResponseError`** (no response received, don't retry) has
  never been triggered by a real network failure. `fast_capture`'s
  handling of it is unit-tested with a simulated exception, not a real
  dropped connection.
- **`MarvinSessionCeilingWarning` firing mid-`create`** was verified with
  the real `client.py` and `create.py` working together, but with the
  call counter fast-forwarded to 800 and only the network layer mocked —
  not by actually making 800 live calls in one session.
- **`get_children`, `get_item`, `get_today`, `get_due`, `get_labels`, and
  `get_time_tracks`'s own wrapper functions** (in `src/tools/reads.py`)
  have had their underlying endpoints verified live (correct query
  params, correct response shape), but the wrapper functions themselves
  — with projection, caching, or chunking applied — were mostly exercised
  via raw `client` calls during endpoint discovery, not by calling the
  actual tool. `get_tree`'s caching is the one exception: it was
  explicitly instrumented and verified end-to-end, including
  invalidation.
- **`read_backup`'s `summary`, `completion_history`, `project_trajectory`,
  and `extract_subtree` operations** are unit-tested against synthetic
  snapshots only. Only `delta` has been run against the real backup
  archive — the account's actual archive is

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Recon2026](https://github.com/Recon2026)
- **Source:** [Recon2026/marvin-mcp](https://github.com/Recon2026/marvin-mcp)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-recon2026-marvin-mcp
- Seller: https://agentstack.voostack.com/s/recon2026
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
