Skip to content

Commit a99d663

Browse files
feat(mcp): add aiir_gitlab_summary tool for Duo Chat integration
New MCP tool: aiir_gitlab_summary - GitLab-flavored Markdown summary with AI authorship breakdown - Optional SAST report data (include_sast=true) - Optional MR comment posting (post_to_mr=true) - Supports commit range or full ledger summary - Designed for GitLab Duo Chat, MR comments, and CI pipelines Tests: 1111 passed (11 new tool tests + tool count 6→7)
1 parent e0aa50f commit a99d663

File tree

4 files changed

+365
-3
lines changed

4 files changed

+365
-3
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ Install signing support: `pip install aiir[sign]`
655655
<details>
656656
<summary><strong>MCP server details</strong></summary>
657657

658-
The AIIR MCP server exposes six tools:
658+
The AIIR MCP server exposes seven tools:
659659

660660
| Tool | Description |
661661
|------|-------------|
@@ -665,6 +665,7 @@ The AIIR MCP server exposes six tools:
665665
| `aiir_explain` | Human-readable explanation of verification results. |
666666
| `aiir_policy_check` | Check ledger against org AI-usage policy thresholds. |
667667
| `aiir_verify_release` | Release-scoped verification — evaluate receipts against policy, emit VSA. |
668+
| `aiir_gitlab_summary` | GitLab-flavored Markdown summary for Duo Chat, MR comments, and CI. |
668669

669670
**Install globally:**
670671

aiir/mcp_server.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@
6262
_load_index,
6363
_ledger_paths,
6464
)
65+
from aiir._gitlab import (
66+
format_gitlab_summary,
67+
format_gl_sast_report,
68+
post_mr_comment,
69+
)
6570

6671
# ---------------------------------------------------------------------------
6772
# Server metadata
@@ -319,6 +324,46 @@ def _sanitize_error(error: Exception) -> str:
319324
},
320325
},
321326
},
327+
{
328+
"name": "aiir_gitlab_summary",
329+
"description": (
330+
"Generate a GitLab-flavored Markdown summary of AIIR receipts. "
331+
"Returns a formatted table with AI authorship breakdown, per-commit "
332+
"details in a collapsible block, and optional SAST report data. "
333+
"Designed for GitLab Duo Chat, MR comments, and CI pipelines. "
334+
"Can optionally post the summary as a merge request comment when "
335+
"running inside GitLab CI. Reads from the .aiir/ ledger in cwd."
336+
),
337+
"inputSchema": {
338+
"type": "object",
339+
"properties": {
340+
"range": {
341+
"type": "string",
342+
"description": (
343+
"Commit range to summarize (e.g., 'origin/main..HEAD'). "
344+
"If omitted, all receipts in the ledger are summarized."
345+
),
346+
},
347+
"include_sast": {
348+
"type": "boolean",
349+
"description": (
350+
"Include SAST report data (Security Dashboard format) "
351+
"alongside the Markdown summary."
352+
),
353+
"default": False,
354+
},
355+
"post_to_mr": {
356+
"type": "boolean",
357+
"description": (
358+
"Post the summary as a comment on the current merge request. "
359+
"Only works when running inside GitLab CI with "
360+
"CI_MERGE_REQUEST_IID set. Ignored outside CI."
361+
),
362+
"default": False,
363+
},
364+
},
365+
},
366+
},
322367
]
323368

324369
# ---------------------------------------------------------------------------
@@ -500,6 +545,80 @@ def _handle_aiir_verify_release(args: Dict[str, Any]) -> Dict[str, Any]:
500545
return _text_result(report)
501546

502547

548+
def _handle_aiir_gitlab_summary(args: Dict[str, Any]) -> Dict[str, Any]:
549+
"""Generate a GitLab-flavored Markdown summary of AIIR receipts.
550+
551+
Designed for GitLab Duo Chat and MR comment integration.
552+
Reads receipts from the ledger and produces a native GitLab Markdown
553+
summary with optional SAST report data and MR comment posting.
554+
"""
555+
range_spec = args.get("range")
556+
include_sast = args.get("include_sast", False)
557+
post_to_mr = args.get("post_to_mr", False)
558+
559+
try:
560+
if range_spec:
561+
# Generate receipts for the specified range
562+
receipts = generate_receipts_for_range(range_spec, cwd=None)
563+
else:
564+
# Load from ledger
565+
ledger_path = Path.cwd() / ".aiir" / "receipts.jsonl"
566+
if not ledger_path.is_file():
567+
return _text_result(
568+
"No AIIR ledger found in this repository.\n"
569+
"Run 'aiir --pretty' to generate your first receipt, "
570+
"then use this tool to create a GitLab summary."
571+
)
572+
receipts = []
573+
with open(ledger_path, encoding="utf-8") as f:
574+
for line in f:
575+
line = line.strip()
576+
if line:
577+
try:
578+
receipts.append(json.loads(line))
579+
except json.JSONDecodeError:
580+
continue
581+
582+
if not receipts:
583+
return _text_result(
584+
"No receipts found. "
585+
"Run 'aiir --range <range>' to generate receipts first."
586+
)
587+
588+
# Format as GitLab-flavored Markdown
589+
summary = format_gitlab_summary(receipts)
590+
591+
# Optionally append SAST report data
592+
if include_sast:
593+
sast = format_gl_sast_report(receipts)
594+
summary += (
595+
"\n\n<details>\n<summary>🛡️ SAST Report Data</summary>\n\n"
596+
"```json\n"
597+
+ json.dumps(sast, indent=2)
598+
+ "\n```\n\n</details>"
599+
)
600+
601+
# Optionally post to MR (best-effort, non-fatal)
602+
mr_posted = False
603+
if post_to_mr:
604+
import os as _os
605+
606+
if _os.environ.get("CI_MERGE_REQUEST_IID"):
607+
try:
608+
post_mr_comment(summary)
609+
mr_posted = True
610+
except Exception:
611+
pass # Best-effort — don't fail the tool call
612+
613+
if mr_posted:
614+
summary += "\n\n✅ *Summary posted to merge request.*"
615+
616+
return _text_result(summary)
617+
618+
except Exception as e:
619+
return _error_result(_sanitize_error(e))
620+
621+
503622
# ---------------------------------------------------------------------------
504623
# MCP response helpers
505624
# ---------------------------------------------------------------------------
@@ -524,6 +643,7 @@ def _error_result(message: str) -> Dict[str, Any]:
524643
"aiir_explain": _handle_aiir_explain,
525644
"aiir_policy_check": _handle_aiir_policy_check,
526645
"aiir_verify_release": _handle_aiir_verify_release,
646+
"aiir_gitlab_summary": _handle_aiir_gitlab_summary,
527647
}
528648

529649

0 commit comments

Comments
 (0)