Skip to content

v0.62.0: normalize adjacent MCP records into the SEP-2828 evidence model#221

Merged
vaaraio merged 2 commits into
mainfrom
feat/normalize-foreign-records
Jun 8, 2026
Merged

v0.62.0: normalize adjacent MCP records into the SEP-2828 evidence model#221
vaaraio merged 2 commits into
mainfrom
feat/normalize-foreign-records

Conversation

@vaaraio

@vaaraio vaaraio commented Jun 8, 2026

Copy link
Copy Markdown
Owner

vaara normalize reads an adjacent MCP record and maps it onto Vaara's SEP-2828 execution-record model. It speaks three dialects:

  • a SEP-2643 authorization denial becomes the refused outcome
  • a SEP-2787 tool-call attestation becomes the decision-attested binding (the exact back-link a conformant receipt must pin)
  • a SEP-2817 invocation audit context becomes the decision-input plane (advisory, never authorization evidence)

For each record it reports which evidence plane it fills, which SEP-2828 fields it populates, and what is still missing for a complete signed record. It promotes nothing: an unsigned client claim stays advisory, a denial is an outcome only, and an attestation fixes the back-link and nothing else, because the record's own alg, signature, and receiptAsserted are the recording side's separate signing event.

Why: the surrounding ecosystem is fragmenting into narrow, single-purpose records. Vaara is the receiving side that reads each one and files it into one evidence model, so the SEP-2828 record is where they compose.

SEP-2643 and SEP-2817 normalization is pure standard library and runs in the base install. The SEP-2787 back-link digest reuses the receipt verifier and degrades to a reported gap without the attestation extra.

Testing: new normalize_v0 vectors built from verbatim spec examples, a Vaara-free independent checker that reproduces every case including the back-link digest recomputed from raw bytes, and a 26-case suite. Full suite green at 1442 passed, ruff and mypy clean.

Summary by CodeRabbit

  • New Features

    • Added vaara normalize CLI command for converting records between formats with automatic format detection
    • Supports both human-readable and JSON output modes
    • Reports populated and missing field information in normalization results
  • Documentation

    • Added normalize vector documentation with test fixtures
  • Tests

    • Added comprehensive test suite covering multiple input format conversions

vaara normalize reads a SEP-2643 authorization denial, a SEP-2787
tool-call attestation, or a SEP-2817 invocation audit context and maps
it onto the SEP-2828 execution-record model: which evidence plane it
fills (outcome / decision-attested / decision-input), which fields it
populates, and what is still missing for a complete signed record.

It promotes nothing: a SEP-2817 client claim stays advisory, a SEP-2643
denial is a refused outcome, and a SEP-2787 attestation fixes the exact
back-link a conformant receipt must pin and nothing else. SEP-2643 and
SEP-2817 are pure standard library (base install); the SEP-2787
back-link digest reuses the receipt verifier and degrades to a reported
gap without the attestation extra.

Adds _normalize.py plus a public re-export, the normalize_v0 vectors
(verbatim spec examples) with a Vaara-free independent checker that
reproduces every case, and the test suite.
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@vaaraio, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 44 minutes and 17 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8c64ed72-d342-487e-a01b-32070b40a8d3

📥 Commits

Reviewing files that changed from the base of the PR and between 5d1f635 and 326ca1f.

📒 Files selected for processing (14)
  • CHANGELOG.md
  • clients/ts/package.json
  • pyproject.toml
  • server-vaara-server.json
  • server.json
  • src/vaara/__init__.py
  • src/vaara/attestation/_normalize.py
  • src/vaara/attestation/_receipt_verifier.py
  • src/vaara/cli.py
  • tests/test_normalize.py
  • tests/vectors/normalize_v0/README.md
  • tests/vectors/normalize_v0/_check_independent.py
  • tests/vectors/normalize_v0/expected.json
  • tests/vectors/normalize_v0/inputs/sep2787_attestation_with_extension.json
📝 Walkthrough

Walkthrough

This PR adds a normalization layer that maps MCP records from adjacent SEP formats (denial, attestation, invocation audit) onto the unified SEP-2828 evidence model. It includes format detection, partial field population with advisory context, back-link digest computation for attestations, a CLI command, and comprehensive vector-based test coverage.

Changes

SEP-2828 Normalization Feature

