# https://github.com/PrefectHQ/marvin Project Manual

Generated at: 2026-06-20 20:01:45 UTC

## Table of Contents

- [Marvin Overview and Architecture](#page-overview)
- [Core Abstractions: Tasks, Agents, and Threads](#page-core-abstractions)
- [Structured-Output Functions and JSON Schema Utilities](#page-structured-output)
- [Memory Providers, Tools, and MCP Integration](#page-memory-tools)

<a id='page-overview'></a>

## Marvin Overview and Architecture

### Related Pages

Related topics: [Core Abstractions: Tasks, Agents, and Threads](#page-core-abstractions)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [README.md](https://github.com/PrefectHQ/marvin/blob/main/README.md)
- [src/marvin/agents/CLAUDE.md](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/agents/CLAUDE.md)
- [src/marvin/agents/agent.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/agents/agent.py)
- [src/marvin/engine/CLAUDE.md](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/engine/CLAUDE.md)
- [src/marvin/fns/plan.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/fns/plan.py)
- [src/marvin/fns/summarize.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/fns/summarize.py)
- [src/marvin/fns/generate.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/fns/generate.py)
- [src/marvin/cli/config.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/cli/config.py)
- [src/marvin/cli/dev.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/cli/dev.py)
- [src/marvin/_internal/integrations/README.md](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/_internal/integrations/README.md)
- [examples/slackbot/README.md](https://github.com/PrefectHQ/marvin/blob/main/examples/slackbot/README.md)
</details>

# Marvin Overview and Architecture

Marvin is a Python framework for producing structured outputs and building agentic AI workflows. It provides an intuitive API for defining workflows and delegating work to LLMs, including utilities for casting, classifying, extracting, and generating structured data; discrete observable **tasks**; specialized **agents**; and **threads** for orchestrating complex behaviors. Source: [README.md:1-15]()

## Purpose and Scope

Marvin sits at the intersection of two audiences: users coming from Marvin 2.x who want familiar structured-output helpers, and former ControlFlow users who want a powerful agentic engine. Marvin 3.0 unifies both by combining the DX of Marvin 2.0 with the engine previously shipped as ControlFlow, and it uses Pydantic AI as its underlying LLM runtime, supporting the full range of providers that Pydantic AI supports. Source: [README.md:130-150]()

The framework's high-level role is to give developers a type-safe, task-centric model for AI work: rather than hand-rolling prompt chains, users express work as `Task` and `Agent` objects and let Marvin's orchestrator manage dependencies, tool calls, memory, and state. Source: [src/marvin/engine/CLAUDE.md:1-12]()

## Core Abstractions

### Tasks

Tasks are the fundamental unit of work in Marvin. Each task represents a clear objective that an AI agent can accomplish. The simplest invocation is `marvin.run("Write a haiku about coding")`, which returns a string. Source: [README.md:175-200]()

Tasks can also be defined explicitly via `marvin.Task(...)` with a `result_type` for type-safe returns, and they can be chained so that earlier outputs flow into later context:

```python
with marvin.Thread() as thread:
    research = marvin.run("Research recent AI developments")
    outline = marvin.run("Create an outline", context={"research": research})
    draft = marvin.run("Write the first draft", context={"outline": outline})
```

Source: [README.md:80-95]()

For complex objectives, `marvin.plan(...)` returns a list of dependent `Task` objects that can then be executed with `marvin.run_tasks(tasks)`, providing smart planning and progress tracking. Source: [src/marvin/fns/plan.py:1-30]()

### Agents

An `Agent` is a stateful, autonomous AI entity that can use tools, maintain memory, and chain tool calls to accomplish a task. Agents are configured with `instructions`, a `name`, optional tools, and an optional `Model`. They extend the lower-level `Actor` concept and integrate with Pydantic AI for model access. Source: [src/marvin/agents/agent.py:1-30](), [src/marvin/agents/CLAUDE.md:1-25]()

Memory is automatically managed and persisted across calls, and the agent's loop is driven by the orchestrator rather than by a single LLM invocation. Source: [src/marvin/agents/CLAUDE.md:20-30]()

### Threads

Threads replace the older "Flow" concept. They act as a context manager that maintains a shared conversation history across multiple `marvin.run` calls. Thread/message history is persisted in SQLite, and an explicit `id` can be supplied for later recovery. Source: [README.md:155-170]()

```python
with marvin.Thread(id="optional-id-for-recovery"):
    marvin.run("do something")
    marvin.run("do another thing")
```

Source: [README.md:155-168]()

## Engine and Orchestration

The Marvin engine is the task orchestration layer that coordinates actors, conversations, and tool calls. Its key files are `orchestrator.py`, `streaming.py`, `end_turn.py`, `events.py`, and `llm.py`. Source: [src/marvin/engine/CLAUDE.md:1-20]()

The orchestrator's high-level flow is:

1. **Task Collection** via `get_all_tasks()` to gather ready tasks and dependencies.
2. **Tool Assembly** that combines regular tools with end-turn tools (e.g., `MarkTaskSuccessful`, `DelegateToActor`, `PostMessage`) from tasks and actors.
3. **Memory Integration** that auto-searches memories based on recent messages.
4. **System Prompt** built from the actor, instructions, and assigned tasks via the `SystemPrompt` Jinja template.
5. **Agent Execution** that runs a Pydantic AI agent with streaming event handling.
6. **Turn Management** that processes end-turn tools and updates task states.

Source: [src/marvin/engine/CLAUDE.md:15-30]()

```mermaid
flowchart TD
    A[Collect ready tasks] --> B[Assemble tools + end-turn tools]
    B --> C[Search memory for context]
    C --> D[Build system prompt]
    D --> E[Run Pydantic AI agent with streaming]
    E --> F[Process end-turn tools]
    F --> G[Update task states]
    G --> A
```

## Structured-Output Utilities

In addition to the task/agent model, Marvin exposes top-level helpers that wrap a single LLM call into a typed operation:

| Utility | Purpose |
|---|---|
| `marvin.run` | Execute a task with an AI agent |
| `marvin.summarize` | Concise summary of text via a language model |
| `marvin.classify` | Categorize data into predefined classes |
| `marvin.extract` | Pull structured information from text |
| `marvin.cast` | Transform data into a different type |
| `marvin.generate` | Create structured data from a description |

Source: [README.md:110-125](), [src/marvin/fns/summarize.py:1-25](), [src/marvin/fns/generate.py:1-30]()

All of these helpers accept an optional `agent`, `thread`, `context`, and `prompt`, and they build a `marvin.Task[...]` internally to drive execution. Source: [src/marvin/fns/summarize.py:50-75]()

## CLI, Integrations, and Known Issues

Marvin ships a small CLI surface. `marvin config view` prints the current settings tree, and `marvin config get <key>` retrieves a single setting. Source: [src/marvin/cli/config.py:1-18]() The dev CLI includes `marvin dev docs` to launch the Mintlify documentation locally. Source: [src/marvin/cli/dev.py:9-25]()

For external tooling, Marvin integrates with FastMCP servers via a lazy-loading adapter in `_internal/integrations/fastmcp.py` that uses duck typing and `_FastMCPImportState` to manage optional dependencies. Source: [src/marvin/_internal/integrations/README.md:1-30]() The MCP lifecycle is managed by the orchestrator so that servers stay alive for the session rather than being restarted on every `agent.run()`. Source: release notes for v3.2.5

A real-world deployment example is the Marvin Slackbot, which uses GPT-5 or Claude, persists memories in TurboPuffer, and is configured via Prefect Secrets/Variables and a `MARVIN_SLACKBOT_*` env namespace. Source: [examples/slackbot/README.md:1-40]()

Several known limitations are worth noting:

- Passing a `pydantic_ai.messages.BinaryImage` to `marvin.cast()` can blow past token limits for moderate images. Source: issue #1246
- Bare `typing.List` (without a type parameter) crashes `is_classifier`/`as_classifier` with `IndexError`. Source: issue #1355
- `from __future__ import annotations` can cause AI functions to fall back to string returns when type resolution is deferred. Source: issue #950
- `schema_to_type()` in `src/marvin/utilities/jsonschema.py` raises `IndexError` for `{"type": ["null"]}` and `TypeError` for `{"type": "array", "items": []}`. Source: issues #1351, #1353
- A defense for OWASP ASI06 (memory poisoning) has been requested for AI functions. Source: issue #1347

## See Also

- [Marvin 3.0 Migration Notes](README.md)
- [Engine / Orchestrator](src/marvin/engine/CLAUDE.md)
- [Agents](src/marvin/agents/CLAUDE.md)
- [FastMCP Integration](src/marvin/_internal/integrations/README.md)
- [Slackbot Example](examples/slackbot/README.md)

---

<a id='page-core-abstractions'></a>

## Core Abstractions: Tasks, Agents, and Threads

### Related Pages

Related topics: [Marvin Overview and Architecture](#page-overview), [Structured-Output Functions and JSON Schema Utilities](#page-structured-output), [Memory Providers, Tools, and MCP Integration](#page-memory-tools)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [src/marvin/tasks/task.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/tasks/task.py)
- [src/marvin/agents/agent.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/agents/agent.py)
- [src/marvin/agents/team.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/agents/team.py)
- [src/marvin/agents/actor.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/agents/actor.py)
- [src/marvin/agents/names.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/agents/names.py)
- [src/marvin/thread.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/thread.py)
- [src/marvin/engine/orchestrator.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/engine/orchestrator.py)
- [src/marvin/engine/CLAUDE.md](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/engine/CLAUDE.md)
- [src/marvin/agents/CLAUDE.md](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/agents/CLAUDE.md)
- [src/marvin/fns/plan.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/fns/plan.py)
- [src/marvin/_internal/integrations/README.md](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/_internal/integrations/README.md)
- [README.md](https://github.com/PrefectHQ/marvin/blob/main/README.md)
</details>

# Core Abstractions: Tasks, Agents, and Threads

Marvin is built around three cooperating abstractions that together turn LLM calls into inspectable, composable workflows. **Tasks** describe what should be done, **Agents** are the entities that actually do the work, and **Threads** are the conversation/session container that connects them. All higher-level helpers (`marvin.run`, `marvin.classify`, `marvin.extract`, `marvin.summarize`, `marvin.cast`, `marvin.generate`, `marvin.plan`) are thin façades that construct a `Task` and route it through the same engine (Source: [README.md](https://github.com/PrefectHQ/marvin/blob/main/README.md)).

```mermaid
flowchart LR
    User[User Code] -->|runs| Task[Task<br/>instructions + result_type]
    Task -->|assigned to| Agent[Agent<br/>name + instructions + tools]
    Agent -->|executes via| Orch[Orchestrator]
    Thread[Thread<br/>shared history + memory] --- Orch
    Orch -->|end_turn tools| Result[Typed Result]
    MCP[MCP Servers] -.->|tools| Agent
    Memory[Memory] -.->|context| Orch
```

## Tasks

A `Task` is the unit of work. It is a generic, type-parameterized class — `marvin.Task[T]` — where `T` is the Python type the model is expected to produce (Source: [src/marvin/tasks/task.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/tasks/task.py)). Tasks carry `instructions`, an optional `context` dict, a list of `tools`, and a `result_type`. When a task is executed, the orchestrator sends the system prompt plus tools to the model and validates the response against `result_type` before returning it (Source: [src/marvin/engine/CLAUDE.md](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/engine/CLAUDE.md)).

Tasks are observable and stateful: they progress through states tracked by the engine and are persisted alongside the thread. The simplest usage is `marvin.run("Write a haiku about coding")`, but explicit construction lets you tune the result type, agents, and dependencies:

```python
import marvin
from pydantic import BaseModel

class Article(BaseModel):
    title: str
    content: str
    key_points: list[str]

article = marvin.run(
    "Write an article using the research",
    result_type=Article,
    context={"research": research},
)
```

Tasks can also be **planned**. `marvin.plan(...)` internally constructs `marvin.Task[list[PlanTask]]`, asks the LLM to decompose an objective, then expands that into a list of real `Task` objects that `marvin.run_tasks` will execute in dependency order (Source: [src/marvin/fns/plan.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/fns/plan.py)).

> **Common pitfall:** Putting `from __future__ import annotations` at the top of a module turns all type hints into strings, which breaks Marvin's runtime type resolution and causes AI functions to return `str` instead of structured objects. See [issue #950](https://github.com/PrefectHQ/marvin/issues/950).

## Agents

An `Agent` is a stateful actor that can use tools and maintain memory (Source: [src/marvin/agents/CLAUDE.md](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/agents/CLAUDE.md)). Concretely, `Agent` is a dataclass subclass of `Actor` that adds tool orchestration, model configuration, and event handling (Source: [src/marvin/agents/agent.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/agents/agent.py)). Agents are specialized (via `instructions`), portable (reusable across tasks), collaborative (`Team` lets multiple agents be assigned to a single task), and customizable (model, temperature, etc.) (Source: [README.md](https://github.com/PrefectHQ/marvin/blob/main/README.md)).

```python
from marvin import Agent

writer = Agent(
    name="Writer",
    instructions="Write clear, engaging content for a technical audience",
)
marvin.run("Write an article", agent=writer)
```

Under the hood, agents integrate with **Pydantic AI** for model invocation, support **MCP servers** as tool sources, and stream events through registered handlers. The agent class also dispatches `handle_event` callbacks to both sync and async handler lists (Source: [src/marvin/agents/agent.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/agents/agent.py)). Lazy-loaded FastMCP support lets agents transparently use tools exposed by a FastMCP server without making `fastmcp` a hard dependency (Source: [src/marvin/_internal/integrations/README.md](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/_internal/integrations/README.md)).

> **MCP lifecycle note:** In v3.2.5 the engine was changed so that MCP server subprocesses are started **once per session** rather than for every `agent.run()` call. Earlier versions repeatedly spawned and tore down the server, which caused both latency and intermittent `TimeoutError`s during initialization (Source: release [v3.2.5](https://github.com/PrefectHQ/marvin/releases/tag/v3.2.5)). v3.2.7 additionally merges user-specified env vars on top of `os.environ` so essential variables like `PATH` and `HOME` are no longer dropped when configuring `MCPServerStdio` (Source: release [v3.2.7](https://github.com/PrefectHQ/marvin/releases/tag/v3.2.7)).

## Threads

A `Thread` is the conversation/session container — the Marvin 3.0 replacement for ControlFlow's `Flow`. It is used as a context manager and may carry an optional `id` for later recovery:

```python
import marvin

with marvin.Thread(id="optional-id-for-recovery"):
    marvin.run("do something")
    marvin.run("do another thing")
```

(Source: [README.md](https://github.com/PrefectHQ/marvin/blob/main/README.md); [src/marvin/thread.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/thread.py)).

Threads are what give Marvin its **stateful** character. They:

- Persist message history in SQLite (no migration tooling yet — expect to reset data during upgrades) (Source: [README.md](https://github.com/PrefectHQ/marvin/blob/main/README.md)).
- Share context and history between tasks executed inside the same `with` block (Source: [README.md](https://github.com/PrefectHQ/marvin/blob/main/README.md)).
- Auto-search memories based on recent messages before each model call (Source: [src/marvin/engine/CLAUDE.md](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/engine/CLAUDE.md)).
- Act as the unit of recovery: passing `thread="some-id"` reattaches new tasks to a prior conversation.

Because every high-level helper is a task, and every task runs inside the active thread, threads are also the natural boundary for **memory poisoning defenses** (OWASP ASI06) — adversarial content embedded in one task's output should be validated before being written into the thread's persisted memory (Source: [issue #1347](https://github.com/PrefectHQ/marvin/issues/1347)).

## The Engine: How the Three Connect

The `Orchestrator` is what actually drives execution. Per the engine module's own documentation, the flow is: collect ready tasks → assemble tools (regular + end-turn tools from tasks/agents) → integrate memory → build a system prompt from the actor, instructions, and tasks → run the Pydantic AI agent with streaming events → process `EndTurn` subclasses (`MarkTaskSuccessful`, `DelegateToActor`, `PostMessage`, etc.) to update task state (Source: [src/marvin/engine/CLAUDE.md](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/engine/CLAUDE.md)). Tools are wrapped with `wrap_tool_errors` so a tool failure surfaces as a recoverable model message rather than a hard crash (Source: [src/marvin/agents/agent.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/agents/agent.py)).

For workloads that need true concurrency, v3.2.1 enabled parallel execution of independent tasks within a thread, and v3.2.2 fixed `Optional` result types returning `None` (Source: releases [v3.2.1](https://github.com/PrefectHQ/marvin/releases/tag/v3.2.1), [v3.2.2](https://github.com/PrefectHQ/marvin/releases/tag/v3.2.2)).

## See Also

- [Engine and Orchestrator](engine-and-orchestrator.md) — deep dive into the execution loop
- [Structured-Output Helpers](structured-output-helpers.md) — `cast`, `classify`, `extract`, `generate`, `summarize`
- [Planning and Threads](planning-and-threads.md) — `marvin.plan` and thread persistence
- [MCP and Tool Integration](mcp-and-tool-integration.md) — wiring external tool servers into agents

---

<a id='page-structured-output'></a>

## Structured-Output Functions and JSON Schema Utilities

### Related Pages

Related topics: [Core Abstractions: Tasks, Agents, and Threads](#page-core-abstractions)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [README.md](https://github.com/PrefectHQ/marvin/blob/main/README.md)
- [src/marvin/utilities/jsonschema.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/utilities/jsonschema.py)
- [src/marvin/fns/generate.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/fns/generate.py)
- [src/marvin/fns/summarize.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/fns/summarize.py)
- [src/marvin/fns/plan.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/fns/plan.py)
- [src/marvin/cli/main.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/cli/main.py)
- [src/marvin/cli/config.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/cli/config.py)
- [src/marvin/agents/agent.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/agents/agent.py)
</details>

# Structured-Output Functions and JSON Schema Utilities

## Overview and Scope

Marvin is a Python framework for producing structured outputs and building agentic AI workflows. At the core of its "structured output" promise sits a collection of high-level functions (`marvin.cast`, `marvin.classify`, `marvin.extract`, `marvin.generate`, `marvin.summarize`, `marvin.plan`) plus a dedicated JSON Schema-to-Python-type converter that bridges raw JSON Schema documents with Pydantic-validated Python types. Source: [README.md](https://github.com/PrefectHQ/marvin/blob/main/README.md).

The structured-output functions are designed to be composable. Each function wraps a `marvin.Task` and delegates LLM interaction to Pydantic AI, then validates the model's reply against a target Python type. The pipeline is:

1. The user supplies a target type (Python class, dataclass, or JSON Schema).
2. The framework constructs a `Task` with that `result_type`.
3. The LLM is instructed to emit JSON matching the type's schema.
4. The response is parsed and validated through Pydantic.

This same pipeline is reused inside the [src/marvin/fns/generate.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/fns/generate.py) module, where `generate_schema_async()` instructs the model to produce a `JSONSchema` document, illustrating the round-trip capability between Python types and JSON Schema.

## High-Level Functions

### Cast, Classify, Extract, Generate

The README documents five "keep-it-simple" utilities:

| Function | Purpose |
|---|---|
| `marvin.run` | Execute any task with an AI agent |
| `marvin.summarize` | Get a quick summary of a text |
| `marvin.classify` | Categorize data into predefined classes |
| `marvin.extract` | Pull structured information from text |
| `marvin.cast` | Transform data into a different type |
| `marvin.generate` | Create structured data from a description |

All of these accept an optional `agent`, `thread`, `context`, `handlers`, and `prompt`, and return through a common `run_sync` wrapper. Source: [src/marvin/fns/summarize.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/fns/summarize.py) — `summarize_async()` shows the canonical pattern: build a `task_context` dict, optionally append user `instructions` to the default prompt, then call `marvin.Task[str](...).run_async(thread=thread, handlers=handlers)`.

### Plan and Thread Orchestration

`marvin.plan()` returns a list of `marvin.Task` objects that together satisfy a high-level objective; `marvin.run_tasks()` executes them. Source: [src/marvin/fns/plan.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/fns/plan.py). Threads act as context managers that share message history between tasks, defined in `marvin.Thread()`. Each `Agent` participating in a task is a dataclass that owns its own `Model`, `instructions`, and optional MCP servers. Source: [src/marvin/agents/agent.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/agents/agent.py).

## JSON Schema Utilities

The most relevant implementation file is [src/marvin/utilities/jsonschema.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/utilities/jsonschema.py). Its top-level docstring lists the supported conversions:

- Basic types: `string`, `number`, `integer`, `boolean`, `null`
- Complex types: arrays, objects
- Format constraints: `date-time`, `email`, `uri`
- Numeric constraints: `minimum`, `maximum`, `multipleOf`
- String constraints: `minLength`, `maxLength`, `pattern`
- Array constraints: `minItems`, `maxItems`, `uniqueItems`
- Object properties with defaults, recursive references, enums, constants, unions

The module constructs Pydantic-friendly types via several helpers: `create_numeric_type`, `create_enum`, `create_array_type`, and `create_object_type`. These produce either plain Python classes, `dataclasses` via `make_dataclass`, `Enum` subclasses, or `Annotated` types with `Field`/`StringConstraints`. The `create_numeric_type` helper, for example, returns a `Literal[schema["const"]]` when the schema declares a constant, otherwise an `Annotated[int|float, Field(...)]` with the relevant constraints. Source: [src/marvin/utilities/jsonschema.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/utilities/jsonschema.py).

The companion `jsonschema_to_type()` function enables arbitrary JSON Schema documents to drive Marvin's structured-output pipeline, allowing users to supply schemas that were not authored in Python.

```mermaid
flowchart LR
    A[User Input / Python Type] --> B{Structured Function}
    B -->|marvin.cast| C[Construct Task]
    B -->|marvin.extract| C
    B -->|marvin.generate| C
    C --> D[Pydantic AI Model]
    D --> E[Raw JSON]
    E --> F[JSON Schema Validator]
    F --> G[Pydantic-Validated Result]
    G --> H[Return Value]
```

## Known Issues and Community-Reported Limitations

The community has surfaced several failure modes that practitioners should be aware of:

- **Token explosion with binary inputs** — Passing a `pydantic_ai.messages.BinaryImage` to `marvin.cast()` can blow past model token limits even for moderately sized images (e.g., a 200 KB PNG). See [issue #1246](https://github.com/PrefectHQ/marvin/issues/1246).
- **Bare `typing.List` crashes** — `is_classifier()` and `as_classifier()` call `get_args(typ)[0]` without checking that `get_args()` is non-empty, so a parameter typed `List` (without a type argument) raises `IndexError`. See [issue #1355](https://github.com/PefectHQ/marvin/issues/1355).
- **`schema_to_type` IndexError on `["null"]`** — A schema whose `type` field is the list `["null"]` raises `IndexError: list index out of range` at line 302 of `src/marvin/utilities/jsonschema.py`. See [issue #1353](https://github.com/PrefectHQ/marvin/issues/1353).
- **`create_array_type` TypeError on empty items** — A schema like `{"type": "array", "items": []}` (valid JSON Schema for a zero-element tuple) raises `TypeError: Cannot take a Union of no types.`. See [issue #1351](https://github.com/PrefectHQ/marvin/issues/1351).
- **`from __future__ import annotations` returns strings** — AI functions declared under PEP 563 postponed annotations may return raw strings rather than structured types because annotations become strings at runtime. See [issue #950](https://github.com/PrefectHQ/marvin/issues/950).
- **OWASP ASI06 memory poisoning** — External content processed by Marvin functions can poison downstream memory; the project has an open feature request for mitigations. See [issue #1347](https://github.com/PrefectHQ/marvin/issues/1347).

To inspect the active configuration that drives model selection for these utilities, use `marvin config view` or `marvin config get <key>`, which read from `marvin.settings`. Source: [src/marvin/cli/config.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/cli/config.py) and [src/marvin/cli/main.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/cli/main.py).

## See Also

- Marvin README — high-level overview and installation
- Agents — `marvin.Agent` and team composition
- Threads — orchestrating multi-task workflows
- MCP Integrations — `MCPServerStdio` lifecycle (v3.2.5, v3.2.7)
- Release Notes — v3.2.1 through v3.2.7 covering concurrency, optional results, and observability

---

<a id='page-memory-tools'></a>

## Memory Providers, Tools, and MCP Integration

### Related Pages

Related topics: [Core Abstractions: Tasks, Agents, and Threads](#page-core-abstractions)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [src/marvin/memory/memory.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/memory/memory.py)
- [src/marvin/agents/CLAUDE.md](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/agents/CLAUDE.md)
- [src/marvin/engine/CLAUDE.md](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/engine/CLAUDE.md)
- [src/marvin/engine/llm.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/engine/llm.py)
- [src/marvin/_internal/integrations/README.md](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/_internal/integrations/README.md)
- [src/marvin/fns/summarize.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/fns/summarize.py)
- [src/marvin/fns/generate.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/fns/generate.py)
- [src/marvin/fns/plan.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/fns/plan.py)
- [src/marvin/cli/config.py](https://github.com/PrefectHQ/marvin/blob/main/src/marvin/cli/config.py)
- [README.md](https://github.com/PrefectHQ/marvin/blob/main/README.md)
- [examples/slackbot/README.md](https://github.com/PrefectHQ/marvin/blob/main/examples/slackbot/README.md)
</details>

# Memory Providers, Tools, and MCP Integration

Marvin's agent runtime is built around three interlocking extension points: a **Memory** abstraction backed by pluggable vector-database providers, a **Tools** layer that promotes regular Python functions into LLM-callable tools, and an **MCP (Model Context Protocol)** integration that lets an agent reach external tool servers. This page describes how those pieces fit together and how to use them safely.

## Overview and High-Level Architecture

Marvin's runtime coordinates actors (LLM-driven agents) over an `Orchestrator` that runs each task turn, assembles available tools, queries memory, builds a system prompt, and dispatches the resulting call to a Pydantic AI agent. Source: [src/marvin/engine/CLAUDE.md](). The orchestrator flow is summarized below.

```mermaid
flowchart TD
    A[Orchestrator.run] --> B[Collect ready tasks]
    B --> C[Assemble regular + end-turn tools]
    C --> D[Query Memory providers for context]
    D --> E[Build SystemPrompt<br/>actor + tasks + memory]
    E --> F[Run pydantic-ai agent<br/>with streaming events]
    F --> G{End-turn tool fired?}
    G -- MarkTaskSuccessful --> H[Task SUCCESS]
    G -- DelegateToActor --> I[Hand off to another agent]
    G -- PostMessage --> J[Continue turn]
    G -- none --> K[Continue tool loop]
    H --> L[Persist to thread DB]
    I --> L
    J --> L
    K --> F
```

Agents are stateful, expose typed Python tools, automatically persist conversation history, and can chain tool calls across turns. Source: [src/marvin/agents/CLAUDE.md](). MCP servers extend the same tool model by importing remote tools into the agent's available toolset at run time.

## Memory: `Memory` Class and Providers

The `Memory` class models a partitioned collection of memories that is stored in a vector database configured by a `MemoryProvider`. Source: [src/marvin/memory/memory.py](""). Its most important fields are:

| Field | Type | Purpose |
| --- | --- | --- |
| `key` | `str` | Alphanumeric/underscore identifier; sanitized in `__post_init__`. |
| `instructions` | `str \| None` | Tells the agent when/how the memory should be used. |
| `provider` | `MemoryProvider` | Backing vector store (resolved from string via `get_memory_provider`). |
| `auto_use` | `bool` | If true, the orchestrator queries the memory before each agent run using the most recent messages. |
| `prompt` | `str \| Path` | Jinja template for the memory's prompt (defaults to `memory.jinja`). |

Construction validates the key (alphanumerics and underscores only) and ensures a provider is configured, otherwise the post-init raises a `ValueError` directing users to configure a default provider. Source: [src/marvin/memory/memory.py](). The class exposes `add`, `delete`, `search`, and `get_tools`, where `get_tools()` converts the memory's own CRUD methods into agent-callable tools prefixed `add_memory__{key}` (and equivalents). Source: [src/marvin/memory/memory.py]().

Each `Memory` is hashed by `id(self)`, so a memory is identity-stable across calls. The `__post_init__` also calls `provider.configure(key)` to initialize the underlying store partition. Source: [src/marvin/memory/memory.py]().

The slackbot example wires memory to a vector store using the `tpuf-api-key` secret for TurboPuffer and overrides the memory-synthesis model via `marvin_memory_synthesis_model` (default `claude-haiku-4-5-20251001`). Source: [examples/slackbot/README.md]().

### Security Note: Memory Poisoning (OWASP ASI06)

Because memory content is fed back into the LLM, untrusted external data that ends up in a memory can poison later outputs (OWASP ASI06). When designing memory flows, sanitize inputs before `await memory.add(content)` and review `instructions` text that influences recall. Source: community issue [#1347](https://github.com/PrefectHQ/marvin/issues/1347).

## Tools: From Python Functions to LLM-Callable Tools

Marvin agents accept ordinary Python functions as tools; type hints drive schema generation. Source: [src/marvin/agents/CLAUDE.md](). A minimal example:

```python
from marvin import Agent

def write_file(path: str, contents: str) -> str:
    "Write the given contents to path."
    ...

agent = Agent(tools=[write_file])
result = agent.run("how to use pydantic? write to docs.md")
```

Source: [README.md]().

Internally, the orchestrator collects two categories of tools: **regular tools** (user-supplied or memory-derived) and **end-turn tools** such as `MarkTaskSuccessful`, `MarkTaskFailed`, `MarkTaskSkipped`, `DelegateToActor`, and `PostMessage`. Source: [src/marvin/engine/CLAUDE.md](). These end-turn tools are how an agent terminates its turn, escalates, or hands work to another agent — they are not user-defined but are appended by the engine on each run. Source: [src/marvin/engine/CLAUDE.md]().

Higher-level helpers (`marvin.summarize`, `marvin.generate`, `marvin.plan`) compile down to the same `marvin.Task[str]`, `marvin.Task[list[T]]`, and `marvin.run_tasks(tasks)` primitives, all of which share the agent's tool surface. Source: [src/marvin/fns/summarize.py](), [src/marvin/fns/generate.py](), [src/marvin/fns/plan.py]().

## MCP Integration and FastMCP Adapter

Marvin's MCP integration lives under `src/marvin/_internal/integrations/` and exposes an adapter that lets a Marvin `Agent` consume tools from a FastMCP server via the `mcp_servers=[...]` argument. Source: [src/marvin/_internal/integrations/README.md]().

### Design Decisions

| Decision | Rationale |
| --- | --- |
| Lazy import of FastMCP | Keeps FastMCP an optional dependency (`marvin[mcp]`); not pulled in for users who do not need it. |
| Duck typing over `isinstance` | Tolerates FastMCP variants by checking for `name`, `list_tools` / `_mcp_list_tools`; avoids tight coupling to a specific release. |
| `_FastMCPImportState` | Encapsulates import state to avoid repeated attempts and keep global namespace clean. |
| Multi-heuristic detection | Class-name contains "FastMCP" **and** presence of required methods — belt-and-suspenders for compatibility. |
| Diagnostics + error messages | Logs each step and produces clear errors when FastMCP is missing. |

Source: [src/marvin/_internal/integrations/README.md]().

### Lifecycle and Recent Fixes

- **v3.2.5**: MCP server subprocesses were being restarted on every `agent.run()`, which broke tool-heavy agents. The lifecycle fix keeps servers alive for the session. Source: [release v3.2.5](https://github.com/PrefectHQ/marvin/releases/tag/v3.2.5).
- **v3.2.7**: `MCPServerStdio` was launched with only user-supplied `env=...`, dropping `PATH`, `HOME`, etc., and causing `TimeoutError` during server init. The fix merges user env on top of `os.environ`. Source: [release v3.2.7](https://github.com/PrefectHQ/marvin/releases/tag/v3.2.7).

### Usage

```python
from fastmcp.server import FastMCP
import marvin

server = FastMCP("My Server")

@server.tool()
def hello_world() -> str:
    return "Hello, world!"

agent = marvin.Agent(mcp_servers=[server])
result = agent.run("Please say hello to the world")
```

Source: [src/marvin/_internal/integrations/README.md]().

## Configuration and CLI Inspection

Settings can be inspected at runtime through the CLI:

```bash
marvin config view          # Show all settings
marvin config get <key>     # Show a specific setting
```

Source: [src/marvin/cli/config.py](). Defaults such as the memory provider, default model, and thread database live on `marvin.settings`; `marvin.defaults.memory_provider` is consulted when a `Memory` is constructed without an explicit provider. Source: [src/marvin/memory/memory.py]().

## Common Failure Modes

| Symptom | Likely cause | Reference |
| --- | --- | --- |
| `TimeoutError` initializing an MCP stdio server | `env` on `MCPServerStdio` overrode `os.environ` instead of merging | v3.2.7 fix |
| Agent re-spawns MCP server every run | Pre-v3.2.5 lifecycle bug | v3.2.5 fix |
| `ValueError: Memory modules require a MemoryProvider...` | No default provider configured | [src/marvin/memory/memory.py]() |
| `ai_fn` returns a string instead of structured data | `from __future__ import annotations` stripped type info | issue [#950](https://github.com/PrefectHQ/marvin/issues/950) |
| Out-of-memory token explosion in `marvin.cast` on images | `BinaryImage` data bloat | issue [#1246](https://github.com/PrefectHQ/marvin/issues/1246) |
| Persisted memory causes downstream model to misbehave | External content written into memory without sanitization (ASI06) | issue [#1347](https://github.com/PrefectHQ/marvin/issues/1347) |

## See Also

- [Engine & Orchestrator](src/marvin/engine/CLAUDE.md) — task loop, end-turn tools, streaming.
- [Agents](src/marvin/agents/CLAUDE.md) — agent class, tools, memory wiring.
- [Memory Providers](src/marvin/memory/providers/) — Chroma, LanceDB, Postgres adapters.
- [Integrations](src/marvin/_internal/integrations/README.md) — FastMCP adapter.
- [Slackbot Example](examples/slackbot/README.md) — end-to-end usage with TurboPuffer memory.

---

<!-- evidence_pipeline_checked: true -->
<!-- evidence_injected: true -->

---

## Pitfall Log

Project: PrefectHQ/marvin

Summary: Found 12 structured pitfall item(s), including 2 high/blocking item(s). Top priority: Maintenance risk - Maintenance risk requires verification.

## 1. Maintenance risk - Maintenance risk requires verification

- Severity: high
- Evidence strength: source_linked
- Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/PrefectHQ/marvin/issues/950

## 2. Security or permission risk - Security or permission risk requires verification

- Severity: high
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/PrefectHQ/marvin/issues/1246

## 3. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/PrefectHQ/marvin/issues/1351

## 4. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/PrefectHQ/marvin/issues/1347

## 5. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/PrefectHQ/marvin/issues/1353

## 6. Capability evidence risk - Capability evidence risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: README/documentation is current enough for a first validation pass.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: capability.assumptions | https://github.com/PrefectHQ/marvin

## 7. Runtime risk - Runtime risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a runtime risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/PrefectHQ/marvin/issues/1355

## 8. Maintenance risk - Maintenance risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/PrefectHQ/marvin

## 9. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: downstream_validation.risk_items | https://github.com/PrefectHQ/marvin

## 10. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: risks.scoring_risks | https://github.com/PrefectHQ/marvin

## 11. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: issue_or_pr_quality=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/PrefectHQ/marvin

## 12. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: release_recency=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/PrefectHQ/marvin

<!-- canonical_name: PrefectHQ/marvin; human_manual_source: deepwiki_human_wiki -->
