Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions actions/ctef-conformance-check/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# CTEF Conformance Check (GitHub Action)

A composite GitHub Action that verifies an MCP server against **CTEF v0.3.2 §4.5
behavioral-evidence conformance** criteria, using runtime telemetry from
[Dominion Observatory](https://dominion-observatory.sgdata.workers.dev).

For each pull request it posts a checklist of the 6 CTEF criteria with
per-criterion remediation steps, a generated conformance-document URL, and a
ready-to-paste README badge.

This is the first GitHub Action that checks CTEF v0.3.2 conformance and the
first that integrates with a behavioral-evidence registry (rather than parsing
static metadata).

## Quick start

```yaml
# .github/workflows/ctef-conformance.yml
name: CTEF Conformance
on:
pull_request:
permissions:
contents: read
pull-requests: write
jobs:
ctef:
runs-on: ubuntu-latest
steps:
- uses: vdineshk/daee-engine/actions/ctef-conformance-check@main
with:
server_id: my-mcp-server
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

Replace `my-mcp-server` with the slug Observatory tracks for your server. If you
do not know your slug, query
`https://dominion-observatory.sgdata.workers.dev/api/leaderboard` or check the
URL slug at `https://dominion-observatory.sgdata.workers.dev/server/<slug>`.

## What it checks

The action calls
`GET /api/ctef/readiness/{server_id}` and reports on the six CTEF v0.3.2
criteria:

| Criterion | CTEF section | Description |
|---|---|---|
| `behavioral_evidence` | §4.5 | ≥10 interactions captured |
| `negative_path_envelope` | §2.1.1 | CTEF-compliant `SUBJECT_NOT_TRACKED` shape |
| `sla_tier_classified` | §3.4 | Server placed into Platinum/Gold/Silver/Bronze tier |
| `behavioral_drift_flag` | §4.5.6 | Drift signal evaluated from ≥2 daily snapshots |
| `trust_grade_assigned` | — | Behavioral trust grade A–F derived from runtime score |
| `conformance_uri` | §4.5 | `/.well-known/ctef-conformance` deployed on the server |

Each failing criterion comes back with a `fix` field describing exactly what to
ship next. The action surfaces those fixes verbatim in the PR comment and in
the GitHub job summary.

## Inputs

| Input | Required | Default | Description |
|---|---|---|---|
| `server_id` | yes | — | Observatory slug for your MCP server |
| `observatory_url` | no | `https://dominion-observatory.sgdata.workers.dev` | Override only for testing |
| `fail_on_non_compliant` | no | `false` | Set `true` to gate merging on `ready_for_ctef==true` |
| `comment_on_pr` | no | `true` | Post a PR comment with conformance summary |
| `ctef_version` | no | `0.3.2` | Action fails if Observatory reports a different spec version |

## Outputs

| Output | Description |
|---|---|
| `readiness_grade` | `PASS`, `PARTIAL`, or `FAIL` |
| `readiness_score` | Number of criteria passed (0–6) |
| `readiness_max` | Maximum possible criteria count |
| `ready_for_ctef` | Boolean — passes all criteria |
| `trust_grade` | Behavioral trust grade A–F |
| `trust_score` | Numeric score 0–100 |
| `evidence_uri` | CTEF §4.5 `behavioral_evidence` URI for this server |
| `attestation_uri` | URL of generated conformance document (`/api/ctef/attest`) |
| `badge_markdown` | Ready-to-paste README badge |

## Gating merges on CTEF conformance

```yaml
- uses: vdineshk/daee-engine/actions/ctef-conformance-check@main
with:
server_id: my-mcp-server
fail_on_non_compliant: 'true'
```

The action exits non-zero if `ready_for_ctef` is not `true`, so a required
status check on this workflow gates merges until all six criteria pass.

## Reading outputs in later steps

```yaml
- id: ctef
uses: vdineshk/daee-engine/actions/ctef-conformance-check@main
with:
server_id: my-mcp-server

- name: Update README badge
if: steps.ctef.outputs.readiness_grade == 'PASS'
run: |
echo "Trust grade ${{ steps.ctef.outputs.trust_grade }}, score ${{ steps.ctef.outputs.trust_score }}"
echo "Evidence: ${{ steps.ctef.outputs.evidence_uri }}"
```

## Why this exists

CTEF v0.3.2 publishes 2026-05-19. The spec calls for runtime
behavioral-evidence registries to host conformance verdicts at canonical URIs
(see §4.5 + §4.5.6). Dominion Observatory is the reference behavioral-evidence
provider cited in the spec; this action is the first agent-economy primitive
that lets MCP server maintainers fail their CI when they regress against any
of the six CTEF criteria, with the exact remediation step delivered inline.

## License

Apache 2.0. See repository root for full text.
195 changes: 195 additions & 0 deletions actions/ctef-conformance-check/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
name: 'CTEF Conformance Check'
description: 'Verify your MCP server against CTEF v0.3.2 §4.5 behavioral-evidence conformance criteria via Dominion Observatory. Posts remediation guidance per failed criterion.'
author: 'Dominion Observatory'
branding:
icon: 'shield'
color: 'blue'

inputs:
server_id:
description: 'MCP server slug as tracked by Observatory (e.g. sg-cpf-calculator-mcp). Typically your worker subdomain or repository name.'
required: true
observatory_url:
description: 'Dominion Observatory base URL. Override only for testing.'
required: false
default: 'https://dominion-observatory.sgdata.workers.dev'
fail_on_non_compliant:
description: 'Exit non-zero when assessment is NON_COMPLIANT or PARTIAL. Set true to gate merging on CTEF conformance.'
required: false
default: 'false'
comment_on_pr:
description: 'Post a PR comment with conformance summary + remediation steps. Requires GITHUB_TOKEN.'
required: false
default: 'true'
ctef_version:
description: 'CTEF spec version to assert. Action will fail if Observatory reports a different version.'
required: false
default: '0.3.2'

outputs:
readiness_grade:
description: 'PASS, PARTIAL, or FAIL'
value: ${{ steps.check.outputs.readiness_grade }}
readiness_score:
description: 'Number of CTEF criteria passed (0-6)'
value: ${{ steps.check.outputs.readiness_score }}
readiness_max:
description: 'Maximum possible criteria count'
value: ${{ steps.check.outputs.readiness_max }}
ready_for_ctef:
description: 'true/false — whether server passes all CTEF v0.3.2 §4.5 criteria'
value: ${{ steps.check.outputs.ready_for_ctef }}
trust_grade:
description: 'Behavioral trust grade A through F'
value: ${{ steps.check.outputs.trust_grade }}
trust_score:
description: 'Numeric trust score 0-100'
value: ${{ steps.check.outputs.trust_score }}
evidence_uri:
description: 'CTEF §4.5 behavioral_evidence URI for this server'
value: ${{ steps.check.outputs.evidence_uri }}
attestation_uri:
description: 'Generated CTEF conformance document URI (POST/GET /api/ctef/attest)'
value: ${{ steps.check.outputs.attestation_uri }}
badge_markdown:
description: 'Ready-to-paste README badge markdown'
value: ${{ steps.check.outputs.badge_markdown }}

runs:
using: 'composite'
steps:
- name: Fetch CTEF readiness
id: check
shell: bash
env:
SERVER_ID: ${{ inputs.server_id }}
OBSERVATORY_URL: ${{ inputs.observatory_url }}
EXPECTED_CTEF_VERSION: ${{ inputs.ctef_version }}
FAIL_ON_NON_COMPLIANT: ${{ inputs.fail_on_non_compliant }}
run: |
set -euo pipefail
URL="${OBSERVATORY_URL%/}/api/ctef/readiness/${SERVER_ID}"
echo "Querying CTEF readiness: ${URL}"
HTTP_BODY=$(mktemp)
HTTP_CODE=$(curl -sS -L --retry 2 --retry-delay 3 -m 20 -o "${HTTP_BODY}" -w "%{http_code}" "${URL}")
if [ "${HTTP_CODE}" != "200" ]; then
echo "::error title=Observatory unreachable::HTTP ${HTTP_CODE} from ${URL}"
cat "${HTTP_BODY}" | head -c 500
exit 1
fi

VERSION=$(jq -r '.ctef_version // empty' "${HTTP_BODY}")
if [ -z "${VERSION}" ]; then
echo "::error::Response missing ctef_version field; check Observatory response shape"
cat "${HTTP_BODY}"
exit 1
fi
if [ "${VERSION}" != "${EXPECTED_CTEF_VERSION}" ]; then
echo "::error title=CTEF version mismatch::Observatory reports ctef_version=${VERSION}, expected ${EXPECTED_CTEF_VERSION}. Update the ctef_version input or pin the action to a version that supports this spec."
exit 1
fi

GRADE=$(jq -r '.readiness_grade // "UNKNOWN"' "${HTTP_BODY}")
SCORE=$(jq -r '.readiness_score // 0' "${HTTP_BODY}")
MAX=$(jq -r '.readiness_max // 0' "${HTTP_BODY}")
READY=$(jq -r '.ready_for_ctef // false' "${HTTP_BODY}")
TRUST_GRADE=$(jq -r '.criteria.trust_grade_assigned.value // "?"' "${HTTP_BODY}")
TRUST_SCORE=$(jq -r '.criteria.trust_grade_assigned.score // 0' "${HTTP_BODY}")
EVIDENCE_URI=$(jq -r '.criteria.behavioral_evidence.evidence // ""' "${HTTP_BODY}")
ATTEST_URI=$(jq -r '.resources.generate_conformance_doc // ""' "${HTTP_BODY}")
BADGE_MD=$(jq -r '.resources.badge_embed_markdown // ""' "${HTTP_BODY}")
MESSAGE=$(jq -r '.message // ""' "${HTTP_BODY}")

echo "readiness_grade=${GRADE}" >> "${GITHUB_OUTPUT}"
echo "readiness_score=${SCORE}" >> "${GITHUB_OUTPUT}"
echo "readiness_max=${MAX}" >> "${GITHUB_OUTPUT}"
echo "ready_for_ctef=${READY}" >> "${GITHUB_OUTPUT}"
echo "trust_grade=${TRUST_GRADE}" >> "${GITHUB_OUTPUT}"
echo "trust_score=${TRUST_SCORE}" >> "${GITHUB_OUTPUT}"
echo "evidence_uri=${EVIDENCE_URI}" >> "${GITHUB_OUTPUT}"
echo "attestation_uri=${ATTEST_URI}" >> "${GITHUB_OUTPUT}"
echo "badge_markdown=${BADGE_MD}" >> "${GITHUB_OUTPUT}"

# Build job summary
{
echo "## CTEF v${VERSION} Conformance — \`${SERVER_ID}\`"
echo ""
echo "| Field | Value |"
echo "|---|---|"
echo "| Readiness | **${GRADE}** (${SCORE}/${MAX}) |"
echo "| Ready for CTEF | ${READY} |"
echo "| Trust grade | ${TRUST_GRADE} (${TRUST_SCORE}) |"
echo ""
echo "${MESSAGE}"
echo ""
echo "### Per-criterion results"
echo ""
jq -r '.criteria | to_entries[] | "- " + (if .value.pass then ":white_check_mark:" else ":warning:" end) + " **" + .key + "** — " + .value.criterion + (if .value.fix then "\n - Fix: " + .value.fix else "" end)' "${HTTP_BODY}"
echo ""
if [ "${ATTEST_URI}" != "" ]; then
echo "### Generated conformance document"
echo ""
echo "[\`/api/ctef/attest\` ↗](${ATTEST_URI})"
echo ""
fi
if [ "${BADGE_MD}" != "" ]; then
echo "### README badge"
echo ""
echo "\`\`\`markdown"
echo "${BADGE_MD}"
echo "\`\`\`"
fi
} >> "${GITHUB_STEP_SUMMARY}"

# Persist response for later steps
cp "${HTTP_BODY}" "${RUNNER_TEMP}/ctef-readiness.json"
echo "ctef_response_path=${RUNNER_TEMP}/ctef-readiness.json" >> "${GITHUB_OUTPUT}"

# Decide exit
if [ "${FAIL_ON_NON_COMPLIANT}" = "true" ] && [ "${READY}" != "true" ]; then
echo "::error title=CTEF NON-COMPLIANT::${SERVER_ID} did not meet all CTEF v${VERSION} criteria (${SCORE}/${MAX}). See job summary."
exit 1
fi

- name: Comment on PR
if: ${{ github.event_name == 'pull_request' && inputs.comment_on_pr == 'true' }}
shell: bash
env:
GH_TOKEN: ${{ env.GITHUB_TOKEN }}
SERVER_ID: ${{ inputs.server_id }}
CTEF_RESPONSE: ${{ steps.check.outputs.ctef_response_path }}
REPO: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
set -euo pipefail
if [ -z "${GH_TOKEN:-}" ]; then
echo "::warning title=GITHUB_TOKEN missing::Set 'permissions: pull-requests: write' and pass GITHUB_TOKEN as env. Skipping PR comment."
exit 0
fi
if [ ! -f "${CTEF_RESPONSE}" ]; then
echo "::warning::No CTEF response captured; skipping PR comment."
exit 0
fi

BODY_MD=$(jq -r '
"### CTEF v" + (.ctef_version // "?") + " Conformance — `" + (.server_id // "?") + "`\n\n" +
"**" + (.readiness_grade // "UNKNOWN") + "** (" + (.readiness_score | tostring) + "/" + (.readiness_max | tostring) + " criteria) — Trust grade " + (.criteria.trust_grade_assigned.value // "?") + " (" + (.criteria.trust_grade_assigned.score | tostring) + ")\n\n" +
(.message // "") + "\n\n" +
"<details><summary>Per-criterion results</summary>\n\n" +
( [.criteria | to_entries[] | "- " + (if .value.pass then "✅" else "⚠️" end) + " **" + .key + "** — " + .value.criterion + (if .value.fix then "\n - Fix: " + .value.fix else "" end) ] | join("\n") ) +
"\n\n</details>\n\n" +
(if .resources.generate_conformance_doc then "Generate the canonical conformance JSON: [`/api/ctef/attest`](" + .resources.generate_conformance_doc + ")\n\n" else "" end) +
"<sub>Posted by [ctef-conformance-check](https://github.com/vdineshk/daee-engine/tree/main/actions/ctef-conformance-check) — runtime evidence: " + (.observatory_url // "") + "</sub>"
' "${CTEF_RESPONSE}")

printf '%s' "${BODY_MD}" > "${RUNNER_TEMP}/ctef-comment.md"

# Use the GitHub API directly (no gh CLI dependency)
curl -sS -m 20 -X POST \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "Accept: application/vnd.github+json" \
-H "Content-Type: application/json" \
"https://api.github.com/repos/${REPO}/issues/${PR_NUMBER}/comments" \
-d "$(jq -Rs '{body: .}' "${RUNNER_TEMP}/ctef-comment.md")" \
-o "${RUNNER_TEMP}/ctef-comment-resp.json" \
-w "PR comment HTTP %{http_code}\n"
38 changes: 38 additions & 0 deletions actions/ctef-conformance-check/example-workflow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Drop-in CTEF conformance workflow for any MCP server repository.
# Save as `.github/workflows/ctef-conformance.yml` in your MCP server repo.
#
# 1. Replace `my-mcp-server` with your Observatory slug
# 2. (optional) Set fail_on_non_compliant: 'true' to gate merges
# 3. Commit and push — the next PR will receive a CTEF conformance comment

name: CTEF Conformance
on:
pull_request:
branches: [ main, master ]
workflow_dispatch:

permissions:
contents: read
pull-requests: write

jobs:
ctef-readiness:
name: Verify CTEF v0.3.2 conformance
runs-on: ubuntu-latest
steps:
- name: Check CTEF readiness via Dominion Observatory
id: ctef
uses: vdineshk/daee-engine/actions/ctef-conformance-check@main
with:
server_id: my-mcp-server
fail_on_non_compliant: 'false'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Surface results in workflow log
run: |
echo "Readiness: ${{ steps.ctef.outputs.readiness_grade }} (${{ steps.ctef.outputs.readiness_score }}/${{ steps.ctef.outputs.readiness_max }})"
echo "Trust grade: ${{ steps.ctef.outputs.trust_grade }} (${{ steps.ctef.outputs.trust_score }})"
echo "Ready for CTEF: ${{ steps.ctef.outputs.ready_for_ctef }}"
echo "Evidence: ${{ steps.ctef.outputs.evidence_uri }}"
echo "Attestation doc: ${{ steps.ctef.outputs.attestation_uri }}"
Loading