Layer / File(s) Summary
Core normalization module and data model
src/vaara/attestation/_normalize.py
NormalizedEvidence dataclass captures evidence plane, populated/missing SEP-2828 fields, and advisory context. detect_format() classifies input records as SEP-2643/2787/2817/unknown using structural heuristics. normalize() routes each format to a specialized mapper: _normalize_denial extracts refused outcome status, _normalize_invocation populates decision-input advisory fields, and _normalize_attestation computes SEP-2828 back-links via optional attestation helpers.
Public API wiring and CLI command
src/vaara/attestation/receipt.py, src/vaara/cli.py
Receipt module re-exports normalization utilities. New vaara normalize RECORD.json --format {auto,sep2643,sep2787,sep2817} [--json] command loads JSON records and emits JSON output or human-readable normalization report showing populated fields, advisory context, and missing proof-related fields; exit code 0 only when record is recognized.
Test vectors, expected outputs, and independent validator
tests/vectors/normalize_v0/
Seven input fixtures (three sep2643 denial variants, one sep2787 attestation, two sep2817 invocations, one unknown) with expected normalization outputs in expected.json. Independent Python checker (_check_independent.py) replicates the normalization logic using only stdlib + optional rfc8785, skipping SEP-2787 cases when rfc8785 is unavailable; serves as reference validation against the main implementation.
Test suite: vectors, format detection, semantics, and CLI
tests/test_normalize.py
Parameterized tests validate exact output equality for all seven vectors (conditional skip for SEP-2787 without rfc8785), format detection across known/malformed inputs, semantic mapping for denial refused outcomes and invocation advisory fields, and attestation back-link digest correctness against paired receipt. CLI integration tests assert exit codes and output for denial/invocation/attestation/error paths.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • vaaraio/vaara#139: Normalization layer imports SEP-2787 attestation helpers and reference implementation to compute back-link digests.
  • vaaraio/vaara#151: Normalization back-link computation depends on SEP-2787 v2 envelope parsing and args projection/digest model changes.
  • vaaraio/vaara#167: Back-link implementation overlaps with receipt backLink helpers and attestation digest construction introduced in execution receipts.

Poem

A rabbit hops through records diverse,
SEP-2643, -2787, -2817 in its purse,
Normalizing to evidence so clear,
Via back-links and advisory cheer,
The future SEP-2828 draws near! 🐰

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.73% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and specifically describes the main addition: normalizing adjacent MCP records into the SEP-2828 evidence model, which is the primary focus of all changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/normalize-foreign-records

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment on lines +252 to +253
"SEP-2817 is client-asserted input audit context; per its own "
"specification it MUST NOT be used as authorization evidence",
Comment on lines +254 to +256
"it maps to the decision-input plane (the agent's stated intent), "
"the unsigned counterpart of an attested rationale, and populates no "
"required SEP-2828 field",
Comment on lines +300 to +301
"a SEP-2787 attestation is the attested request a SEP-2828 receipt "
"answers; it fixes the exact back-link a conformant receipt must pin",
Comment on lines +302 to +303
"plannerDeclared.intent is client-declared and bound by the issuer's "
"signature, not asserted true by the issuer",
Comment on lines +304 to +305
"the record's own signing (alg, signature, receiptAsserted) is a "
"separate event by the recording side, not derived from the attestation",
Comment on lines +150 to +151
"SEP-2817 is client-asserted input audit context; per its own "
"specification it MUST NOT be used as authorization evidence",
Comment on lines +152 to +154
"it maps to the decision-input plane (the agent's stated intent), "
"the unsigned counterpart of an attested rationale, and populates no "
"required SEP-2828 field",
Comment on lines +190 to +191
["a SEP-2787 attestation is the attested request a SEP-2828 receipt "
"answers; it fixes the exact back-link a conformant receipt must pin",
Comment on lines +192 to +193
"plannerDeclared.intent is client-declared and bound by the issuer's "
"signature, not asserted true by the issuer",
Comment on lines +194 to +195
"the record's own signing (alg, signature, receiptAsserted) is a "
"separate event by the recording side, not derived from the attestation"],

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/vectors/normalize_v0/README.md (1)

49-51: 💤 Low value

Add language identifier to fenced code block.

The code block should specify bash or shell as the language identifier for proper syntax highlighting and to satisfy markdown linting rules.

📝 Proposed fix
-```
+```bash
 python tests/vectors/normalize_v0/_check_independent.py
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @tests/vectors/normalize_v0/README.md around lines 49 - 51, Update the fenced
code block in README.md to include a shell language identifier for proper syntax
highlighting and linting; replace the opening fence "" before the command referencing tests/vectors/normalize_v0/_check_independent.py with "bash" (or
"shell") so the block begins with bash and the rest of the content remains
unchanged.


</details>

<!-- cr-comment:v1:c6e7e506e35a05abe4861e6e -->

_Source: Linters/SAST tools_

</blockquote></details>

</blockquote></details>

<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @tests/vectors/normalize_v0/inputs/sep2643_url_denial.json:

  • Line 5: The test vector has an inconsistent error.code type: the JSON field
    "error.code" is numeric (-32042) but other SEP-2643 vectors use the string
    "AUTHORIZATION_DENIAL"; update the vector so "error.code" is the canonical
    string "AUTHORIZATION_DENIAL" to match the suite (or alternatively update the
    normalization/validation logic to accept both numeric and string), and ensure
    the change is applied to the sep2643_url_denial.json test to keep all SEP-2643
    inputs consistent with the other vectors.

Nitpick comments:
In @tests/vectors/normalize_v0/README.md:

  • Around line 49-51: Update the fenced code block in README.md to include a
    shell language identifier for proper syntax highlighting and linting; replace
    the opening fence "" before the command referencing tests/vectors/normalize_v0/_check_independent.py with "bash" (or "shell") so the block begins with bash and the rest of the content remains unchanged.

</details>

