Skip to content

feat(mcp): add list_recent_decisions tool (#1982)#111

Merged
saurabhjain1592 merged 1 commit intomainfrom
feat/list-recent-decisions
May 8, 2026
Merged

feat(mcp): add list_recent_decisions tool (#1982)#111
saurabhjain1592 merged 1 commit intomainfrom
feat/list-recent-decisions

Conversation

@saurabhjain1592
Copy link
Copy Markdown
Member

Summary

V1.1 (axonflow-enterprise#1982) — adds axonflow_list_recent_decisions to OpenClaw's agent-callable tool surface. Companion to the existing axonflow_explain_decision tool: explain answers "why was this blocked?" for a single decision; list_recent_decisions answers "what's been blocked recently?" with a 5-field summary per row.

What changed

File Why
src/upgrade-prompt.ts Added decision_list_size to V1_LIMIT_TYPES. Required for handleEnvelope to recognize the new V1.1 limit type — without this, the throttle gate would silently drop the upgrade wording.
src/axonflow-client.ts New listRecentDecisionsStrict method. Returns { kind: "ok", decisions } on 200 / { kind: "envelope", envelope } on 429 / throws on other non-2xx. The 429 path runs through the existing handleEnvelope helper so the throttle file is stamped + the upgrade wording is logged once-per-day.
src/agent-tools.ts New buildListRecentDecisionsTool. Maps the JSON-Schema args → client method. On envelope hit, returns a structured tool result with upgrade_required: true so the host LLM/UI can render the upgrade prompt.
src/index.ts Re-exports buildListRecentDecisionsTool.
Tests 4 new agent-tools.test.ts cases (forwarding, decision-enum, envelope passthrough, transport error). Updated registration count 10→11. Updated V1_LIMIT_TYPES lock test.
runtime-e2e/list-recent-decisions/ New wire-level runtime-e2e gate: tools/list advertisement check + happy-path + cap-hit envelope passthrough.
tests/e2e/runtime-tools-smoke.mjs Added 6th scenario for the new tool. Pre-existing bug fix: expectedNames was stale at 5 (V1 Pro added 5 more, never updated) — extended to the full 11-tool surface.
runtime-e2e/_lib/openclaw-runtime.sh Pre-existing bug fix: hardcoded Registered 5 agent-callable tools regex was stale since V1 Plugin Pro. Replaced with Registered [0-9]+ so the gate doesn't break on every tool addition.

Three-rule definition of done

Rule 0 — Runtime proof

Captured at runtime-e2e/list-recent-decisions/EVIDENCE.txt:

PASS: MCP session initialized (axonflow-e8d8a91a00596c14522b7912)
PASS: MCP server advertises list_recent_decisions
PASS: tools/call list_recent_decisions returned a recognized shape
PASS: cap-hit returned wrapped V1 envelope (upgrade_required + decision_list_size + buy_url)

PASS: list_recent_decisions runtime — MCP server advertises tool, happy path returns decisions, cap-hit preserves V1 upgrade envelope

Also verified via tests/e2e/runtime-tools-smoke.mjs against the live stack:

Registered 11 tools: axonflow_audit_search, axonflow_explain_decision, axonflow_list_recent_decisions, ...
--- 6/6 axonflow_list_recent_decisions ---
PASS: list_recent_decisions returned 0 decisions
PASS: runtime-tools-smoke — all 6 tools registered and dispatch correctly

Rule 1 — Self-review

Hunk-by-hunk per the 5-question protocol from feedback_self_review_is_mandatory_every_pr.md:

  • ✅ The tool's execute() does NOT collapse 429 into isError: true. The kind === "envelope" branch returns the upgrade message + structured details so the host renders it as success-with-upgrade-prompt, not as a tool failure. This is the exact gap from feedback_429_no_upgrade_hint_is_conversion_gap.md.
  • decision_list_size was added to V1_LIMIT_TYPES. Without this, handleEnvelope would short-circuit at line 309-314 (if (!V1_LIMIT_TYPES.includes(envelope.limit_type))) and silently drop the wording.
  • ✅ The new SQL-bound input fields are validated client-side (decision enum check before the HTTP call) so a malformed arg becomes a structured isError instead of a wasted round-trip.
  • listRecentDecisionsStrict distinguishes 429 (envelope) from other non-2xx (throws AxonFlowHttpError) — same defense-in-depth shape as the existing searchAuditEventsStrict / listOverridesStrict methods.

Rule 2 — No deferred bugs

Two stale checks fixed in this same PR rather than handed off to "another session":

  • runtime-tools-smoke.mjs expectedNames (was frozen at 5; bumped to 11).
  • openclaw-runtime.sh Registered 5 regex (replaced with Registered [0-9]+).

Both were latent since the V1 Plugin Pro tools landed; the broken state was simply masked because nobody ran the e2e since then.

Test plan

  • npx jest --no-coverage — 450/450 pass
  • npx tsc --noEmit — clean
  • bash runtime-e2e/list-recent-decisions/test.sh against docker-compose at localhost:8080 — PASS
  • node tests/e2e/runtime-tools-smoke.mjs against docker-compose at localhost:8080 — PASS

Do NOT merge

Per feedback_v1_1_no_merge_during_release_window.md: V1.1 release-window PRs stay open until user explicitly authorizes the V1.1 release. This PR consumes the platform endpoint at getaxonflow/axonflow-enterprise#1985 (also held open). Both land together when V1.1 ships.

@saurabhjain1592 saurabhjain1592 added the do-not-merge Hold until V1.1 release window opens label May 7, 2026
@saurabhjain1592
Copy link
Copy Markdown
Member Author

🚫 Release-window hold

Per feedback_v1_1_no_merge_during_release_window.md: V1.1 PRs stay open until the user explicitly authorizes the V1.1 release.

This PR depends on the platform-side endpoint + MCP tool registration in getaxonflow/axonflow-enterprise#1985, which is also held open. The two land together when V1.1 ships.

Runtime testing + verification on this branch only — do not merge to main.

@saurabhjain1592 saurabhjain1592 force-pushed the feat/list-recent-decisions branch from 46e2d55 to 3765f98 Compare May 7, 2026 16:43
saurabhjain1592 added a commit to getaxonflow/axonflow-claude-plugin that referenced this pull request May 7, 2026
Surfaces the V1.1 platform endpoint GET /api/v1/decisions in Claude Code
via the corresponding MCP tool exposed by the agent's MCP server. Adds
the slash command + skill + runtime-e2e gate so a Claude Code user can
ask "what just got blocked?" and get a structured tier-aware response.

Files:
- commands/axonflow-list-recent-decisions.md — new slash command.
- skills/list-recent-decisions/SKILL.md — new skill describing the
  tool's args, response shape, and the tier cap-hit envelope handling.
  Explicitly instructs the LLM to render the upgrade wording verbatim
  on cap-hit instead of silently retrying or summarizing.
- runtime-e2e/list-recent-decisions/{README.md,test.sh,EVIDENCE.txt} —
  wire-level proof: MCP server advertises the tool, happy-path
  tools/call returns the decisions shape, cap-hit returns the wrapped
  V1 upgrade envelope with upgrade.compare_url + upgrade.buy_url +
  limit_type=decision_list_size intact.
- tests/e2e/runtime-mcp-tools.sh — added 7th scenario for the
  over-cap envelope. Locks in
  feedback_429_no_upgrade_hint_is_conversion_gap.md at the wire level
  so a future regression that swallows the cap-hit fails CI.
- CHANGELOG.md — Unreleased "Added" section.

DO NOT MERGE — V1.1 release window is gated by user authorization
per feedback_v1_1_no_merge_during_release_window.md. The platform
side (axonflow-enterprise#1985) and the openclaw plugin side
(getaxonflow/axonflow-openclaw-plugin#111) are also held open;
all three land together when V1.1 ships.

Signed-off-by: Saurabh Jain <saurabhjain1592@gmail.com>
@saurabhjain1592 saurabhjain1592 force-pushed the feat/list-recent-decisions branch from 3765f98 to 600aacb Compare May 7, 2026 16:49
saurabhjain1592 added a commit to getaxonflow/axonflow-claude-plugin that referenced this pull request May 8, 2026
Surfaces the V1.1 platform endpoint GET /api/v1/decisions in Claude Code
via the corresponding MCP tool exposed by the agent's MCP server. Adds
the slash command + skill + runtime-e2e gate so a Claude Code user can
ask "what just got blocked?" and get a structured tier-aware response.

Files:
- commands/axonflow-list-recent-decisions.md — new slash command.
- skills/list-recent-decisions/SKILL.md — new skill describing the
  tool's args, response shape, and the tier cap-hit envelope handling.
  Explicitly instructs the LLM to render the upgrade wording verbatim
  on cap-hit instead of silently retrying or summarizing.
- runtime-e2e/list-recent-decisions/{README.md,test.sh,EVIDENCE.txt} —
  wire-level proof: MCP server advertises the tool, happy-path
  tools/call returns the decisions shape, cap-hit returns the wrapped
  V1 upgrade envelope with upgrade.compare_url + upgrade.buy_url +
  limit_type=decision_list_size intact.
- tests/e2e/runtime-mcp-tools.sh — added 7th scenario for the
  over-cap envelope. Locks in
  feedback_429_no_upgrade_hint_is_conversion_gap.md at the wire level
  so a future regression that swallows the cap-hit fails CI.
- CHANGELOG.md — Unreleased "Added" section.

DO NOT MERGE — V1.1 release window is gated by user authorization
per feedback_v1_1_no_merge_during_release_window.md. The platform
side (axonflow-enterprise#1985) and the openclaw plugin side
(getaxonflow/axonflow-openclaw-plugin#111) are also held open;
all three land together when V1.1 ships.

Signed-off-by: Saurabh Jain <saurabhjain1592@gmail.com>
Wraps the V1.1 platform endpoint GET /api/v1/decisions as an
OpenClaw-callable agent tool: surfaces the caller's recent governance
decisions for "what just got blocked" UX, appeal/override flows, and
forensic decision-history tracing.

Tool: axonflow_list_recent_decisions
- Inputs: since (RFC3339), decision (allow|deny|require_approval),
  policy_id, tool_signature, limit (1-1000).
- Output: { decisions: [...] } on happy path, OR
          { upgrade_required: true, envelope: {...} } when a Free user
          hits the tier page cap.

The 429 envelope passthrough is the critical Pro-conversion surface
per feedback_429_no_upgrade_hint_is_conversion_gap.md. The new
AxonFlowClient.listRecentDecisionsStrict method delegates to the
existing handleEnvelope helper so the throttle file is stamped + the
upgrade wording is logged once-per-day, AND the structured envelope
is returned to the agent-tool wrapper so the host LLM/UI can render
the buy_url + compare_url. decision_list_size added to V1_LIMIT_TYPES
so handleEnvelope recognizes the new limit type.

Pre-existing bugs caught + fixed in same PR (Rule 2):
- runtime-e2e/_lib/openclaw-runtime.sh was checking for "Registered 5
  agent-callable tools" — stale since V1 Plugin Pro added 5 more
  (real count: 11 with this PR). Replaced with a regex that doesn't
  go stale on tool additions.
- tests/e2e/runtime-tools-smoke.mjs's expectedNames was frozen at 5
  for the same reason — extended to the full 11-tool surface.

Tests:
- agent-tools.test.ts: 4 new cases (forwarding, decision-enum
  validation, V1 envelope passthrough, transport error).
- registration.test.ts: count assertion 10 → 11 + new tool name.
- upgrade-prompt.test.ts: V1_LIMIT_TYPES locked enumeration extended.
- runtime-e2e/list-recent-decisions/test.sh: wire-level proof that
  the MCP server advertises the tool, happy-path tools/call returns
  the decisions shape, AND cap-hit returns the wrapped V1 envelope
  with buy_url + compare_url + limit_type=decision_list_size intact.

All 450 unit tests pass; runtime-e2e PASSES live against
docker-compose at localhost:8080.

DO NOT MERGE — V1.1 release window is gated by user authorization
per feedback_v1_1_no_merge_during_release_window.md.

Signed-off-by: Saurabh Jain <saurabhjain1592@gmail.com>
@saurabhjain1592 saurabhjain1592 force-pushed the feat/list-recent-decisions branch from 600aacb to 513f8c5 Compare May 8, 2026 07:36
@saurabhjain1592 saurabhjain1592 merged commit d628cef into main May 8, 2026
18 checks passed
@saurabhjain1592 saurabhjain1592 deleted the feat/list-recent-decisions branch May 8, 2026 07:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge Hold until V1.1 release window opens

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant