Metadata-Version: 2.4
Name: mcp-agent-handoff
Version: 0.11.1
Summary: Portable MCP server for agent handoff state and review tracking.
Project-URL: Homepage, https://github.com/darce/agentic-protocol-monorepo
Project-URL: Source, https://github.com/darce/agentic-protocol-monorepo/tree/main/packages/mcp-agent-handoff
Project-URL: Changelog, https://github.com/darce/agentic-protocol-monorepo/blob/main/packages/mcp-agent-handoff/CHANGELOG.md
Project-URL: Issues, https://github.com/darce/agentic-protocol-monorepo/issues
Requires-Python: >=3.12
Description-Content-Type: text/markdown
Requires-Dist: fastmcp
Requires-Dist: pydantic<3.0.0,>=2.6.0
Requires-Dist: tiktoken>=0.9.0
Requires-Dist: agentic-protocol<0.2.0,>=0.1.4
Provides-Extra: test
Requires-Dist: pytest>=8.0.0; extra == "test"
Requires-Dist: pytest-timeout>=2.3.0; extra == "test"
Requires-Dist: jsonschema>=4.0.0; extra == "test"
Provides-Extra: dev
Requires-Dist: jsonschema>=4.0.0; extra == "dev"
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: pytest-timeout>=2.3.0; extra == "dev"
Requires-Dist: ruff<1.0.0,>=0.11.0; extra == "dev"
Requires-Dist: mypy<2.0.0,>=1.13.0; extra == "dev"

# Agent Handoff MCP

Portable MCP server for agent handoff state, review findings, exports, close checks, and lane-scoped worker/orchestrator coordination.

## Scope

This package owns handoff state and related workflow primitives:

- task state, decisions, blockers, and next actions
- review findings and review runs
- generated `CURRENT_TASK.json` snapshots
- lane registration, lane activity, worker reports, and lane messages
- artifact indexing and derived metrics snapshots

It does not include unrelated repo-intel or UI helpers.

## Runtime State

By default, runtime state lives under the workspace you point the CLI at:

- SQLite DB: `.task-state/handoff.db`
- exports: `.task-state/exports/`
- generated markdown: `CURRENT_TASK.json`

Override these with CLI flags or `AGENT_HANDOFF_*` environment variables.

## Installation

### From PyPI (recommended)

```bash
pip install mcp-agent-handoff
# or, as an isolated tool:
uv tool install mcp-agent-handoff
# or, ad-hoc without installing (pin a release for reproducibility):
uvx mcp-agent-handoff@0.11.1 --workspace-root /path/to/workspace serve-stdio
```

### From the monorepo source tree (development)

From this package root inside `agentic-protocol-monorepo`:

```bash
cd packages/mcp-agent-handoff
python -m pip install -e ".[dev]"
```

## Development

All package-local development commands are intended to run from the package root:

```bash
make lint-handoff
make fix-lint-handoff
make format-handoff
make mypy-handoff
make test-handoff
make check-handoff
```

Prefer the package-local Make targets when running checks from editors or agent sessions. Workspace-level interpreter discovery can point at a minimal environment that lacks this package's dev extras such as `pytest`, while `make test-handoff` and `make check-handoff` keep execution anchored to the package root and honor `PYTHON=/path/to/python3` overrides when you need to pin a specific interpreter.

The Makefile automatically adds a sibling `../codex-subagent-bridge/src` to `PYTHONPATH` when that checkout exists. In a fully extracted repo, that fallback is unnecessary once dependencies are installed normally.

Direct commands also work:

```bash
PYTHONPATH=src python -m ruff check src tests
PYTHONPATH=src python -m mypy src
PYTHONPATH=src python -m pytest tests -q
```

## Python API

The package can be used directly as a library — this is the primary fallback when MCP tool calls are unavailable.

**Always import from the package root** (`agent_handoff_mcp`), never from submodules like `.config`, `.decisions`, or `.core`. Submodules are internal and may change.

```python
from pathlib import Path
from agent_handoff_mcp import (
    RuntimeConfig,
    configure_runtime,
    get_handoff_state,
    search_handoff,
  validate_decision_id,
    record_event,
    review_findings,
    list_review_findings,
    set_handoff_state,
    update_task_status,
    get_verified_tests,
    render_handoff,
    record_file_touch,
    get_touched_files,
)

# Configure runtime before any read/write call
configure_runtime(RuntimeConfig.for_repo(Path("/path/to/workspace")))

# Read state
state = get_handoff_state(sections="identity")

# Search decisions
results = search_handoff(queries=["slice_complete"], record_types=["decision"], limit=5)

# Preflight a slice-complete decision id before writing
preflight = validate_decision_id(
  decision="codex_slice_complete_plan0004_contract-pinning-and-docs",
  decision_kind="slice_complete",
)

# List open findings
findings = list_review_findings(status="open")
```

Without installation (monorepo source tree):

```bash
PYTHONPATH=packages/mcp-agent-handoff/src python3 -c "
from pathlib import Path
from agent_handoff_mcp import RuntimeConfig, configure_runtime, get_handoff_state
configure_runtime(RuntimeConfig.for_repo(Path('.')))
state = get_handoff_state(sections='identity')
print(state['data']['active']['task_ref'])
"
```

## CLI Usage

Run the MCP server over stdio:

```bash
mcp-agent-handoff --workspace-root /path/to/workspace serve-stdio
```

Run the MCP server over HTTP:

```bash
mcp-agent-handoff --workspace-root /path/to/workspace serve-http
```

FastMCP's current HTTP defaults are:

- host: `127.0.0.1`
- port: `8000`
- path: `/mcp`

Useful CLI checks:

```bash
mcp-agent-handoff --workspace-root /path/to/workspace doctor
mcp-agent-handoff --workspace-root /path/to/workspace state
mcp-agent-handoff --workspace-root /path/to/workspace validate --kind decision-id --decision codex_slice_complete_plan0004_contract-pinning-and-docs --decision-kind slice_complete
mcp-agent-handoff --workspace-root /path/to/workspace review-findings --operation list
mcp-agent-handoff --workspace-root /path/to/workspace handoff-close-check
```

## Slice-complete Decision IDs

Do not hard-code the slice-complete regex in client prompts or scripts. Treat `get_handoff_state(sections="identity").data.limits.write.slice_complete_decision_id` as the authoritative registry, and use `validate_decision_id(decision=..., decision_kind="slice_complete")` for side-effect-free preflight when composing ids outside `close_slice(author_tag=..., work_ref=..., slug=...)`.

Valid: `cdx_slice_complete_plan0004_contract_pinning_and_docs`

Invalid: `cdx_slice_complete_plan0004_contract-pinning-and-docs`

## Review Intake

Preferred path when `agent-orchestrator-mcp` is available:

1. `get_latest_slice_review_packet`
2. `get_review_findings_summary` or `review_findings --operation list` as needed

Handoff-only fallback:

1. `load_session`
2. `search_handoff(queries=["slice_complete"], record_types=["decision"], limit=1)`
3. `get_verified_tests(task_ref=..., commit_sha=...)`
4. `review_findings(review={"operation":"list","status":"open"})`

Use the MCP read surfaces above before inspecting `.task-state/handoff.db` directly. Drop to raw SQLite or filesystem inspection only when the required MCP tool is unavailable or when you are debugging MCP transport or serialization failures.

Source-tree execution without installation:

```bash
PYTHONPATH=src python -m agent_handoff_mcp --workspace-root /path/to/workspace serve-stdio
```

## Token-Efficient Usage

The v2 envelope is already compact, but callers still save the most tokens by shaping read responses deliberately:

- Use `get_handoff_state(sections="identity")` for routine task checks instead of a full state fetch.
- Use `detail="summary"` on read surfaces such as `get_handoff_state`, `load_session`, `review_findings`, `search_handoff`, and `artifacts` when truncated text is acceptable.
- Use `top_n_*`, `limit=`, and `fields=` to cap read size instead of trimming large payloads client-side. `load_session` accepts `top_n_touched_files` (default 20, max 200) to bound the additive `touched_files` list.
- Read from the canonical `data` block — `result["data"]["active"]` etc. The legacy top-level mirror was removed in 0.3.0 and never returns.

Package-local guidance and examples live in [docs/guides/token-efficient-usage.md](docs/guides/token-efficient-usage.md).

### Wire format note (≥0.3.0)

Starting in `agent-handoff-mcp 0.3.0`, MCP tool responses are **native JSON
objects** on the wire, not JSON strings inside `structured_content.result`.
Every handler is annotated `-> dict` and returns a real dict via
`_envelope()`; FastMCP serialises it once. If you previously did
`json.loads(handoff_tool(...))` to parse a JSON string return value, drop
the `json.loads` — the call returns a dict directly. If you previously
read `result.content[0].text` from the MCP wire payload and parsed it,
read `result.structured_content` directly instead.

The envelope **field set is unchanged** (`ok`, `schema_version`, `tool`,
`scope`, `data`, `mutation`, `artifacts`, `warnings`, `task_ref`) and
`schema_version` stays at `2`. Only the wire-encoding moved from
JSON-string-inside-JSON to a native nested object. See
[CHANGELOG.md](CHANGELOG.md) for the full migration notice.

## Client Adapter Shape

Installed console-script adapter (resolves whatever version is currently installed):

```json
{
  "name": "altcontext-mcp",
  "command": "mcp-agent-handoff",
  "args": ["--workspace-root", "/path/to/workspace", "serve-stdio"]
}
```

Pinned via uvx (recommended for consumers — reproducible across machines without a global install):

```json
{
  "name": "altcontext-mcp",
  "command": "uvx",
  "args": [
    "mcp-agent-handoff@0.11.1",
    "--workspace-root",
    "/path/to/workspace",
    "serve-stdio"
  ]
}
```

Source checkout adapter:

```json
{
  "name": "altcontext-mcp",
  "command": "python3",
  "args": [
    "-m",
    "agent_handoff_mcp",
    "--workspace-root",
    "/path/to/workspace",
    "serve-stdio"
  ],
  "cwd": "/path/to/mcp-agent-handoff",
  "env": {
    "PYTHONPATH": "/path/to/mcp-agent-handoff/src"
  }
}
```

## Version pinning

Consumer configs should pin the server version they were validated against so that
client-adapter contract drift is visible at config-review time rather than at
runtime. The installed console-script adapter resolves whatever release is
currently installed; the `uvx` and source-checkout variants above pin a known
release.

To verify the version of the server actually being launched:

```bash
mcp-agent-handoff --version
# mcp-agent-handoff 0.11.0
```

The same value is exposed as a top-level field in `run_doctor` output so
adapters can introspect it via the MCP surface:

```json
{
  "ok": true,
  "version": "0.11.0",
  "workspace_root": "/path/to/workspace",
  "...": "..."
}
```

## Tool Surface

`mcp-agent-handoff` now exposes a single unified MCP surface.

Legacy `--tool-profile all|core|extended` inputs are still accepted for compatibility, but they all expose the same tool set:

```bash
mcp-agent-handoff --workspace-root /path/to/workspace --tool-profile core serve-stdio
```

### Unified surface

| CLI name | MCP tool |
| --- | --- |
| `state` | `get_handoff_state` |
| `set` | `set_handoff_state` (pass `--status-only` for status-only updates that replace the legacy `update_task_status`) |
| `validate` | `validate` (`--kind decision_id\|write`; replaces the legacy `validate-decision-id` and `validate-write` subcommands) |
| `event` | `record_event` |
| `next-actions` | `next_actions` |
| `review-findings` | `review_findings` |
| `review-runs` | `review_runs` |
| `integrity-check` | `integrity_check` (`--kind working_tree\|post_merge\|close`; replaces the legacy `working-tree-integrity-check`, `post-merge-integrity-check`, and `handoff-close-check` subcommands) |
| `render-handoff` | `render_handoff` (kind=current_task/dashboard) |
| *(no CLI)* | `load_session` |
| *(no CLI)* | `close_slice` |
| `audit-decisions` | `audit_decision_ids` |
| `export` | `export_handoff_state` |
| `import` | `import_handoff_state` |
| `archive` | `archive` (`--operation archive\|gc\|get`; replaces the legacy `archive-task-state`, `tasks-gc`, and `get-archived-task` subcommands) |
| `artifacts` | `artifacts` (typed `--operation` selects `record`, `search`, `get`, or `purge` for indexed artifact rows) |
| `touched-files` | `touched_files` (`--operation record\|list`; replaces the legacy `record-file-touch` and `get-touched-files` subcommands) |
| `compaction` | `compaction` (`--operation record\|get\|get_latest`; replaces the legacy `compact-session`, `get-compaction`, and `get-latest-compaction` subcommands) |
| `handoff-search` | `search_handoff` |
| `handoff-rows` | `list_handoff_rows` |
| `get-verified-tests` | `get_verified_tests` |

`record_event` uses a typed `event` payload with `event_kind="decision" | "test_result" | "blocker"` so each variant keeps its own required fields.
`next_actions` uses `operation="list" | "add" | "update" | "complete" | "skip"`.
`review_findings` uses `operation="record" | "batch_record" | "update" | "list"`.
`review_runs` uses `operation="record" | "list" | "coverage"`.

Branch/worktree drift is surfaced through `warnings` on write responses. If you
set `AGENT_HANDOFF_ENFORCE_BRANCH=1`, branch mismatches against the active
task's `target_branch` fail before mutation on enforceable branches. Direct
Python callers see `BranchMismatchError`; MCP clients receive the standard v2
error envelope with `data.expected_branch` and `data.actual_branch`.

CLI-only extras (not part of MCP registry): `artifact-list`, `artifact-terms`.

Surface classes:

- `action`: mutates canonical state; do not blind-retry
- `query`: read-only inspection of canonical state; usually safe to retry
- `generator`: derived output such as close checks, markdown renders, and metrics summaries

## Troubleshooting

The fastest first check is still:

```bash
mcp-agent-handoff --workspace-root /path/to/workspace doctor
```

If startup succeeds but calls fail, check the workspace paths first: `--workspace-root`, `--state-dir`, `--current-task-path`, and `--exports-dir` must all point at the same workspace state.
