Public record
Software health reportschema 0.27.0 · metrics 2.5.0 · 2026-07-29 20:11 UTC

cboxdk / statamic-mcp

MCP (Model Context Protocol) server for Statamic CMS v6 — gives AI assistants structured access to content, blueprints, assets, and more.

PHPNo license detected★ 32 stars⑂ 10 forkssince Aug 2025View on GitHub ↗

cboxdk/statamic-mcp holds a health index of 71 out of 100, placing it in the Good band. It scores highest on Engineering Quality (87/100) and lowest on Community & Adoption (36/100). It was last updated today. A single contributor accounts for most of its recent work.

71
overall / 100
Good

Software health index

Metrics are grouped into weighted categories on one standardized 1–100 scale. Overall starts as their weighted mean, calibrated against the distribution of the public record so bands carry percentile meaning; when public evidence triggers the High-Risk Jurisdiction Policy, the rating is adjusted and receives an At Risk ceiling of 34.

71
Exceptional93-100The record's top tier (≈ top 5%); essentially all checked criteria met
Excellent80-92Strong across the board; minor gaps
Good65-79Healthy; gaps are limited and manageable
Moderate50-64Acceptable with notable gaps; review recommended
Weak35-49Material weaknesses across several areas
At Risk20-34Significant weaknesses; adoption warrants caution
Critical1-19Severe problems (abandoned, single-maintainer, no hygiene)
VitalityCommunity &AdoptionSustainability &GovernanceEngineeringQualitySecurityAI Readiness

Score profile

Each axis is a category. The shape matters more than the average — a healthy subject fills the whole shape, while a spike-and-crater profile means strength in one dimension is masking risk in another.

The weighted overall 63 is calibrated to 71 on the published index scale (record calibration 2026-08-02).

Ownership

CboxOrganization
6 followers66 public repossince Jun 2016

This repository is backed by an organization — shared, accountable stewardship that can outlive any single maintainer.

Package ecosystems

RegistryPackageVersionDownloads / moVersionsLast publishTags
Packagistcboxdk/statamic-mcpv2.7.05,3223124 days agotoolsdevelopmentlaravelcursoraistatamicmcpclaude

Metrics by category

Vitality

Is the project alive — is code being written and are releases shipping?

84Excellent · 21% of overall
How it's scored
36/36Push recency — last push 0 days ago
11.8/36Commit cadence — 17/52 weeks with commits
18/18Commit volume — 150 commits in the last year
8/10OpenSSF Scorecard: Maintained — 10 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 8
Inputs used
commits_last_year150
human_commit_share1
days_since_last_push0
active_weeks_last_year17

Release discipline

100Exceptional
How it's scored
27/27Ships releases — 31 releases published
36/36Release recency — latest release 24 days ago
27/27Release cadence — a release every ~9.1 days
0/10OpenSSF Scorecard: Signed-Releases — no data
Inputs used
releases_count31
latest_release_tagv2.7.0
releases_from_tagsno
days_since_latest_release24
mean_days_between_releases9.1
Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.

Community & Adoption

Does the project have users, downloads, attention, and a welcoming setup for contributors?

36Weak · 17% of overall
How it's scored
24.2/60Stars — 32 stars
8/25Forks — 10 forks
0/15Watchers — 0 watchers
Inputs used
forks10
stars32
watchers0
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
How it's scored
22.5/22.5README
0/22.5License — no license file detected
0/18CONTRIBUTING guide
0/13.5Code of conduct
0/7.2Issue template
6.3/6.3PR template
Inputs used
has_readmeyes
has_licenseno
readme_badges
has_contributingno
has_issue_templateno
has_code_of_conductno
readme_badge_services
has_pull_request_templateyes
How it's scored
49.7/80Monthly downloads — 5,322 downloads/month across packagist
0/20Registry dependents — 0 packages depend on it
Inputs used
packagescboxdk/statamic-mcp
dependents0
ecosystemspackagist
total_downloads19,659
monthly_downloads5,322

Sustainability & Governance

Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?

62Moderate · 23% of overall
How it's scored
9/54Bus factor — 1 contributor(s) cover half of all commits
0.9/22.5Commit distribution — top contributor authored 96% of commits
5.4/13.5Contributor breadth — 4 contributors
6/10OpenSSF Scorecard: Contributors — project has 2 contributing companies or organizations -- score normalized to 6
Inputs used
bus_factor1
contributors_sampled4
top_contributor_share0.96
How it's scored
42/42Issue resolution — 100% of issues closed
28.8/30PR acceptance — 25/26 decided PRs merged
0/13Newcomer PR acceptance — no first-time contributor's PR decided in 30d
0/15OpenSSF Scorecard: Code-Review — Found 1/18 approved changesets -- score normalized to 0
Inputs used
merged_prs25
open_issues0
closed_issues9
prs_merged_7d
prs_decided_7d
prs_merged_30d
prs_decided_30d
issue_closed_ratio1
closed_unmerged_prs1
first_time_authors_30d
first_time_prs_merged_30d
first_time_prs_decided_30d
Excluded from scoring (no data or not applicable): newcomer_pr_acceptance. Remaining weights renormalized.
How it's scored
30/30Ownership backing — organization-owned
0/20Verified domain
6.1/25Owner reach — 6 followers of cboxdk
25/25Track record — 66 public repos, account ~10 yr old
Inputs used
followers6
owner_typeOrganization
is_verified
owner_logincboxdk
public_repos66
account_age_days3,697

Package maintenance

100Exceptional
How it's scored
25/25Published & resolvable — 1 package(s) on packagist
35/35Publish recency — latest publish 24 days ago
20/20Version history — 31 published versions
20/20Not deprecated — active, not deprecated or yanked
Inputs used
packagescboxdk/statamic-mcp
ecosystemspackagist
any_deprecatedno
min_days_since_publish24

Engineering Quality

Are baseline engineering and documentation practices in place?

87Excellent · 19% of overall
How it's scored
24/24CI workflows — 3 workflow(s)
24/24Tests present
16/16Linter config — phpstan.neon
0/9.6Pre-commit hooks
0/6.4.editorconfig
14/20OpenSSF Scorecard: CI-Tests — 7 out of 9 merged PRs checked by a CI test -- score normalized to 7
Inputs used
has_ciyes
has_testsyes
has_editorconfigno
has_linter_configyes
has_precommit_configno

Documentation

100Exceptional
How it's scored
30/30README
25/25Documentation directory
15/15Documentation / homepage site — https://cbox.dk/packages/statamic-mcp
10/10Repository description
10/10Topics — 8 topics
10/10Wiki
Inputs used
topicsai, claude, development, mcp, statamic, cursor, laravel, tools
has_wikiyes
homepagehttps://cbox.dk/packages/statamic-mcp
has_readmeyes
has_docs_diryes
has_descriptionyes

Security

Are visible security and supply-chain practices strong, without unresolved high-risk jurisdiction exposure?

37Weak · 16% of overall
How it's scored
7.5/7.5Binary-Artifacts — no binaries found in the repo
0/7.5Branch-Protection — branch protection not enabled on development/release branches
1.8/2.5CI-Tests — 7 out of 9 merged PRs checked by a CI test -- score normalized to 7
0/2.5CII-Best-Practices — no effort to earn an OpenSSF best practices badge detected
0/7.5Code-Review — Found 1/18 approved changesets -- score normalized to 0
1.5/2.5Contributors — project has 2 contributing companies or organizations -- score normalized to 6
10/10Dangerous-Workflow — no dangerous workflow patterns detected
0/7.5Dependency-Update-Tool — no update tool detected
0/5Fuzzing — project is not fuzzed
0/2.5License — license file not detected
6/7.5Maintained — 10 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 8
0/5Packaging — no data
0/5Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
0/5SAST — SAST tool is not run on all commits -- score normalized to 0
0/5Security-Policy — security policy file not detected
0/7.5Signed-Releases — no data
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
7.5/7.5Vulnerabilities — 0 existing vulnerabilities detected
Inputs used
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate3.7
Excluded from scoring (no data or not applicable): packaging, signed_releases. Remaining weights renormalized.

AI Readiness

How well is the repo equipped to be developed and maintained with AI coding agents? Carries a deliberately small weight (4%): agent tooling is a real maintenance signal, but a repository with none can still reach 100/100.

54Moderate · 4% of overall
How it's scored
45/45Agent instructions — CLAUDE.md
0/15Machine-readable docs (llms.txt)
40/40Legible commit history — 94 of 100 human commits state their intent (structured subject or explanatory body)
Inputs used
has_llms_txtno
legible_history_share0.94
agent_instruction_filesCLAUDE.md
agent_instruction_max_bytes39,819
How it's scored
0/18One-command bootstrap
22/22Automated tests
11/11Lint / format config — phpstan.neon
0/11Static type checking
0/10Reproducible environment
10/10Demonstrated agent practice — 13 of the last 100 commits agent-authored or agent-credited
0/8Automated maintenance — no automated dependency updates observed
0/10OpenSSF Scorecard: Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
Inputs used
has_nixno
has_testsyes
lockfiles
has_dockerfileno
typed_languageno
bootstrap_files
has_devcontainerno
has_linter_configyes
typecheck_configs
agent_commit_share0.13
toolchain_manifests
dependency_bot_commit_share0
How it's scored
0/45Type-checkable code — PHP without a type-check config
55/55Manageable file sizes — 0/211 source files over 60KB
Inputs used
primary_languagePHP
largest_source_bytes50,386
source_files_sampled211
oversized_source_files0
How it's scored
0/40API schema (OpenAPI/GraphQL/proto)
20/20MCP server
0/40Runnable examples
Inputs used
example_dirs
has_mcp_signalyes
api_schema_files

Key facts

32GitHub stars
4contributors
150commits, last 12 months
0days since last push
31releases
1bus factor
0open issues
npm, Packagistpackage ecosystems

Data collection warnings

  • Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token
  • GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

More detail

Star and fork history 0 ★ / 10 ⇿
0Stars
10Forks
19Releases

When each star and fork was added, collected from GitHub and bucketed by day. Cumulative growth sits directly above the daily additions it is made of, so the two read against each other: steady organic accretion looks nothing like an abrupt, short-lived burst. Where that difference is measurable, it is reported as growth authenticity.

02468101012025-112026-022026-05
Major 1Minor 7Patch 8
OpenSSF Scorecard 3.7 / 10
3.7aggregate

Independent, tool-agnostic security assessment from the open-source OpenSSF Scorecard. Each check rewards a security practice, not a specific vendor's tool. Checks Scorecard could not determine are marked n/a and excluded from the security score (never counted as zero).Scorecard v5.5.0 · 2026-07-29 20:11 UTC

10Binary-Artifactsno binaries found in the repo
0Branch-Protectionbranch protection not enabled on development/release branches
7CI-Tests7 out of 9 merged PRs checked by a CI test -- score normalized to 7
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
0Code-ReviewFound 1/18 approved changesets -- score normalized to 0
6Contributorsproject has 2 contributing companies or organizations -- score normalized to 6
10Dangerous-Workflowno dangerous workflow patterns detected
0Dependency-Update-Toolno update tool detected
0Fuzzingproject is not fuzzed
0Licenselicense file not detected
8Maintained10 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 8
n/aPackagingpackaging workflow not detected
0Pinned-Dependenciesdependency not pinned by hash detected -- score normalized to 0
0SASTSAST tool is not run on all commits -- score normalized to 0
0Security-Policysecurity policy file not detected
n/aSigned-Releasesno releases found
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
10Vulnerabilities0 existing vulnerabilities detected
Direct dependencies 3
RegistryPackageVersion constraintManifest
Packagiststatamic/cms^6.6composer.json
Packagistlaravel/mcp^0.6 || ^0.7 || ^0.8composer.json
Packagistsymfony/yaml^7.0 || ^8.0composer.json
All dependencies not collected

The resolved dependency set could not be collected for this report: GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

Raw JSON report machine-readable
{
  "data": {
    "repo": {
      "topics": [
        "ai",
        "claude",
        "development",
        "mcp",
        "statamic",
        "cursor",
        "laravel",
        "tools"
      ],
      "is_fork": false,
      "size_kb": 1409,
      "has_wiki": true,
      "homepage": "https://cbox.dk/packages/statamic-mcp",
      "languages": {
        "PHP": 1447749,
        "Vue": 85343,
        "Blade": 6039,
        "JavaScript": 10640
      },
      "pushed_at": "2026-07-29T20:09:14Z",
      "created_at": "2025-08-29T20:52:25Z",
      "owner_type": "Organization",
      "updated_at": "2026-07-23T01:32:26Z",
      "description": "MCP (Model Context Protocol) server for Statamic CMS v6 — gives AI assistants structured access to content, blueprints, assets, and more.",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": null,
      "default_branch": "main",
      "license_spdx_raw": null,
      "primary_language": "PHP",
      "significant_languages": [
        "PHP"
      ]
    },
    "owner": {
      "blog": "www.cbox.dk",
      "name": "Cbox",
      "type": "Organization",
      "login": "cboxdk",
      "company": null,
      "location": "Denmark",
      "followers": 6,
      "avatar_url": "https://avatars.githubusercontent.com/u/19936443?v=4",
      "created_at": "2016-06-14T18:35:48Z",
      "is_verified": null,
      "public_repos": 66,
      "account_age_days": 3697
    },
    "license": {
      "state": "absent",
      "spdx_id": null,
      "raw_spdx": null,
      "file_present": false,
      "scorecard_found": false,
      "profile_has_license": false
    },
    "activity": {
      "releases": [
        {
          "tag": "v2.7.0",
          "kind": "minor",
          "published_at": "2026-07-05T10:36:38Z"
        },
        {
          "tag": "v2.6.1",
          "kind": "patch",
          "published_at": "2026-06-30T11:18:30Z"
        },
        {
          "tag": "v2.6.0",
          "kind": "minor",
          "published_at": "2026-06-02T21:27:17Z"
        },
        {
          "tag": "v2.5.0",
          "kind": "minor",
          "published_at": "2026-05-06T16:25:57Z"
        },
        {
          "tag": "v2.4.0",
          "kind": "minor",
          "published_at": "2026-05-05T13:54:48Z"
        },
        {
          "tag": "v2.3.0",
          "kind": "minor",
          "published_at": "2026-04-23T14:03:59Z"
        },
        {
          "tag": "v2.2.4",
          "kind": "patch",
          "published_at": "2026-04-14T13:38:36Z"
        },
        {
          "tag": "v2.2.3",
          "kind": "patch",
          "published_at": "2026-04-14T13:08:57Z"
        },
        {
          "tag": "v2.2.2",
          "kind": "patch",
          "published_at": "2026-04-14T11:44:31Z"
        },
        {
          "tag": "v2.2.1",
          "kind": "patch",
          "published_at": "2026-04-14T11:09:17Z"
        },
        {
          "tag": "v2.2.0",
          "kind": "minor",
          "published_at": "2026-04-14T10:42:45Z"
        },
        {
          "tag": "v2.1.0",
          "kind": "minor",
          "published_at": "2026-04-13T15:02:46Z"
        },
        {
          "tag": "v2.0.4",
          "kind": "patch",
          "published_at": "2026-04-10T11:10:12Z"
        },
        {
          "tag": "v2.0.3",
          "kind": "patch",
          "published_at": "2026-04-09T08:57:08Z"
        },
        {
          "tag": "v2.0.2",
          "kind": "patch",
          "published_at": "2026-03-19T09:02:29Z"
        },
        {
          "tag": "v2.0.1",
          "kind": "patch",
          "published_at": "2026-03-18T13:42:18Z"
        },
        {
          "tag": "v2.0.0",
          "kind": "major",
          "published_at": "2026-03-18T09:54:11Z"
        },
        {
          "tag": "v1.0.0-alpha.3",
          "kind": "prerelease",
          "published_at": "2026-01-23T11:22:17Z"
        },
        {
          "tag": "v1.0.0-alpha.2",
          "kind": "prerelease",
          "published_at": "2026-01-23T09:26:02Z"
        },
        {
          "tag": "v1.0.0-alpha.1",
          "kind": "prerelease",
          "published_at": "2025-12-13T06:40:53Z"
        },
        {
          "tag": "v0.11.0",
          "kind": "minor",
          "published_at": "2025-12-10T12:37:49Z"
        },
        {
          "tag": "v0.10.0",
          "kind": "minor",
          "published_at": "2025-11-02T23:51:13Z"
        },
        {
          "tag": "v0.9.0",
          "kind": "minor",
          "published_at": "2025-09-27T18:31:48Z"
        },
        {
          "tag": "v0.8.0",
          "kind": "minor",
          "published_at": "2025-09-08T10:34:53Z"
        },
        {
          "tag": "v0.7.0",
          "kind": "minor",
          "published_at": "2025-09-08T10:20:26Z"
        },
        {
          "tag": "v0.6.0",
          "kind": "minor",
          "published_at": "2025-09-08T09:45:16Z"
        },
        {
          "tag": "v0.5.0",
          "kind": "minor",
          "published_at": "2025-09-08T09:40:18Z"
        },
        {
          "tag": "v0.4.0",
          "kind": "minor",
          "published_at": "2025-09-08T09:28:35Z"
        },
        {
          "tag": "v0.3.0",
          "kind": "minor",
          "published_at": "2025-09-04T06:39:05Z"
        },
        {
          "tag": "v0.2.0",
          "kind": "minor",
          "published_at": "2025-09-03T20:06:56Z"
        },
        {
          "tag": "v0.1.0",
          "kind": "minor",
          "published_at": "2025-09-03T07:24:21Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "ec433cc971b7ba318c389da68cc4539fb01ac517",
          "body": "Allow laravel/mcp ^0.8 alongside ^0.6 and ^0.7. Fixes null blueprint\nhandle in types analysis and output buffer cleanup type safety flagged\nby stricter dependency types.",
          "is_bot": false,
          "headline": "Release v2.7.0",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-07-05T10:36:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c7114e2a9b84ccdde99aadc8c0f22db577980b42",
          "body": null,
          "is_bot": false,
          "headline": "Release v2.6.1",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-06-30T11:18:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "390d2e4e25172885087b4136b60a6acbf9845e11",
          "body": "Fixes #34.",
          "is_bot": false,
          "headline": "Fix confirmation token retry loop",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-06-30T11:15:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "38a6c2b87d79b8eeeae404e06ce7c77b39f4734b",
          "body": null,
          "is_bot": false,
          "headline": "Fix Eloquent user IDs for MCP dashboard (#33)",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-06-02T21:25:15Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e79a3fbf7b64741d6d9f0839dd717a5bab8b079d",
          "body": "Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: add v2.5.0 changelog",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-05-06T16:25:44Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "bd4f895cf31ccb77f92d3df772cc777830b63adb",
          "body": "* feat(entries): add revision-aware behavior to EntriesRouter\n\nWhen a collection has revisions enabled, the MCP server now respects\nStatamic's editorial workflow instead of bypassing it with direct saves.\n\nChanges:\n- Update on published entry creates a working copy (published content unchanged)\n- Cr\n[…]\n Add revision_message type check in publishWorkingCopyAction\n- Add missing PHPDoc annotations and return types for PHPStan L8\n\n* fix: resolve PHPStan L8 error in filterOutputFields mixed offset access",
          "is_bot": false,
          "headline": "feat(entries): add revision-aware behavior to EntriesRouter (#30)",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-05-06T16:22:06Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "df632b0015080fd08b205201fbd306c734c1da6f",
          "body": "Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: add v2.4.0 changelog",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-05-05T13:54:32Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "90fdb1a56900927e6664997a4d3164bd7d15b746",
          "body": "Login was called per-test (5× per run), hitting Statamic's login\nthrottle on the last test. Switch to globalSetup + storageState so\nlogin happens once and all tests reuse the authenticated session.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(ci): use shared auth state to prevent Playwright login throttling",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-05-05T13:39:47Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "1765e715d8dc456bcf701963c9fb5f7a66ce2ce4",
          "body": "… (#29)\n\nFixes ENG-804.\n\nAdds a FieldFormatSpec service that derives a wire-format guidance object\nfrom each Statamic Field — bard inline vs full, replicator/grid/group item\nshape, allowed set types, recursive set definitions, markdown vs ProseMirror\ndistinction, relationship/asset/date input shapes\n[…]\n leaking internals.\n\nSchema descriptions on BlueprintsRouter were also corrected to reflect the\nactual list-vs-get scoping of include_fields, include_config,\ninclude_format_spec, and max_format_depth.",
          "is_bot": false,
          "headline": "feat(blueprints): emit per-field wire-format spec to guide MCP agents…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-05-05T13:01:05Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5bfe2d278ad0ea1c4c8e800e34bcad81ae6cbaf7",
          "body": "…n-27\n\nfix(entries): exclude current entry from slug uniqueness on update (#27)",
          "is_bot": false,
          "headline": "Merge pull request #28 from SAY-5/fix/update-entry-slug-self-collisio…",
          "author_name": "Sai Asish Y",
          "author_login": "SAY-5",
          "committed_at": "2026-05-05T13:00:43Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "eedbe53a2156f41f8d50ee4a9c2ae05ab94c9bcb",
          "body": "…ions\n\nfeat: make confirmation-token action list configurable per domain",
          "is_bot": false,
          "headline": "Merge pull request #26 from cboxdk/feat/configurable-confirmation-act…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-24T05:51:42Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "564795dc55fbc6e7991346165a80001e7d33af13",
          "body": "Previously the confirmation-token flow was hardcoded to gate only\n'delete' on every router plus 'create'/'update' on blueprints.\nOperators running STATAMIC_MCP_CONFIRMATION_ENABLED=true in production\nhad no way to require confirmation on other destructive actions\n(entries.update, globals.update, use\n[…]\nctionGate. Domains not listed\nfall back to 'default'; '*' gates every action; [] disables the gate\nfor a domain. Shipped defaults reproduce the original behaviour, so\nexisting consumers see no change.",
          "is_bot": false,
          "headline": "feat(confirmation): configurable per-domain action gate",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-23T14:43:48Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d1cef678a98aef02f6c8b0b9d3ba5ace60085987",
          "body": "fix(sanitizer): normalize table cells to scalars before persist",
          "is_bot": false,
          "headline": "Merge pull request #25 from cboxdk/fix/sanitize-table-cells",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-23T14:02:58Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6f4c3500c611f5c6bfe26201033aac00f667026a",
          "body": "…cate\n\nlaravel/mcp's AddWwwAuthenticateHeader resolves the RFC 9728\nresource_metadata URL through route-name lookup. When that route name\nisn't visible at request time (observed in the Playwright e2e env\nrunning under `php artisan serve`) it falls back to a generic\n`Bearer realm=\"mcp\", error=\"invali\n[…]\nlace the middleware in the container with a subclass that builds\nthe discovery URL directly via url(), so the pointer is emitted as\nlong as OAuth is configured — regardless of route-name availability.",
          "is_bot": false,
          "headline": "fix(oauth): always emit resource_metadata pointer in 401 WWW-Authenti…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-23T13:50:49Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "049d2d3d0bf639ea68d85840037296c8c32755f2",
          "body": "Table fieldtype stores each cell as a bare string or null, but LLM\noutput and template-layer augmentation commonly wrap cells as\n`['value' => scalar]`. That form rendered fine on the frontend while\nthe CP displayed `[object Object]` because the editor reads raw storage.\n\nAdd sanitizeTableValue() to the existing SanitizesFieldData trait so\ndirect table fields and tables nested inside Bard sets get normalized\n(unwrap `value` key, reject unknown shapes) before they reach disk.",
          "is_bot": false,
          "headline": "fix(sanitizer): normalize table cells to scalars before persist",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-23T13:28:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "55b285935ef25275bd4436651da0ebc21e7ddd0a",
          "body": "Resolves PHPStan Level 8 errors where array<mixed, mixed> was passed\nto ResourcePolicy::filterFields() which expects array<string, mixed>.",
          "is_bot": false,
          "headline": "fix: add PHPStan type assertions for filterFields() calls",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-20T07:47:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7a10b8c9c5ff6f3c411894eadd2837d4b4977df8",
          "body": "docs: add Node.js TLS certificate fix for Laravel Herd/Valet",
          "is_bot": false,
          "headline": "Merge pull request #24 from cboxdk/docs/node-tls-certificate-fix",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-20T07:15:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "541ce99ce763f70e0cdacda3c6e881e95ffcaa62",
          "body": "…th Laravel Herd/Valet",
          "is_bot": false,
          "headline": "docs: add troubleshooting guide for Node.js TLS certificate issues wi…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-20T07:14:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2c1036ee212ed95b4af4a0d037e84e446930506f",
          "body": "…de failure\n\n- BaseStatamicTool: return structured response for all standard envelopes\n  (not just success), so confirmation token data payloads are preserved\n- ConfirmationTokenManager: handle json_encode failure explicitly\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: preserve structured data in error responses and handle json_enco…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-17T21:06:19Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "8f45724d267d355d83b97fd895610e9405fe990f",
          "body": "Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: add confirmation tokens and resource policy to CLAUDE.md",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-17T20:30:32Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "4d7d61c66a569650e6ed20df26411315d62dfdad",
          "body": "Old test expected 'Deletion requires explicit confirmation' from the\nremoved confirm parameter. Updated to test that deleting a nonexistent\nblueprint returns 'Blueprint not found' (CLI context bypasses confirmation).\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test: fix blueprint delete test for confirmation token changes",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-17T20:29:52Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "12d864181b9536f52e024276abf1c11bd5f0eb0f",
          "body": "BaseRouter now calls checkResourceAccess(), handleConfirmation(),\nfilterInputFields(), and filterOutputFields() for all routers.\nRemoves old confirm parameter from BlueprintsRouter.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: wire confirmation tokens and resource policy into router flow",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-17T20:27:01Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "d8ce65b8a237ebcc71e3dccee101b1baa59793bd",
          "body": "Trait provides resource access checks (glob-based) and field filtering\n(input + output) for routers. Not yet wired into BaseRouter.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: add EnforcesResourcePolicy trait for resource authorization",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-17T20:25:55Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "55855e3f6818600464bb0461dc4e888736243a0d",
          "body": "Trait provides handleConfirmation() for routers to call before\ndestructive actions. Not yet wired into BaseRouter.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: add RequiresConfirmation trait for destructive operations",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-17T20:25:55Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "a4c18733f937f892dd37b975f3f032c5c4f37176",
          "body": "Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: register ConfirmationTokenManager and ResourcePolicy singletons",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-17T20:24:51Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "e147e41bc56996dc8d1f8ad652f254ad976f3cc8",
          "body": "Adds confirmation section (enabled auto-detect, 300s TTL) and extends\neach tool domain with resources (read/write glob lists) and denied_fields.\nDefaults are fully backwards compatible (everything open).\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "config: add confirmation tokens and resource policy settings",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-17T20:24:47Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "bfec36cf6cfe5b42856daf971db8da38e65da335",
          "body": "Glob-based resource allowlists (read/write per domain) and recursive\nfield deny list filtering. Unconfigured domains default to allow-all\nfor backwards compatibility.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(auth): add ResourcePolicy for granular resource authorization",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-17T20:23:30Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "bad013b67b06efc4fd3920abfe146558cb88ac62",
          "body": "Adds ConfirmationTokenManager with generate/validate/isEnabled methods.\nTokens are cryptographically bound to tool + arguments, expire via TTL,\nand auto-detect production vs development environments.\n\nCo-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(auth): add stateless HMAC confirmation token manager",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-17T20:23:23Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "dd70b44348fbf499ee9d6e293db8503300b1d3ab",
          "body": "8 additional discovery endpoint tests:\n- authorization_endpoint follows custom CP route config\n- authorization_endpoint handles CP route with leading/trailing slashes\n- revocation_endpoint is present in AS metadata\n- full client discovery flow (protected-resource → path-suffixed AS\n  metadata → CIMD\n[…]\nith default path, custom path, and CIMD off\n- root vs path-suffixed response field-by-field identity checks for\n  both authorization-server and protected-resource endpoints\n\nBumps CHANGELOG to v2.2.4.",
          "is_bot": false,
          "headline": "test(oauth): add full discovery flow, CP route, and revocation tests",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T13:38:22Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b35268abd2656f9b4e2f463e3f258044ec471351",
          "body": "…and path-suffixed routes\n\n9 new tests covering:\n- CIMD config missing entirely (shallow merge scenario)\n- CIMD config as string \"true\"/\"false\" (env var behavior)\n- CIMD config as zero (explicit disable)\n- Path-suffixed authorization server discovery (RFC 8414 §3.1)\n- Path-suffixed protected resource discovery\n- Custom web path with path-suffixed discovery\n- Deeply nested and single-segment path suffixes\n\nAlso updates CHANGELOG to consolidate v2.2.0–v2.2.3 fixes.",
          "is_bot": false,
          "headline": "test(oauth): add discovery endpoint tests for CIMD config edge cases …",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T13:18:08Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "db4195e1900e0fae19d8ca4b6140561a7324fb66",
          "body": "MCP clients following the 2025-11-25 spec use path insertion for\ndiscovery: a server at /mcp/statamic triggers a fetch to\n/.well-known/oauth-authorization-server/mcp/statamic first.\n\nWithout these routes the request returned 403, so ChatGPT never\nsaw client_id_metadata_document_supported and disabled CIMD.",
          "is_bot": false,
          "headline": "fix(oauth): register path-suffixed discovery endpoints (RFC 8414 §3.1)",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T13:08:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a7dca2754f471802b6777c5d6f966475274c86f8",
          "body": "mergeConfigFrom() only does shallow merge — published configs missing\nthe cimd_enabled key return null, disabling CIMD silently.",
          "is_bot": false,
          "headline": "fix(oauth): add default true to all cimd_enabled config lookups",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T11:44:20Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "87ceeb1330336840d2449779472020bc847d492e",
          "body": "config() returns env() strings, not booleans. The strict === true\ncheck always failed, so CIMD was never advertised in discovery\nmetadata and never resolved during authorization or token exchange.",
          "is_bot": false,
          "headline": "fix(oauth): use boolean cast for cimd_enabled config check",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T11:09:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fb1b0ec570ec79f6dee355c73e2a5fad014ba0fd",
          "body": null,
          "is_bot": false,
          "headline": "chore: set CHANGELOG to v2.2.0 — 2026-04-14",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T10:42:32Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5d68cf96a5b447a3b095d9b3d00d9b25eec6a29c",
          "body": "Add Client ID Metadata Document (CIMD) support for OAuth 2.1",
          "is_bot": false,
          "headline": "Merge pull request #16 from cboxdk/add-cimd-support",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T10:40:32Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9a31d46c668840ab0e5454960f47fe40b1e66c12",
          "body": null,
          "is_bot": false,
          "headline": "Auto-fix code formatting [skip ci]",
          "author_name": "GitHub Action",
          "author_login": "actions-user",
          "committed_at": "2026-04-14T10:22:22Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a3e8482481da504fdc8d5b35e9f52e2a7b5429e8",
          "body": null,
          "is_bot": false,
          "headline": "feat: [US-006] - Token endpoint CIMD integration and E2E flow",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T10:22:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fd31f38fa240a277a5f050a007ea78e394c71868",
          "body": null,
          "is_bot": false,
          "headline": "feat: [US-005] - Authorization flow CIMD integration",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T10:22:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "18239964927c0d238a36421526bf3f1ac67c87a8",
          "body": null,
          "is_bot": false,
          "headline": "feat: [US-004] - OAuthClient CIMD fields and discovery endpoint",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T10:22:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "64290180fd84f394c0f1d13e7fa043f094e83507",
          "body": null,
          "is_bot": false,
          "headline": "feat: [US-003] - CIMD resolver service with SSRF protection",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T10:22:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "90d99de3441792e89b9d4fe770265ea15c052ff5",
          "body": null,
          "is_bot": false,
          "headline": "feat: [US-002] - CIMD metadata document parsing and validation",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T10:22:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "24e2a0b1795ef18d8216c1e329a69f5223b979b7",
          "body": null,
          "is_bot": false,
          "headline": "feat: [US-001] - CIMD config and client ID URL validator",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T10:22:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f232c3153d59b7c7ae11ab3ed1bc050b879e93b4",
          "body": "…allback\n\nfix(routers): fall back to incoming-only validation on TypeError in update",
          "is_bot": false,
          "headline": "Merge pull request #21 from cboxdk/fix/update-validation-type-error-f…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T10:20:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1ba67e5ca2b422f3a8924eba73e4efd0af9fc93a",
          "body": null,
          "is_bot": false,
          "headline": "chore: remove internal ticket and client references from comments",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T09:27:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "140b5a291d29f9013cd6bfaf28a725c82e2c7445",
          "body": "…pdate\n\nThe update action on entries, terms, and globals ran the FieldsValidator\non ALL merged data (existing + incoming). Third-party fieldtypes (e.g.,\nSEO Pro) whose preProcessValidatable or extraRules methods cannot handle\nstored data formats threw TypeError, breaking every update regardless of\np\n[…]\nalready valid when saved.\n\nAdds 10 tests covering deep nested replicator/bard/grid/group\nblueprints, round-trip create→update, partial updates with required\nfields, and a simulated crashing fieldtype.",
          "is_bot": false,
          "headline": "fix(routers): fall back to incoming-only validation on TypeError in u…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-14T09:25:47Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "062e90cfb55e46764b0b1a0192459c33591034e9",
          "body": "…-remote\n\nClaude Desktop's config file only supports stdio transport — the `url`-based\nformat we documented doesn't work there. Replace with two valid options:\nOAuth Connectors (recommended) and mcp-remote stdio bridge (fallback).\n\nFixes #20",
          "is_bot": false,
          "headline": "docs: fix Claude Desktop setup instructions to use Connectors and mcp…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T16:23:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e02c06aea75650e35abf04b037d39b6c8cccd9a9",
          "body": null,
          "is_bot": false,
          "headline": "chore: set CHANGELOG to v2.1.0 — 2026-04-13",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T15:02:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8e6b3b794379edf6df6c3f7fb9069961b0c8fb78",
          "body": "Fix entry/term data pipeline, field persistence, and hardening",
          "is_bot": false,
          "headline": "Merge pull request #19 from cboxdk/fix-nested-field-offset",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T15:01:27Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "45527181e1d0d90bf4170bf340c1852c183ac388",
          "body": "…nforce HTTPS\n\n- Add fflush() before flock(LOCK_UN) in exchangeCode() and\n  exchangeRefreshToken() — without it, concurrent requests could both\n  read 'used: false' from the disk buffer and double-spend an auth code\n  or refresh token. Same bug we fixed in FileTokenStore.\n\n- Strip HTML tags from cli\n[…]\ner, token, revoke) in\n  EnsureSecureTransport middleware so tokens cannot be exchanged over\n  plain HTTP in production. Discovery endpoints remain unprotected\n  since they expose only public metadata.",
          "is_bot": false,
          "headline": "fix(oauth): flush writes before lock release, sanitize client_name, e…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T14:15:00Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b6190cc88331a82d882c3cc1c42e181c6a94965a",
          "body": "AuthorizeController had three config() calls for default_scopes with\ndifferent fallbacks: two used ['content:read'], one used ['*']. All\nthree now fall back to [] — the config file is the single source of\ntruth for default scopes.",
          "is_bot": false,
          "headline": "fix(oauth): remove inconsistent hardcoded scope fallbacks",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T13:43:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "be5494b4b89eafa55160bbc02c988e18f5ebd95d",
          "body": "…read only\n\nA client with only content:read cannot read blueprints, structures, or\nsystem info — making it effectively useless for MCP discovery. Default\nto all :read scopes so OAuth clients can explore the CMS out of the box\nwithout write access.",
          "is_bot": false,
          "headline": "fix(config): use all read scopes as OAuth default instead of content:…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T13:36:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bdc20669f7372776cc3c4cb1d835997df43fe27d",
          "body": null,
          "is_bot": false,
          "headline": "chore: remove leftover .context repro test file",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T13:34:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "37aca91b3da8626ea0356faeff1884ae30d6c53a",
          "body": "…fault\n\n- BlueprintsRouter: close file handle when flock() fails but fopen()\n  succeeded — previously leaked the descriptor under contention.\n\n- FileTokenStore: add fflush() before releasing lock in updateIndex()\n  and removeFromIndex() to prevent partial writes on crash. The other\n  write methods (\n[…]\nebuildIndex) already had this.\n\n- Config: change OAuth default_scopes from '*' (all permissions) to\n  'content:read'. Operators can still override via env var but the\n  default is no longer wide open.",
          "is_bot": false,
          "headline": "fix: file handle leak, missing fflush, and overly permissive OAuth de…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T12:57:22Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "125db1daf463598fe4885b5fad0d61a003bf0720",
          "body": "…ompat\n\nThe method exists because this addon stored data without the fieldtype\nprocess() step prior to v2.1 — not because of Statamic legacy formats.\nMark as deprecated with clear removal criteria.",
          "is_bot": false,
          "headline": "docs: clarify that sanitizeStoredFieldDataForValidation is backward c…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T12:38:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "73e2c32ec32acc92f960e4e8fbd0b71029f75c46",
          "body": "Match the Statamic CP pipeline by calling $fields->process()->values()\nafter validation and before saving. Previously the routers stored raw\nvalidated data, skipping fieldtype transformations:\n\n- Terms::process() wraps values via Arr::wrap and strips taxonomy\n  prefixes\n- Bard::process() normalizes \n[…]\n)\nwas needed as a bandage to re-normalize on subsequent updates.\n\nFor updates, only the incoming data is processed (not existing stored\nvalues), preventing double-processing of already-stored content.",
          "is_bot": false,
          "headline": "fix(routers): process field data through fieldtypes before storage",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T12:05:08Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d55c9fbc1d18691ea03b63699916164ccb1decfd",
          "body": "… update\n\nAudit of all write paths found the same silent-drop pattern that caused\nENG-697:\n\n- HandlesTaxonomies: create/update now handle preview_targets and\n  default_status (previously only in configureTaxonomy). Removed the\n  broken collections() call from configureTaxonomy — Taxonomy::collection\n[…]\n now handle collections (previously\n  only in configureNavigation).\n\n- SanitizesFieldData: add checkboxes to relationship-style normalization\n  so bare strings are wrapped to arrays before validation.",
          "is_bot": false,
          "headline": "fix(structures): add missing fields to taxonomy/navigation create and…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T11:47:21Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "37a271867a7527e323914594d2cde520cd2a02a8",
          "body": "…istence\n\nSanitize relationship field values (terms, entries, users, assets) by\nwrapping bare strings in arrays before validation — LLMs often send\n\"slug\" instead of [\"slug\"], which crashed Statamic's validator with\n\"Cannot access offset of type string on string\".\n\nAlso add taxonomies handling to collection create/update in the\nstructures router — the field was silently ignored by the match\nstatement, so taxonomies never persisted despite a success response.\n\nRefs: ENG-697",
          "is_bot": false,
          "headline": "fix(entries): handle terms field updates and collection taxonomy pers…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-13T11:37:10Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a6f4bc8c6d81c3ead2ee7a8c69863a4e3f3b97cc",
          "body": null,
          "is_bot": false,
          "headline": "Fix nested field offset sanitization",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-11T05:28:41Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a0dd632a3fd2248bfeefe80cb7685c95a7afb6ef",
          "body": null,
          "is_bot": false,
          "headline": "chore: set CHANGELOG to v2.0.4 — 2026-04-10",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-10T11:10:02Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d4f19eae07096d89f365e844b022cd7b1c88db78",
          "body": "fix: date normalization and entry property extraction",
          "is_bot": false,
          "headline": "Merge pull request #18 from cboxdk/fix/date-normalization-and-validation",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-10T11:09:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "95625359a3f1c0a03e1585a86594aad002c4d80c",
          "body": "Three related bugs fixed:\n\n1. addValues() exceptions escaping try-catch — Statamic's\n   Fields::addValues() was called outside the try-catch block in all\n   routers, causing \"Cannot access offset of type string on string\"\n   errors to propagate uncaught.\n\n2. Entry-level date/published not extracted \n[…]\nd dates as Y-m-d, Y-m-d H:i, ISO 8601, or {date, time}\n   objects. Added NormalizesDateFields trait that inspects the blueprint,\n   finds all date-type fields, and normalizes values before validation.",
          "is_bot": false,
          "headline": "fix: handle date normalization and entry property extraction in routers",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-10T10:59:53Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "332cf17c14cf2642cb82186255182bf034f7a5e7",
          "body": null,
          "is_bot": false,
          "headline": "Auto-fix code formatting [skip ci]",
          "author_name": "GitHub Action",
          "author_login": "actions-user",
          "committed_at": "2026-04-09T08:58:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e6d657d9e7632a7a27cb670ddf1f31b4846ab9f2",
          "body": null,
          "is_bot": false,
          "headline": "chore: set CHANGELOG to v2.0.3 — 2026-04-09",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-09T08:56:57Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a5d61b3b41fe5943d3cdbf82effe3173b11cdd2b",
          "body": "fix: blueprint update merges fields instead of replacing",
          "is_bot": false,
          "headline": "Merge pull request #17 from cboxdk/fix/blueprint-update-data-loss",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-09T08:56:17Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "85b1fbd970695a9ed2f0b8afabd7edc4b1da4a7b",
          "body": "…ting fields\n\nThe update action was silently destroying all existing blueprint fields when\nadding new ones. Three compounding issues:\n\n1. Fields were replaced (`$contents['fields'] = $new`) instead of merged\n2. Existing fields were read from `$contents['fields']` which is always empty\n   after save \n[…]\nall()` collapsed multi-tab\n   blueprints into a single \"main\" tab\n\nNow: update merges by default (preserving tab/section structure), with an\nexplicit `replace_fields=true` opt-in for full replacement.",
          "is_bot": false,
          "headline": "fix: blueprint update now merges fields instead of replacing all exis…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-04-09T08:46:52Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "92e7cdd6408944ded18452f84c8cc1932720247e",
          "body": null,
          "is_bot": false,
          "headline": "chore: set CHANGELOG to v2.0.2 — 2026-03-19",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-19T09:02:21Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "17fd70e5b515851c316e2599858ade8a07fe53a6",
          "body": "fix: install command no longer crashes without a database",
          "is_bot": false,
          "headline": "Merge pull request #15 from cboxdk/fix/install-command-resilience",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-19T08:59:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8373fec40a93f263250ee57eed4ee5accf120b58",
          "body": "- Config publish: user confirming \"overwrite\" now actually forces the\n  publish (previously --force stayed false, so vendor:publish silently\n  skipped the file)\n- Migrations: skip automatically when all storage drivers are file-based\n  (the default); only run when DatabaseTokenStore, DatabaseAuditSt\n[…]\nhDriver is configured\n- Add --skip-migrations flag as an explicit escape hatch\n- Wrap migrate call in try/catch with actionable guidance on failure\n- Completion message reflects what actually happened",
          "is_bot": false,
          "headline": "fix: install command no longer crashes without a database",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-19T08:48:53Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3ab9e9dab029d61cd45d8be7b36503a4633424bc",
          "body": null,
          "is_bot": false,
          "headline": "chore: set CHANGELOG to v2.0.1 — 2026-03-18",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T13:42:06Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "740f0cca97018e920107aa3e6c8eb0175eda4171",
          "body": "fix: token form UX improvements",
          "is_bot": false,
          "headline": "Merge pull request #14 from cboxdk/fix/token-form-ux",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T13:41:30Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "15723edc998a5a9b343250679a495077cf0756f9",
          "body": "… improve error feedback\n\n- Remove hard `before` validation on expires_at — max_token_lifetime_days is a default suggestion, not a block\n- Add scope presets (Read Only, Content Editor, Full Access) matching documented common combinations\n- Use Statamic toast notifications for all token CRUD operatio\n[…]\ntoken table (shows preset name instead of individual scopes)\n- Set stack size to half for better proportions\n- Remove internal docs/superpowers directory (development plans/specs, not for public repo)",
          "is_bot": false,
          "headline": "fix: token form UX — remove date validation block, add scope presets,…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T13:27:56Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "7497d259ea52dc2b88ce6aedb98d608e966607f4",
          "body": "v2.0.0: Storage drivers, OAuth 2.1, audit overhaul, security hardening",
          "is_bot": false,
          "headline": "v2.0.0: Storage drivers, OAuth 2.1, audit overhaul, security hardening",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T09:53:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e2ca418e9ad1c0ba9502947aef3433fbf88f923b",
          "body": null,
          "is_bot": false,
          "headline": "chore: set CHANGELOG to v2.0.0 — 2026-03-18",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T09:41:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "12ac0c0c6a371bad30a09ba94cf324ff19a89f80",
          "body": "Extracts findTokenWithPermission() to handle the admin vs regular user\ntoken lookup consistently across update, regenerate, and destroy methods.\n\nAdmins (super or with manage/revoke all mcp tokens) can operate on any\ntoken. Regular users can only operate on their own.",
          "is_bot": false,
          "headline": "fix: allow admins to edit and regenerate any token, not just their own",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T09:27:12Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b8f9314cd49d340c9f10629d03fb7aa6cadff12b",
          "body": "TokenController::destroy() only searched the current user's tokens,\nso admins on the All Tokens page couldn't delete tokens belonging to\nother users (e.g. OAuth tokens with unknown/different user IDs).\n\nNow checks 'revoke all mcp tokens' permission — if granted, searches\nall tokens. Falls back to own-tokens-only for regular users.",
          "is_bot": false,
          "headline": "fix: allow admins to delete any token, not just their own",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T09:19:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "fe26135b64c1057acb6e899786d14a46127e23a1",
          "body": "All runtime dependencies already support Laravel 13:\n- statamic/cms ^6.6 supports Laravel 12 and 13\n- laravel/mcp ^0.6 supports Laravel 11, 12, and 13\n\nChanges:\n- orchestra/testbench: drop ^9.0 (Laravel 11), keep ^10.0 (L12) + ^11.0 (L13)\n- Update docs to reflect Laravel 12/13 support",
          "is_bot": false,
          "headline": "feat: add Laravel 13 support",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T09:02:49Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b4cc6689390711f4977b04e7bb64279ef98c241f",
          "body": "- UPGRADE.md: step-by-step migration guide from v1.x to v2.0\n- CHANGELOG.md: merge premature [2.0.0] section back into [Unreleased],\n  add all recent changes (security hardening, DRY cleanup, OAuth quotas),\n  fix inaccurate action lists and scope counts",
          "is_bot": false,
          "headline": "docs: add UPGRADE.md and consolidate CHANGELOG for v2.0 release",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T09:00:15Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9037af4a46ae03e1b1e55ae2a5e6dda4a8d587ba",
          "body": "- Scope count 17→21 (missing content-facade scopes)\n- Tool count 12→11\n- Fix incorrect action lists for globals, system, content-facade, structures\n- Remove references to non-existent config keys (decay_minutes, web.middleware,\n  STATAMIC_MCP_MAX_TOKENS, STATAMIC_MCP_TOKEN_EXPIRY)\n- Fix oauth.max_clients default 1000→50, add max_clients_per_ip docs\n- Clarify default storage is file-based YAML, not database",
          "is_bot": false,
          "headline": "docs: fix 26 factual errors across all documentation",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T08:57:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "d0254eec817a4e357c4c84f949613cf302fdc580",
          "body": "Security:\n- OAuth client registration: per-IP quota (5), global max 50, stricter rate limit (3/hr)\n- Directory permissions 0700 for token/OAuth storage\n- Correlation ID validated (alphanumeric, max 128 chars) instead of blindly trusted\n- HTTPS error no longer leaks env variable name\n- DatabaseTokenS\n[…]\nruntime effect)\n\nOther:\n- parseBytes() replaced with PHP 8.3 ini_parse_quantity()\n- Fixed isWebContext/isCliContext inconsistency across routers\n- OAuth test isolation: clean storage between test runs",
          "is_bot": false,
          "headline": "refactor: security hardening, DRY cleanup, and dead code removal",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-18T08:28:27Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d801577527eac78c6e823dd5cbf3c549c3675de7",
          "body": "…ecture\n\nSecurity:\n- Delete OAuth refresh tokens after exchange (single-use rotation)\n- Add X-Frame-Options and X-Content-Type-Options to all responses\n- Reject CORS wildcard in production instead of just warning\n- Default OAuth scopes to config value instead of full access\n- Reject bearer tokens ov\n[…]\nis tests (20 tests)\n- Add PruneExpiredTokensCommand tests (3 tests)\n- Add ClientConfigGenerator tests (17 tests)\n\nFrontend:\n- Split McpPage.vue (747→80 lines) into ConnectPanel, TokenList, useTokenApi",
          "is_bot": false,
          "headline": "refactor: comprehensive codebase review fixes — security, DRY, archit…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T23:32:32Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "71af40d9d3340778fbce27883059490acc76d7d5",
          "body": "High:\n- Fix empty scopes when OAuth client omits scope parameter: fallback\n  to all TokenScope values so tokens are functional\n\nMedium:\n- RegistrationController: use $e->httpStatus instead of $e->getCode()\n- SchemaTool: correct ContentFacadeRouter catalog (content_audit,\n  cross_reference — not exec\n[…]\no OAuth discovery metadata (RFC 8414)\n- Remove spurious success key from DiscoveryTool response\n- Fix wrong scope names in InstallCommand docs (navigation→structures,\n  remove non-existent forms:read)",
          "is_bot": false,
          "headline": "fix: address all 6 fourth-pass review findings",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T21:59:32Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "73e8697d5c352f580b740516677654e6d107e73c",
          "body": "Critical:\n- Fix open redirect in OAuth authorize: validate client_id and\n  redirect_uri BEFORE any redirect-based error responses. Errors for\n  response_type, code_challenge, code_challenge_method now use the\n  validated redirect_uri.\n\nHigh:\n- Revoke existing OAuth tokens for same client+user before\n[…]\netAccessible(true).\n\nLow:\n- Remove orphaned PHPDoc block on GlobalsRouter\n- Simplify no-op ternary in AuthorizeController::approve()\n- Separate OAuthException::$httpStatus from RuntimeException::$code",
          "is_bot": false,
          "headline": "fix: address all 7 third-pass review findings",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T21:45:15Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a99232557b1b5aef87e6fae2c7b86892af0de06e",
          "body": "Make it explicit that binary files must use encoding=base64, clarify\nthe difference between create and upload actions, and explain when to\nuse content vs file_path based on client type (remote vs CLI).",
          "is_bot": false,
          "headline": "docs(assets): improve schema descriptions for upload and content params",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T21:21:23Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "76f469b1556f78a1e48ddeed5bcfb9ea6d45e8c6",
          "body": "Remote MCP clients like ChatGPT cannot access the server filesystem,\nso file_path-based uploads fail. The upload action now accepts content\nwith encoding (base64/raw) as an alternative to file_path.\n\n- Upload action supports: file_path (local) OR content+encoding+filename (remote)\n- Add filename, content, file_path parameters to asset schema\n- Same security checks as create action (size limit, path traversal, temp file cleanup)\n- Update action description to document both upload methods",
          "is_bot": false,
          "headline": "feat(assets): support base64 content upload for remote MCP clients",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T21:17:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c286b58616fbd07d0c4e75f7a8e7b1965e8fec44",
          "body": "High:\n- Fix rate limit decay: multiply by 60 (config is minutes, hit() expects seconds)\n\nMedium:\n- Remove dead OAuthConsent.vue and its addon.js registration\n- Remove dead web.middleware config key\n- Use app()->make() instead of reflection in DiscoveryTool\n\nLow:\n- Remove unused McpToken model method\n[…]\nion in ContentFacadeRouter::crossReference\n- Remove redundant expiry check from RequireMcpPermission\n- Standardize environment check to app()->environment()\n- Remove deprecated ToolLogger::toolStarted",
          "is_bot": false,
          "headline": "fix: address all 12 second-pass review findings",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T21:10:39Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "18114255051ac07df1137d95cea5487bdb657779",
          "body": "…rays\n\nStatamic v6 uses standard Tailwind gray scale for dark mode, not custom\ndark-* utility classes. Systematically replaced all dark:bg-dark-*,\ndark:text-dark-*, dark:border-dark-*, dark:hover:bg-dark-* classes\nacross both McpPage.vue and McpAdminPage.vue with their standard\nTailwind gray equivalents.",
          "is_bot": false,
          "headline": "fix(ui): replace all custom dark-* utilities with standard Tailwind g…",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T20:25:13Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cf8ca2394381f25010f23dd8da4ba49ec4b72096",
          "body": "Use Statamic's dark mode color tokens (gray-850, gray-900) instead of\ncustom dark-* utilities that don't exist in Statamic's Tailwind config.",
          "is_bot": false,
          "headline": "fix(ui): correct dark mode colors for permissions cards",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T20:03:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bf9c4f73eb80f1c80ec1ef3d6759f93e5ca14235",
          "body": "Replace flat checkbox list with grouped cards — each domain gets its\nown card with a \"Check All\" toggle and descriptions per permission.\nMatches Statamic's native role permissions UI pattern.\n\nAlso:\n- Add description() method to TokenScope enum\n- Include descriptions in scope serialization\n- Prefill expiration with max_token_lifetime_days default\n- Show \"Never expire\" button to clear the date\n- Communicate default expiration in field description",
          "is_bot": false,
          "headline": "feat(ui): redesign permissions to match Statamic role permissions style",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T14:48:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c325dad4d7e6c0ddb0499719e4e4c2e808a9796e",
          "body": "Add revokeRefreshToken() to OAuthDriver contract and both drivers.\nRevocationController now tries refresh token revocation when access\ntoken lookup fails (RFC 7009 compliance).\n\nUpdate all documentation to match current codebase:\n- Statamic constraint ^6.6, symfony/yaml ^7.0 || ^8.0\n- 21 scopes (was\n[…]\ns architecture documented\n- Git automation events documented\n- Tool env toggles documented\n- Fix tool names, config references, and broken links\n- Remove references to non-existent auth config section",
          "is_bot": false,
          "headline": "feat(oauth): add refresh token revocation + update all documentation",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T14:30:59Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "43fcda43752cb93a7352c933ac682de3c0e04e2c",
          "body": "Register McpTokenSaved and McpTokenDeleted events with Statamic's Git\nautomation listener. When git automation is enabled, token changes will\ntrigger automatic commits with descriptive messages.\n\nEvents are dispatched from TokenService for all token operations:\ncreate, update, regenerate, and revoke.",
          "is_bot": false,
          "headline": "feat: dispatch git events for token create, update, and delete",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T12:58:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "97761855830336d79385878f5b6ac777f5aa148d",
          "body": "Critical:\n- Fix rate limit config key mismatch (security.rate_limit_max → rate_limit.max_attempts)\n- Fix expose_versions default to false (was leaking version info)\n- Add missing OAuth storage paths to published config\n\nHigh:\n- Extract validateRedirectUri into shared ValidatesRedirectUris trait\n- Ex\n[…]\nh traversal check on asset filenames\n- Simplify ContentFacadeRouter to route via action directly\n- Clarify StatsService::getToolCount documentation\n- Fix misleading hash_equals comment in TokenService",
          "is_bot": false,
          "headline": "fix: address all 17 code review findings",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T12:51:26Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "43a4b93d8c9d7ace671e7d165e02d20ce2996f0b",
          "body": "- Add STATAMIC_MCP_TOOL_{NAME}_ENABLED env vars for all 9 tool domains\n- Remove deprecated security.audit_channel and security.audit_path\n- Remove unused ToolLogger::getLogPath() method and its test\n- Remove empty auth config section\n- Remove duplicate \"Tool Configuration\" comment block",
          "is_bot": false,
          "headline": "chore(config): add env toggles to tools, remove deprecated audit keys",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T12:34:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2aa895d68352bcb0d349ae31896970b306eead9d",
          "body": "- CP login test: check we left /auth/ instead of expecting /cp/ URL\n  (Statamic may redirect to site root after login)\n- Claude Desktop guide: use .first() for \"Connectors\" text that\n  appears in multiple elements (strict mode violation)",
          "is_bot": false,
          "headline": "fix(ci): fix remaining Playwright test assertions",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T12:22:59Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c02a6ca65611c4934aa10f851de7d138c0318849",
          "body": "Use named input selectors instead of positional locators, wait for full\nnavigation away from /auth/ paths, and wait for networkidle to ensure\nthe session cookie is set before subsequent navigation.",
          "is_bot": false,
          "headline": "fix(ci): improve Playwright login helper for reliable session handling",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T12:19:52Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "879bf377a6478330680365cc1ccb37a49cbfee59",
          "body": "The addon's IIFE script could execute before Statamic's CP bundle\nfinished initializing, causing \"Cannot read properties of undefined\n(reading 'register')\" on Statamic.$inertia.\n\nWrap registration in Statamic.booting() callback which runs after\nStatamic is fully initialized.",
          "is_bot": false,
          "headline": "fix(frontend): defer Inertia page registration to Statamic.booting()",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T12:15:04Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1fb5680cdbe1fdfb58f1cf38fb363f33262cadd4",
          "body": "The cp -r command silently failed because the target directory didn't\nexist. Use vendor:publish instead, which creates the directory and\ncopies assets correctly.",
          "is_bot": false,
          "headline": "fix(ci): publish addon assets properly in browser tests",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T11:52:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "260c7cf84563a32ac27e74a8081c37fad3bf4b97",
          "body": "Playwright couldn't find playwright.config.js when running from the\naddon root directory. Explicitly pass --config to point at the correct\nconfig file in tests/Browser/.",
          "is_bot": false,
          "headline": "fix(ci): specify Playwright config path in browser tests workflow",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T11:48:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4916ad27ce963af6bb15e855e63ad0f5d3b9c23a",
          "body": "…MySQL\n\nMySQL tests failed with \"Table already exists\" because tests manually\nran migrations without cleanup between runs. SQLite (in-memory) was\nunaffected since each test gets a fresh database.\n\n- Prefix token migrations with 0001/0002/0003 for correct alphabetical order\n- Replace manual include+u\n[…]\nonsFrom()\n- Add RefreshDatabase trait to all 11 database test files\n- Fix browser-tests workflow: create user via YAML file instead of\n  statamic:make:user --email (which doesn't exist in Statamic v6)",
          "is_bot": false,
          "headline": "fix(tests): add RefreshDatabase trait and fix migration ordering for …",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T11:44:27Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "90b64cf30ebc91e0a94d00fcf2ac53742c52eacb",
          "body": "CI creates a fresh Statamic site that pulls symfony/yaml v8.\nOur ^7.0 constraint blocked the install.",
          "is_bot": false,
          "headline": "chore(deps): widen symfony/yaml constraint to include v8",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T11:23:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cb20ffd12f368ebeb17898af87452bd1d747fcdd",
          "body": "Add oauth_client_id and oauth_client_name fields to tokens created via\nOAuth flow. This enables the dashboard to show which integration created\na token and prevent regeneration (which would break the integration).\n\n- Add nullable oauth_client_id/oauth_client_name to McpTokenData DTO\n- Add database m\n[…]\nController through TokenService\n- Show green \"OAuth · ClientName\" badge in dashboard\n- Hide regenerate button for OAuth tokens (frontend + backend 403)\n- Scope editing remains available for all tokens",
          "is_bot": false,
          "headline": "feat(oauth): store client metadata on tokens, hide regenerate for OAuth",
          "author_name": "Sylvester Damgaard",
          "author_login": "sylvesterdamgaard",
          "committed_at": "2026-03-17T11:21:51Z",
          "body_truncated": true,
          "is_coding_agent": false
        }
      ],
      "releases_count": 31,
      "commits_last_year": 150,
      "latest_release_at": "2026-07-05T10:36:38Z",
      "latest_release_tag": "v2.7.0",
      "releases_from_tags": false,
      "days_since_last_push": 0,
      "active_weeks_last_year": 17,
      "days_since_latest_release": 24,
      "mean_days_between_releases": 9.1
    },
    "community": {
      "has_readme": true,
      "has_license": false,
      "has_description": true,
      "has_contributing": false,
      "health_percentage": 50,
      "has_issue_template": false,
      "has_code_of_conduct": false,
      "has_pull_request_template": true
    },
    "ecosystem": {
      "packages": [
        {
          "name": "cboxdk/statamic-mcp",
          "exists": true,
          "license": "MIT",
          "keywords": [
            "tools",
            "development",
            "laravel",
            "cursor",
            "ai",
            "statamic",
            "mcp",
            "claude"
          ],
          "ecosystem": "packagist",
          "matches_repo": true,
          "registry_url": "https://packagist.org/packages/cboxdk/statamic-mcp",
          "is_deprecated": false,
          "latest_version": "v2.7.0",
          "repository_url": "https://github.com/cboxdk/statamic-mcp",
          "versions_count": 31,
          "total_downloads": 19659,
          "dependents_count": 0,
          "deprecation_note": null,
          "maintainers_count": null,
          "monthly_downloads": 5322,
          "first_published_at": null,
          "latest_published_at": "2026-07-05T10:36:05Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 24
        }
      ]
    },
    "popularity": {
      "forks": 10,
      "stars": 32,
      "watchers": 0,
      "fork_history": {
        "days": [
          {
            "date": "2025-11-01",
            "count": 1
          },
          {
            "date": "2025-12-23",
            "count": 1
          },
          {
            "date": "2025-12-28",
            "count": 1
          },
          {
            "date": "2026-01-13",
            "count": 1
          },
          {
            "date": "2026-01-22",
            "count": 1
          },
          {
            "date": "2026-02-10",
            "count": 1
          },
          {
            "date": "2026-03-05",
            "count": 1
          },
          {
            "date": "2026-04-28",
            "count": 1
          },
          {
            "date": "2026-05-11",
            "count": 1
          },
          {
            "date": "2026-05-13",
            "count": 1
          }
        ],
        "complete": true,
        "collected": 10,
        "total_forks": 10
      },
      "star_history": null,
      "open_issues_and_prs": 1
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [],
      "has_llms_txt": false,
      "has_dockerfile": false,
      "has_mcp_signal": true,
      "bootstrap_files": [],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [],
      "toolchain_manifests": [],
      "largest_source_bytes": 50386,
      "source_files_sampled": 211,
      "oversized_source_files": 0,
      "agent_instruction_files": [
        "CLAUDE.md"
      ],
      "agent_instruction_max_bytes": 39819
    },
    "dependencies": {
      "manifests": [
        "composer.json",
        "package.json"
      ],
      "advisories": {
        "error": null,
        "scope": null,
        "source": null,
        "findings": [],
        "collected": false,
        "malicious": [],
        "truncated": false,
        "by_severity": {},
        "advisory_count": 0,
        "affected_count": 0,
        "assessed_count": 0,
        "malicious_count": 0,
        "assessed_package": null,
        "unassessed_count": 0,
        "direct_affected_count": 0
      },
      "ecosystems": [
        "npm",
        "packagist"
      ],
      "dependencies": [
        {
          "name": "statamic/cms",
          "manifest": "composer.json",
          "ecosystem": "packagist",
          "version_constraint": "^6.6"
        },
        {
          "name": "laravel/mcp",
          "manifest": "composer.json",
          "ecosystem": "packagist",
          "version_constraint": "^0.6 || ^0.7 || ^0.8"
        },
        {
          "name": "symfony/yaml",
          "manifest": "composer.json",
          "ecosystem": "packagist",
          "version_constraint": "^7.0 || ^8.0"
        }
      ],
      "all_dependencies": {
        "error": "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository",
        "source": null,
        "packages": [],
        "collected": false,
        "truncated": false,
        "total_count": null,
        "direct_count": null,
        "indirect_count": null
      }
    },
    "maintainership": {
      "issues": {
        "open_prs": 1,
        "merged_prs": 25,
        "open_issues": 0,
        "closed_ratio": 1,
        "closed_issues": 9,
        "closed_unmerged_prs": 1
      },
      "bus_factor": 1,
      "bot_contributors": 0,
      "top_contributors": [
        {
          "type": "User",
          "login": "sylvesterdamgaard",
          "commits": 144,
          "avatar_url": "https://avatars.githubusercontent.com/u/2431914?v=4"
        },
        {
          "type": "User",
          "login": "actions-user",
          "commits": 4,
          "avatar_url": "https://avatars.githubusercontent.com/u/65916846?v=4"
        },
        {
          "type": "User",
          "login": "SAY-5",
          "commits": 1,
          "avatar_url": "https://avatars.githubusercontent.com/u/240962040?v=4"
        },
        {
          "type": "User",
          "login": "ruttydm",
          "commits": 1,
          "avatar_url": "https://avatars.githubusercontent.com/u/5909558?v=4"
        }
      ],
      "contributors_sampled": 4,
      "top_contributor_share": 0.96
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "browser-tests.yml",
        "release.yml",
        "tests.yml"
      ],
      "has_docs_dir": true,
      "linter_configs": [
        "phpstan.neon"
      ],
      "has_editorconfig": false,
      "has_linter_config": true,
      "has_precommit_config": false
    },
    "security_signals": {
      "lockfiles": [],
      "scorecard": {
        "checks": [
          {
            "name": "Binary-Artifacts",
            "score": 10,
            "reason": "no binaries found in the repo",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
          },
          {
            "name": "Branch-Protection",
            "score": 0,
            "reason": "branch protection not enabled on development/release branches",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
          },
          {
            "name": "CI-Tests",
            "score": 7,
            "reason": "7 out of 9 merged PRs checked by a CI test -- score normalized to 7",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
          },
          {
            "name": "CII-Best-Practices",
            "score": 0,
            "reason": "no effort to earn an OpenSSF best practices badge detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
          },
          {
            "name": "Code-Review",
            "score": 0,
            "reason": "Found 1/18 approved changesets -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
          },
          {
            "name": "Contributors",
            "score": 6,
            "reason": "project has 2 contributing companies or organizations -- score normalized to 6",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#contributors"
          },
          {
            "name": "Dangerous-Workflow",
            "score": 10,
            "reason": "no dangerous workflow patterns detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dangerous-workflow"
          },
          {
            "name": "Dependency-Update-Tool",
            "score": 0,
            "reason": "no update tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
          },
          {
            "name": "Fuzzing",
            "score": 0,
            "reason": "project is not fuzzed",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
          },
          {
            "name": "License",
            "score": 0,
            "reason": "license file not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
          },
          {
            "name": "Maintained",
            "score": 8,
            "reason": "10 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 8",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
          },
          {
            "name": "Packaging",
            "score": null,
            "reason": "packaging workflow not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
          },
          {
            "name": "Pinned-Dependencies",
            "score": 0,
            "reason": "dependency not pinned by hash detected -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
          },
          {
            "name": "SAST",
            "score": 0,
            "reason": "SAST tool is not run on all commits -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
          },
          {
            "name": "Security-Policy",
            "score": 0,
            "reason": "security policy file not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
          },
          {
            "name": "Signed-Releases",
            "score": null,
            "reason": "no releases found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
          },
          {
            "name": "Token-Permissions",
            "score": 0,
            "reason": "detected GitHub workflow tokens with excessive permissions",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
          },
          {
            "name": "Vulnerabilities",
            "score": 10,
            "reason": "0 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "ec433cc971b7ba318c389da68cc4539fb01ac517",
        "ran_at": "2026-07-29T20:11:41Z",
        "aggregate_score": 3.7,
        "scorecard_version": "v5.5.0"
      },
      "has_codeql_workflow": false,
      "has_security_policy": false,
      "has_dependabot_config": false
    },
    "contribution_flow": {
      "collected": true,
      "ci_last_run_at": "2026-07-05T10:38:57Z",
      "oldest_open_prs": [
        {
          "number": 36,
          "created_at": "2026-07-29T20:09:28Z",
          "last_comment_at": null,
          "last_comment_author": null
        }
      ],
      "last_merged_pr_at": "2026-06-30T11:15:16Z",
      "ci_last_conclusion": "FAILURE",
      "oldest_open_issues": []
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/cboxdk/statamic-mcp",
    "host": "github.com",
    "name": "statamic-mcp",
    "owner": "cboxdk"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "good",
      "name": "Overall health",
      "note": "The weighted overall 63 is calibrated to 71 on the published index scale (record calibration 2026-08-02).",
      "notes": [
        {
          "code": "overall_calibration",
          "params": {
            "raw": 63,
            "calibrated": 71,
            "calibration": "2026-08-02"
          }
        }
      ],
      "value": 71,
      "inputs": {
        "security": 37,
        "vitality": 84,
        "community": 36,
        "governance": 62,
        "calibration": "2026-08-02",
        "engineering": 87,
        "ai_readiness": 54,
        "weighted_overall_raw": 63
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "excellent",
        "name": "Vitality",
        "value": 84,
        "weight": 0.21,
        "metrics": [
          {
            "key": "development_activity",
            "band": "good",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 74,
            "inputs": {
              "commits_last_year": 150,
              "human_commit_share": 1,
              "days_since_last_push": 0,
              "active_weeks_last_year": 17
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 0 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 0
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "17/52 weeks with commits",
                "points": 11.8,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 17
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "150 commits in the last year",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 150
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "10 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 8",
                "points": 8,
                "status": "partial",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "release_discipline",
            "band": "exceptional",
            "name": "Release discipline",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "releases_count": 31,
              "latest_release_tag": "v2.7.0",
              "releases_from_tags": false,
              "days_since_latest_release": 24,
              "mean_days_between_releases": 9.1
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "31 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 31
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 24 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 24
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~9.1 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 9.1
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "openssf_scorecard_signed_releases",
                "name": "OpenSSF Scorecard: Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 10
              }
            ]
          },
          {
            "key": "abandonment",
            "band": "exceptional",
            "name": "Abandonment",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "cap": null,
              "state": "maintained",
              "guards": [],
              "signals": [],
              "red_flag": false,
              "multiplier_pct": 100,
              "declared_reason": null,
              "unverified_reason": null,
              "unanswered_open_prs": null,
              "unanswered_open_issues": null,
              "days_since_last_merged_pr": null,
              "days_since_last_human_commit": 30,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "last human commit 30 days ago",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_maintained",
                    "params": {
                      "days": 30
                    }
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Is the project alive — is code being written and are releases shipping?"
      },
      {
        "key": "community",
        "band": "weak",
        "name": "Community & Adoption",
        "value": 36,
        "weight": 0.17,
        "metrics": [
          {
            "key": "popularity",
            "band": "at_risk",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 32,
            "inputs": {
              "forks": 10,
              "stars": 32,
              "watchers": 0,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "32 stars",
                "points": 24.2,
                "status": "partial",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 32
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "10 forks",
                "points": 8,
                "status": "partial",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 10
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "0 watchers",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "at_risk",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 32,
            "inputs": {
              "has_readme": true,
              "has_license": false,
              "readme_badges": null,
              "has_contributing": false,
              "has_issue_template": false,
              "has_code_of_conduct": false,
              "readme_badge_services": [],
              "has_pull_request_template": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 22.5,
                "status": "met",
                "details": [],
                "max_points": 22.5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "no license file detected",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "license_absent",
                    "params": {}
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributing_guide",
                "name": "CONTRIBUTING guide",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 18
              },
              {
                "key": "code_of_conduct",
                "name": "Code of conduct",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 13.5
              },
              {
                "key": "issue_template",
                "name": "Issue template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.2
              },
              {
                "key": "pr_template",
                "name": "PR template",
                "detail": null,
                "points": 6.3,
                "status": "met",
                "details": [],
                "max_points": 6.3
              }
            ]
          },
          {
            "key": "ecosystem_adoption",
            "band": "moderate",
            "name": "Ecosystem adoption (downloads)",
            "note": null,
            "notes": [],
            "value": 50,
            "inputs": {
              "packages": [
                "cboxdk/statamic-mcp"
              ],
              "dependents": 0,
              "ecosystems": "packagist",
              "total_downloads": 19659,
              "monthly_downloads": 5322
            },
            "components": [
              {
                "key": "monthly_downloads",
                "name": "Monthly downloads",
                "detail": "5,322 downloads/month across packagist",
                "points": 49.7,
                "status": "partial",
                "details": [
                  {
                    "code": "downloads_monthly",
                    "params": {
                      "count": 5322,
                      "ecosystems": "packagist"
                    }
                  }
                ],
                "max_points": 80
              },
              {
                "key": "registry_dependents",
                "name": "Registry dependents",
                "detail": "0 packages depend on it",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "registry_dependents",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "moderate",
        "name": "Sustainability & Governance",
        "value": 62,
        "weight": 0.23,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "at_risk",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 21,
            "inputs": {
              "bus_factor": 1,
              "contributors_sampled": 4,
              "top_contributor_share": 0.96
            },
            "components": [
              {
                "key": "bus_factor",
                "name": "Bus factor",
                "detail": "1 contributor(s) cover half of all commits",
                "points": 9,
                "status": "partial",
                "details": [
                  {
                    "code": "bus_factor",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 54
              },
              {
                "key": "commit_distribution",
                "name": "Commit distribution",
                "detail": "top contributor authored 96% of commits",
                "points": 0.9,
                "status": "partial",
                "details": [
                  {
                    "code": "top_contributor_share",
                    "params": {
                      "share": 96
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributor_breadth",
                "name": "Contributor breadth",
                "detail": "4 contributors",
                "points": 5.4,
                "status": "partial",
                "details": [
                  {
                    "code": "contributors_sampled",
                    "params": {
                      "count": 4
                    }
                  }
                ],
                "max_points": 13.5
              },
              {
                "key": "openssf_scorecard_contributors",
                "name": "OpenSSF Scorecard: Contributors",
                "detail": "project has 2 contributing companies or organizations -- score normalized to 6",
                "points": 6,
                "status": "partial",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "excellent",
            "name": "Issue & PR responsiveness",
            "note": "Excluded from scoring (no data or not applicable): Newcomer PR acceptance. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "newcomer_pr_acceptance"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 81,
            "inputs": {
              "merged_prs": 25,
              "open_issues": 0,
              "closed_issues": 9,
              "prs_merged_7d": null,
              "prs_decided_7d": null,
              "prs_merged_30d": null,
              "prs_decided_30d": null,
              "issue_closed_ratio": 1,
              "closed_unmerged_prs": 1,
              "first_time_authors_30d": null,
              "first_time_prs_merged_30d": null,
              "first_time_prs_decided_30d": null
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "100% of issues closed",
                "points": 42,
                "status": "met",
                "details": [
                  {
                    "code": "issues_closed_share",
                    "params": {
                      "share": 100
                    }
                  }
                ],
                "max_points": 42
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "25/26 decided PRs merged",
                "points": 28.8,
                "status": "partial",
                "details": [
                  {
                    "code": "decided_prs_merged",
                    "params": {
                      "merged": 25,
                      "decided": 26
                    }
                  }
                ],
                "max_points": 30
              },
              {
                "key": "newcomer_pr_acceptance",
                "name": "Newcomer PR acceptance",
                "detail": "no first-time contributor's PR decided in 30d",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_newcomer_prs",
                    "params": {
                      "days": 30
                    }
                  }
                ],
                "max_points": 13
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 1/18 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "moderate",
            "name": "Ownership & stewardship",
            "note": null,
            "notes": [],
            "value": 61,
            "inputs": {
              "followers": 6,
              "owner_type": "Organization",
              "is_verified": null,
              "owner_login": "cboxdk",
              "public_repos": 66,
              "account_age_days": 3697
            },
            "components": [
              {
                "key": "ownership_backing",
                "name": "Ownership backing",
                "detail": "organization-owned",
                "points": 30,
                "status": "met",
                "details": [
                  {
                    "code": "owner_organization",
                    "params": {}
                  }
                ],
                "max_points": 30
              },
              {
                "key": "verified_domain",
                "name": "Verified domain",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 20
              },
              {
                "key": "owner_reach",
                "name": "Owner reach",
                "detail": "6 followers of cboxdk",
                "points": 6.1,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 6,
                      "login": "cboxdk"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "66 public repos, account ~10 yr old",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 66
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 10
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "exceptional",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "packages": [
                "cboxdk/statamic-mcp"
              ],
              "ecosystems": "packagist",
              "any_deprecated": false,
              "min_days_since_publish": 24
            },
            "components": [
              {
                "key": "published_resolvable",
                "name": "Published & resolvable",
                "detail": "1 package(s) on packagist",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "packages_published",
                    "params": {
                      "count": 1,
                      "ecosystems": "packagist"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "publish_recency",
                "name": "Publish recency",
                "detail": "latest publish 24 days ago",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 24
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "31 published versions",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 31
                    }
                  }
                ],
                "max_points": 20
              },
              {
                "key": "not_deprecated",
                "name": "Not deprecated",
                "detail": "active, not deprecated or yanked",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "package_not_deprecated",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
      },
      {
        "key": "engineering",
        "band": "excellent",
        "name": "Engineering Quality",
        "value": 87,
        "weight": 0.19,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "good",
            "name": "Engineering practices",
            "note": null,
            "notes": [],
            "value": 78,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": false,
              "has_linter_config": true,
              "has_precommit_config": false
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "3 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 3
                    }
                  }
                ],
                "max_points": 24
              },
              {
                "key": "tests_present",
                "name": "Tests present",
                "detail": null,
                "points": 24,
                "status": "met",
                "details": [],
                "max_points": 24
              },
              {
                "key": "linter_config",
                "name": "Linter config",
                "detail": "phpstan.neon",
                "points": 16,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "phpstan.neon"
                    }
                  }
                ],
                "max_points": 16
              },
              {
                "key": "pre_commit_hooks",
                "name": "Pre-commit hooks",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 9.6
              },
              {
                "key": "editorconfig",
                "name": ".editorconfig",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.4
              },
              {
                "key": "openssf_scorecard_ci_tests",
                "name": "OpenSSF Scorecard: CI-Tests",
                "detail": "7 out of 9 merged PRs checked by a CI test -- score normalized to 7",
                "points": 14,
                "status": "partial",
                "details": [],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "exceptional",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "topics": [
                "ai",
                "claude",
                "development",
                "mcp",
                "statamic",
                "cursor",
                "laravel",
                "tools"
              ],
              "has_wiki": true,
              "homepage": "https://cbox.dk/packages/statamic-mcp",
              "has_readme": true,
              "has_docs_dir": true,
              "has_description": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 30,
                "status": "met",
                "details": [],
                "max_points": 30
              },
              {
                "key": "documentation_directory",
                "name": "Documentation directory",
                "detail": null,
                "points": 25,
                "status": "met",
                "details": [],
                "max_points": 25
              },
              {
                "key": "documentation_homepage_site",
                "name": "Documentation / homepage site",
                "detail": "https://cbox.dk/packages/statamic-mcp",
                "points": 15,
                "status": "met",
                "details": [],
                "max_points": 15
              },
              {
                "key": "repository_description",
                "name": "Repository description",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "topics",
                "name": "Topics",
                "detail": "8 topics",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "topics_count",
                    "params": {
                      "count": 8
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "wiki",
                "name": "Wiki",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "weak",
        "name": "Security",
        "value": 37,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "weak",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): Packaging, Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "packaging",
                    "signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 37,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 16,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 2,
              "scorecard_aggregate": 3.7
            },
            "components": [
              {
                "key": "binary_artifacts",
                "name": "Binary-Artifacts",
                "detail": "no binaries found in the repo",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "branch_protection",
                "name": "Branch-Protection",
                "detail": "branch protection not enabled on development/release branches",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "ci_tests",
                "name": "CI-Tests",
                "detail": "7 out of 9 merged PRs checked by a CI test -- score normalized to 7",
                "points": 1.8,
                "status": "partial",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "cii_best_practices",
                "name": "CII-Best-Practices",
                "detail": "no effort to earn an OpenSSF best practices badge detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "code_review",
                "name": "Code-Review",
                "detail": "Found 1/18 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "contributors",
                "name": "Contributors",
                "detail": "project has 2 contributing companies or organizations -- score normalized to 6",
                "points": 1.5,
                "status": "partial",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "dangerous_workflow",
                "name": "Dangerous-Workflow",
                "detail": "no dangerous workflow patterns detected",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "dependency_update_tool",
                "name": "Dependency-Update-Tool",
                "detail": "no update tool detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "fuzzing",
                "name": "Fuzzing",
                "detail": "project is not fuzzed",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "license file not detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "maintained",
                "name": "Maintained",
                "detail": "10 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 8",
                "points": 6,
                "status": "partial",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "packaging",
                "name": "Packaging",
                "detail": "packaging workflow not detected",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 5
              },
              {
                "key": "pinned_dependencies",
                "name": "Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "sast",
                "name": "SAST",
                "detail": "SAST tool is not run on all commits -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "security_policy",
                "name": "Security-Policy",
                "detail": "security policy file not detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "signed_releases",
                "name": "Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 7.5
              },
              {
                "key": "token_permissions",
                "name": "Token-Permissions",
                "detail": "detected GitHub workflow tokens with excessive permissions",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "vulnerabilities",
                "name": "Vulnerabilities",
                "detail": "0 existing vulnerabilities detected",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              }
            ]
          },
          {
            "key": "high_risk_jurisdiction_exposure",
            "band": "exceptional",
            "name": "High-Risk Jurisdiction Exposure",
            "note": "Only high-confidence self-published location evidence affects this multiplier. Ambiguous matches are review-only; country evidence is not proof of nationality, citizenship, legal registration, malicious intent, or sanctions status.",
            "notes": [
              {
                "code": "jurisdiction_evidence_limits",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "meaning": "self-published location evidence; not nationality or citizenship",
              "red_flag": false,
              "exposures": [],
              "policy_countries": [
                "Russia",
                "Iran",
                "North Korea"
              ],
              "commit_weight_rule": {
                "min_commits": 50,
                "min_commit_share": 0.1
              },
              "review_only_matches": 0,
              "below_threshold_exposures": [],
              "assessed_self_published_locations": 5
            },
            "components": [
              {
                "key": "policy_exposure_multiplier",
                "name": "Policy exposure multiplier",
                "detail": "no confirmed policy-scope location match",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "jurisdiction_no_match",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
      },
      {
        "key": "ai_readiness",
        "band": "moderate",
        "name": "AI Readiness",
        "value": 54,
        "weight": 0.04,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "excellent",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 85,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 0.94,
              "agent_instruction_files": [
                "CLAUDE.md"
              ],
              "agent_instruction_max_bytes": 39819
            },
            "components": [
              {
                "key": "agent_instructions",
                "name": "Agent instructions",
                "detail": "CLAUDE.md",
                "points": 45,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "CLAUDE.md"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "machine_readable_docs_llms_txt",
                "name": "Machine-readable docs (llms.txt)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "legible_commit_history",
                "name": "Legible commit history",
                "detail": "94 of 100 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 94,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "weak",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 43,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [],
              "has_dockerfile": false,
              "typed_language": false,
              "bootstrap_files": [],
              "has_devcontainer": false,
              "has_linter_config": true,
              "typecheck_configs": [],
              "agent_commit_share": 0.13,
              "toolchain_manifests": [],
              "dependency_bot_commit_share": 0
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 18
              },
              {
                "key": "automated_tests",
                "name": "Automated tests",
                "detail": null,
                "points": 22,
                "status": "met",
                "details": [],
                "max_points": 22
              },
              {
                "key": "lint_format_config",
                "name": "Lint / format config",
                "detail": "phpstan.neon",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "phpstan.neon"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 11
              },
              {
                "key": "reproducible_environment",
                "name": "Reproducible environment",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              },
              {
                "key": "demonstrated_agent_practice",
                "name": "Demonstrated agent practice",
                "detail": "13 of the last 100 commits agent-authored or agent-credited",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "agent_authored_commits",
                    "params": {
                      "count": 13,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "automated_maintenance",
                "name": "Automated maintenance",
                "detail": "no automated dependency updates observed",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_dependency_automation",
                    "params": {}
                  }
                ],
                "max_points": 8
              },
              {
                "key": "openssf_scorecard_pinned_dependencies",
                "name": "OpenSSF Scorecard: Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "ai_code_legibility",
            "band": "moderate",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 55,
            "inputs": {
              "primary_language": "PHP",
              "largest_source_bytes": 50386,
              "source_files_sampled": 211,
              "oversized_source_files": 0
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "PHP without a type-check config",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_typecheck_config_language",
                    "params": {
                      "language": "PHP"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "0/211 source files over 60KB",
                "points": 55,
                "status": "met",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 211,
                      "oversized": 0
                    }
                  }
                ],
                "max_points": 55
              }
            ]
          },
          {
            "key": "ai_interfaces",
            "band": "at_risk",
            "name": "Machine-readable interfaces",
            "note": null,
            "notes": [],
            "value": 20,
            "inputs": {
              "example_dirs": [],
              "has_mcp_signal": true,
              "api_schema_files": []
            },
            "components": [
              {
                "key": "api_schema_openapi_graphql_proto",
                "name": "API schema (OpenAPI/GraphQL/proto)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 40
              },
              {
                "key": "mcp_server",
                "name": "MCP server",
                "detail": null,
                "points": 20,
                "status": "met",
                "details": [],
                "max_points": 20
              },
              {
                "key": "runnable_examples",
                "name": "Runnable examples",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 40
              }
            ]
          }
        ],
        "description": "How well is the repo equipped to be developed and maintained with AI coding agents? Carries a deliberately small weight: agent tooling is a real maintenance signal, but its absence must never gate the top of the scale (calibration saturates at raw 91, so 100/100 remains reachable with AI Readiness at zero)."
      }
    ],
    "classification": {
      "top": [
        "library"
      ],
      "labels": [
        "library"
      ],
      "scores": {
        "library": 6,
        "mcp-server": 3
      },
      "primary": "library",
      "evidence": [
        {
          "tier": "distribution",
          "label": "library",
          "source": "registry:packagist",
          "weight": 6
        },
        {
          "tier": "structure",
          "label": "mcp-server",
          "source": "mcp_signal",
          "weight": 3
        }
      ],
      "artifacts": [],
      "confidence": "medium",
      "host_extension": false,
      "runs_as_process": false,
      "consumed_by_code": true
    },
    "metrics_version": "2.5.0"
  },
  "warnings": [
    "Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token",
    "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-29T20:11:55.698173Z",
  "schema_version": "0.27.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/c/cboxdk/statamic-mcp.svg",
  "full_name": "cboxdk/statamic-mcp",
  "license_state": "absent",
  "license_spdx": null
}

Scores are signals, not warranties. They reflect publicly visible practices on GitHub — not a code audit, and not a security guarantee.

Missing data is excluded and weights renormalized, never scored as zero. Methodology is versioned and open: metrics v2.5.0, schema v0.27.0 — full methodology · metrics wiki.

How one result sits in the wider record: aggregate statisticsPackagist.