<details>
<summary>🪄 Autofix (Beta)</summary>

Fix all unresolved CodeRabbit comments on this PR:

- [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended)
- [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes

</details>

---

<details>
<summary>ℹ️ Review info</summary>

<details>
<summary>⚙️ Run configuration</summary>

**Configuration used**: defaults

**Review profile**: CHILL

**Plan**: Pro Plus

**Run ID**: `10ea30be-fe2e-4bce-92da-59ade1248f23`

</details>

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 259946cccdbd3830964d6de7f8f32348a4df37b3 and 5d1f63540fed2e448c275593f0779108c08450e6.

</details>

<details>
<summary>📒 Files selected for processing (14)</summary>

* `src/vaara/attestation/_normalize.py`
* `src/vaara/attestation/receipt.py`
* `src/vaara/cli.py`
* `tests/test_normalize.py`
* `tests/vectors/normalize_v0/README.md`
* `tests/vectors/normalize_v0/_check_independent.py`
* `tests/vectors/normalize_v0/expected.json`
* `tests/vectors/normalize_v0/inputs/sep2643_rar_denial.json`
* `tests/vectors/normalize_v0/inputs/sep2643_scope_denial.json`
* `tests/vectors/normalize_v0/inputs/sep2643_url_denial.json`
* `tests/vectors/normalize_v0/inputs/sep2787_attestation.json`
* `tests/vectors/normalize_v0/inputs/sep2817_multiturn.json`
* `tests/vectors/normalize_v0/inputs/sep2817_single.json`
* `tests/vectors/normalize_v0/inputs/unknown.json`

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

"jsonrpc": "2.0",
"id": 17,
"error": {
"code": -32042,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify error code format consistency across all SEP-2643 test vectors
echo "=== SEP-2643 test vector error codes ==="
fd -e json 'sep2643' tests/vectors/normalize_v0/inputs/ --exec jq -r '.error.code' {}

Repository: vaaraio/vaara

Length of output: 147


Inconsistent SEP-2643 error.code type across test vectors (numeric vs string).

tests/vectors/normalize_v0/inputs/sep2643_url_denial.json uses numeric error.code: -32042, while other SEP-2643 vectors in tests/vectors/normalize_v0/inputs/ use string error.code: "AUTHORIZATION_DENIAL". The suite/spec should treat one format as canonical (per SEP-2643) or explicitly validate both representations to keep vectors consistent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/vectors/normalize_v0/inputs/sep2643_url_denial.json` at line 5, The
test vector has an inconsistent error.code type: the JSON field "error.code" is
numeric (-32042) but other SEP-2643 vectors use the string
"AUTHORIZATION_DENIAL"; update the vector so "error.code" is the canonical
string "AUTHORIZATION_DENIAL" to match the suite (or alternatively update the
normalization/validation logic to accept both numeric and string), and ensure
the change is applied to the sep2643_url_denial.json test to keep all SEP-2643
inputs consistent with the other vectors.

…rt; bump to 0.62.0

Hardening from an adversarial review, before release:
- the independent checker now reconstructs the SEP-2787-modeled envelope and
  digests that (matching the receipt verifier) instead of digesting the raw
  document bytes, and a new extension-field attestation vector pins the
  modeled-fields-only semantics (extra fields do not change the back-link)
- a SEP-2817 userIntent flagged redacted no longer emits the cleartext
- the human report escapes control characters in foreign-derived values so a
  crafted value cannot forge report lines
- denial detection requires a denial marker before reading a bare object, so a
  stray top-level reason is not misread as a denial
- docstrings and the README state the digest is over the modeled fields, not
  raw wire bytes (corrected in _receipt_verifier too)

Bumps the version to 0.62.0 with the CHANGELOG entry.
@vaaraio

vaaraio commented Jun 8, 2026

Copy link
Copy Markdown
Owner Author

Implements part of the SEP-2828 execution-record proposal: modelcontextprotocol/modelcontextprotocol#2828

The adjacent records vaara normalize reads: modelcontextprotocol/modelcontextprotocol#2643, modelcontextprotocol/modelcontextprotocol#2787, modelcontextprotocol/modelcontextprotocol#2817.

@vaaraio
vaaraio merged commit c94b6da into main Jun 8, 2026
13 checks passed
@vaaraio
vaaraio deleted the feat/normalize-foreign-records branch June 8, 2026 19:32
vaaraio added a commit that referenced this pull request Jun 17, 2026
…del (#221)

vaara normalize reads an adjacent MCP record and maps it onto the SEP-2828 execution-record evidence model: a SEP-2643 denial becomes a refused outcome, a SEP-2787 attestation becomes the decision-attested back-link a conformant receipt must pin, and a SEP-2817 invocation context becomes advisory decision-input. For each record it reports which evidence plane it fills, which fields it populates, and what is still missing for a complete signed record. It promotes nothing: an unsigned client claim stays advisory.

Ships the normalize_v0 vectors (verbatim spec examples plus an extension-field attestation) and a Vaara-free independent checker that reconstructs the modeled envelope and reproduces every case. Bumps to 0.62.0.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants