Skip to content

Repository files navigation

AFP — Agent Feedback Protocol (reference implementation)

CI License

Español: README.es.md

Python reference implementation of the return path MCP lacks: a standard channel for an AI agent to report the friction it hit while using a tool, addressed to that tool's maintainer.

Normative spec: spec/SPEC.md (RFC 2119) · canonical JSON Schemas · conformance test vectors Design rationale (Spanish): docs/superpowers/specs/2026-05-30-afp-protocol-design.md

Installation

uv sync

CLI usage

# 1. Build a complete field report from a partial JSON
uv run afp report --from partial.json --out report.json

# 2. Validate (JSON Schema + secret hard-block)
uv run afp validate report.json

# Validate an afp.json manifest
uv run afp validate-manifest afp.json

# 3. Deposit it respecting the routing policy
#    Without an afp.json in --dir, only local/draft is allowed (never auto remote submission).
uv run afp submit report.json --dir . --sink draft

# Atomic alternative: build and deposit in a single call
uv run afp report --from partial.json --submit --dir . --sink draft

# Review local drafts before promoting them
uv run afp drafts list --dir .
uv run afp drafts show afp_<id> --dir .

# Explicit promotion after human review
uv run afp drafts promote afp_<id> --dir . --sink github_issues

# Discard a stale draft (friction verified fixed, or promoted issue closed);
# reason + timestamp are recorded in .afp/discarded.json
uv run afp drafts discard afp_<id> --dir . --reason "verified fixed: ..."

Review signal (AFP-REVIEW:)

Every time a submission is deposited as draft (submit, report --submit, dogfood without a manifest), AFP emits on stderr a line with the stable prefix AFP-REVIEW: stating how many drafts are pending and the command to review them:

AFP-REVIEW: 2 drafts pendientes de revisión humana → afp drafts list --dir .

This is a documented contract: an agentic harness can detect the AFP-REVIEW: prefix on stderr to show the human there are drafts to approve, and a human can read it directly. The OK: ... -> draft:/path stays on stdout.

Minimal partial.json:

{
  "subject_uri": "pkg:pypi/ruff",
  "goal": "lint the project",
  "expectation": "JSON output with the errors",
  "observed": "plain-text output with no structure",
  "friction_type": "wrong_output",
  "fault_domain": "tool",
  "severity": "degraded"
}

Harness integrations

integrations/ teaches agents to file AFP drafts when they hit tool friction, and to verify/discard stale drafts (always --sink draft; a human reviews and promotes).

Install AFP once:

uv tool install git+https://github.com/Vasallo94/afp-protocol

Then install the harness integration you use:

afp integrations install codex --global
afp integrations install claude-code --global
afp integrations install cursor --project .
afp doctor

Integrations install from packaged resources; cloning this repository is not required. Agents emit drafts only. Humans review with afp drafts list/show and promote with afp drafts promote.

Tool maintainers can initialize the trust manifest that enables reviewed remote promotion:

afp init \
  --dir . \
  --subject mcp://github.com/acme/weather-mcp \
  --sink github_issues \
  --repo acme/weather-mcp

Dogfooding AFP on AFP

To report friction found while using this very implementation:

uv run afp dogfood \
  --goal "validate a report generated by AFP" \
  --expectation "the CLI should explain why it fails" \
  --observed "the message did not make clear which field was invalid" \
  --friction-type confusing_interface \
  --fault-domain tool \
  --severity degraded \
  --sink draft

By default this creates a local draft under .afp/drafts/. The AFP repo declares its own afp.json, so manually reviewed reports can later be promoted to issues on Vasallo94/afp-protocol using the remote sink.

Security golden rule

Without an afp.json declared by the tool, AFP never auto-submits to a third-party repo: it only writes to local (spool) or draft (human review). Remote submission (e.g. github_issues) is always maintainer opt-in, and the report's subject_uri is additionally verified to fall under the one declared by the manifest (anti-spoofing):

  • PURL: same package base (@version and #fragment don't change the owner).
  • http(s)/mcp: same host/authority (api.acme.com.evil.comapi.acme.com) and the report's path equals the manifest's or is a per-segment sub-path (/v1 owns /v1/charges, not /v1abc).

Sensitive data handling (§5): high-confidence secrets (tokens, keys, JWT, Bearer) are a hard-block — if detected, the submission is aborted. Direct PII (email) does not abort: it is redacted to [REDACTED_EMAIL] and the report continues, because an email mentioned in free text is usually useful information for the maintainer.

Delivery semantics (at-least-once, idempotent on remotes)

Remote sinks (github_issues/gitlab_issues) are idempotent by report_id: a local ledger (.afp/submitted.json) maps report_id -> ref, so resubmitting the same report (e.g. a retry after a network timeout whose submission actually landed) returns the previous ref instead of opening a duplicate issue. It is deterministic and does not depend on the provider's search indexing lag.

Local sinks are not deduplicated: local is an append spool and draft is already overwritten by report_id. Semantic deduplication across distinct reports (grouping "14 agents blocked on the same thing") remains the Harvester's job (§7 of the spec).

GitLab sink (self-hosted / on-premise)

To route reports to an internal GitLab, the tool declares in its afp.json:

{
  "afp_version": "0.2",
  "subject_uri": "mcp://<gitlab-host>/<group>/<project>",
  "sink": {
    "type": "gitlab_issues",
    "host": "<gitlab-host>",
    "repo": "<group>/<project>",
    "label": "afp-report"
  },
  "redaction": "required",
  "accepts_remote": true
}

Requires glab installed and authenticated against the host (glab auth login --hostname <host>). Promotion to an issue is explicit and reviewed: afp drafts promote <id> --dir <repo> --sink gitlab_issues.

Tests

uv run pytest -v

License

Apache-2.0

About

Agent Feedback Protocol — the return path MCP lacks: a standard channel for an AI agent to report the friction it hit using a tool, back to that tool's maintainer. Spec + Python reference implementation.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages