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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ letsauth keeps one open dataset of what the agent protocols require and what rea
| 🕸️ | **[Agent auth atlas](https://letsauth.eu/standards/)** | The specifications, drafts and EU acts behind agent authorization as one map of what builds on, profiles, competes with and replaces what, with maturity and dated revisions per document. Export: [`data.json`](https://letsauth.eu/standards/data.json). |
| 🔗 | **[Delegation chain explorer](https://letsauth.eu/explorer/)** | Scenarios of how authority travels from a person to an agent to an API, step by step, with the decoded token at each hop and toggles for the attacks and misconfigurations that break the chain. Every step cites a specification revision. Export: [`data.json`](https://letsauth.eu/explorer/data.json). |
| 🩻 | **[Breach anatomy](https://letsauth.eu/breaches/)** | Published vulnerabilities and incidents in how agents were authorized, each written from its advisory and traced to the control that was missing, the atlas requirements that stand for it, and a replay toggle in the explorer. Export: [`data.json`](https://letsauth.eu/breaches/data.json). |
| 🔍 | **[Scanner](https://letsauth.eu/mcp/scanner/)** | A passive scanner that reads each listed server's public OAuth metadata every night, checks it against the MCP Authorization specification and opens one pull request of observations. Nothing is graded: the atlas shows each status, and every report counts them per requirement level; one report per server at `/mcp/servers/<id>/scan/` and `/mcp/servers/<id>/scan.json`. |
| 🔍 | **[Scanner](https://letsauth.eu/mcp/scanner/)** | A passive scanner that reads each listed server's public OAuth metadata every night, checks it against the MCP Authorization specification and opens one pull request of observations. Nothing is graded: the atlas shows each status, and each server page counts them per requirement level and lists the requests behind them; the raw report is at `/mcp/servers/<id>/scan.json`. |

Next on the [roadmap](docs/roadmap.md): agents under EU rules, and a changelog generated from
the data.
Expand Down
4 changes: 3 additions & 1 deletion astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ export default defineConfig({
"/atlas/standards": "/standards",
"/atlas/standards/[id]": "/standards/[id]",
"/scanner": "/mcp/scanner",
"/scanner/[id]": "/mcp/servers/[id]/scan",
"/scanner/[id]": "/mcp/servers/[id]",
// the scan report was folded into the server page
"/mcp/servers/[id]/scan": "/mcp/servers/[id]",
},
integrations: [svelte(), sitemap(), pagefind()],
});
9 changes: 4 additions & 5 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,11 @@ that has no page of its own.
```
/ home
/mcp/ MCP auth atlas: the matrix for the current revision
/mcp/servers/<id>/ one server; <id>.json is its export
/mcp/servers/<id>/scan/ the scanner's report for that server; scan.json is its export
/mcp/servers/<id>/ one server, with its newest passive scan; <id>.json and scan.json are its exports
/mcp/features/<id>/ one requirement
/mcp/revisions/<id>/ the matrix as of an older revision (the current one redirects to /mcp/)
/mcp/changes/<from>/<to>/ what changed between two revisions
/mcp/scanner/ what the scanner reads, how it reports, and every report
/mcp/scanner/ what the scanner reads, how it counts, and what it does not check
/mcp/data.json the atlas export
/standards/ Agent auth atlas: the standards map
/standards/<id>/ one specification
Expand Down Expand Up @@ -91,7 +90,7 @@ scripts/ command-line entry points: validate-data, scan, scan-nightly, a
and the generators for brand and README assets
specs/ Spec Kit artifacts per feature: spec, plan, tasks, contracts
src/
components/ Astro components and the Svelte islands (matrix, standards map, explorer, scan report)
components/ Astro components and the Svelte islands (matrix, standards map, explorer, scan transcript)
layouts/ Base (header, footer, theme) and Document (long-form pages); the shared
page head and foot every page uses are in styles/global.css
lib/ pure functions over the dataset: atlas matrix, standards graph, chains, exports, citations
Expand All @@ -111,7 +110,7 @@ tests/
- **Astro 7** with strict TypeScript. Content is data-driven: the pages under `src/pages/atlas/`,
`src/pages/explorer/` and `src/pages/scanner/` are generated from the dataset at build time.
- **Svelte 5** islands only where interaction needs them: the matrix filters, the standards map
and the scan report. Every page reads fully without JavaScript, and no page ships more than
and the scan transcript. Every page reads fully without JavaScript, and no page ships more than
150 kB of compressed JavaScript.
- **One design token system** in `src/styles/tokens.css`. Colours are OKLCH pairs resolved with
`light-dark()`; `data-theme` only flips `color-scheme`. Type is Geist for headlines and
Expand Down
12 changes: 7 additions & 5 deletions docs/scanner.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,12 @@ SHOULD 4 checks unsupported 3, unknown 1
MAY 2 checks unsupported 2
```

Under the counts, each level names the checks that are not supported, linked to their rows. The
levels are never added up into one number, and nothing on the site sorts servers by a scan. The
atlas matrix shows the cells themselves and a quiet "report" link next to each server name; the
server page shows how many checks ran and when.
The levels are never added up into one number, and nothing on the site sorts servers by a scan.
There is no report page of its own: the server page is where a scan shows. Its status panel
prints the counts and the date, the checks are rows of its feature tables (marked passive scan,
with the metadata each read), and "What the scanner asked" lists the requests behind them. The
atlas matrix shows the cells themselves and a quiet "scan" link next to each server name that
opens that section. `/mcp/servers/<id>/scan/` forwards to the server page; `scan.json` stays.

`unknown` is counted as unknown, never as unsupported. A MUST the server may meet in either of
two ways counts as supported when either way is supported, and as unknown while the other way is
Expand All @@ -248,7 +250,7 @@ row. Deprecated requirements are counted under their own level. Servers whose `a

There is no on-demand scanning. The site has no endpoint that accepts a URL, the scanner pages
ship no JavaScript, and no visitor can cause a request to any server. A server owner who wants
to see their server's report asks for it to be listed; a contributor who wants fresh cells runs
to see their server's scan asks for it to be listed; a contributor who wants fresh cells runs
`pnpm scan <id> --write` and opens a pull request.

## Opting out
Expand Down
15 changes: 15 additions & 0 deletions specs/017-neutral-scan-summary/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,21 @@ row already shows every status the grade was derived from.
`open`, `notes`) and bumps `version` to 2. The nightly pull request's table and the
`pnpm scan` output use the same summary.

## Amendment: the report folded into the server page

The scan report page repeated the server page's scanner rows with the same status, date and
evidence; only the request transcript and the counts were its own. The report has no page of its
own any more:

- **FR-007**: The server page MUST show the newest passive scan: the per-level counts and the
date in its status panel (FR-001, FR-005), and "What the scanner asked", the request transcript
(M12), after its feature tables, with the either-or notes of FR-002. The checks are its feature
rows; the ones not supported are the rows that say so, so the counts do not list them again.
- **FR-008**: The summary rule (FR-003) and the full list of requirements the scanner does not
check MUST be published on the scanner page; the server page links it.
- **FR-009**: `/mcp/servers/<id>/scan/` MUST forward to the server page. `scan.json` stays, and
the matrix link next to each server name (FR-004) opens the server page's scan section.

## Success criteria

- **SC-001**: No page, JSON file or scanner output contains a grade letter or the word "grade"
Expand Down
9 changes: 9 additions & 0 deletions specs/017-neutral-scan-summary/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,12 @@
- [x] T08 Unit and e2e tests; `tests/unit/scanner-grade.test.ts` becomes `scanner-summary.test.ts`
- [x] T09 Docs: `docs/scanner.md` "The summary", README, architecture, design (M12), development, data model, maintenance; 004 spec and report contract marked superseded; constitution 1.0.1
- [ ] T10 Screenshot baselines for the matrix, report and server pages: `refresh-screenshots` label on the pull request

## Amendment: the report folded into the server page (FR-007 to FR-009)

- [x] T11 `src/components/ScanTranscript.astro` replaces `ScanReport.astro`; `src/pages/mcp/servers/[id].astro` shows the counts in the status panel and the transcript after the feature tables
- [x] T12 `src/pages/mcp/servers/[id]/scan.astro` removed; `astro.config.mjs` forwards `/mcp/servers/<id>/scan/` (and the older `/scanner/<id>/`) to the server page
- [x] T13 `src/pages/mcp/scanner/index.astro`: "Where the results show" points at the server page; "What it does not check" lists every requirement
- [x] T14 Matrix link opens `#scan`; e2e tests move from the report page to the server page; report baselines removed
- [x] T15 Docs: README, architecture, scanner
- [ ] T16 Screenshot baselines for the matrix, scanner and server pages: `refresh-screenshots` label on the pull request
4 changes: 2 additions & 2 deletions src/components/MatrixPage.astro
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const usedLevels = new Set(
const usedCategories = [...new Set(matrix.columns.map((f) => f.category))];
const hrefFor = (id: string) => (id === current.id ? "/mcp/" : `/mcp/revisions/${id}/`);

// the nightly scanner's view per server: its report is linked next to the name, never sorted on
// the nightly scanner's view per server: its requests are linked next to the name, never sorted on
const scans = new Map(matrix.rows.map((s) => [s.id, latestScan(data, s, revisionId)]));
const scannerChecks = passiveChecks(data, revisionId).length;

Expand Down Expand Up @@ -194,7 +194,7 @@ const classWords = (counts: { value: string; count: number }[], label: Record<st
{" and "}
<abbr title="Authorization server">AS</abbr> name the party the feature is observed on. The
<a href="/mcp/scanner/">nightly scanner</a> reads {scannerChecks} of these requirements from
public metadata; "report" next to a server's name opens what it asked and what it found.
public metadata; "scan" next to a server's name opens what it asked.
Rows are alphabetical. Nothing here is scored or ranked.
</p>
<dl class="legend__classes" aria-label="Access and auth classes">
Expand Down
8 changes: 4 additions & 4 deletions src/components/MatrixTable.astro
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface Props {
diff?: RevisionDiff;
/**
* The nightly scanner's latest view per server id. A quiet link next to the server's name
* opens the report; the row's cells already show what the scan found, so nothing is added
* opens the requests behind it on the server page; the row's cells already show what the scan found, so nothing is added
* here and nothing is a sort key.
*/
scans?: Map<string, ScanView>;
Expand Down Expand Up @@ -210,11 +210,11 @@ const shortLabel = (id: string) => id.split(".").slice(2).join(".") || id;
{scan && (
<a
class="scan-link"
href={`/mcp/servers/${server.id}/scan/`}
href={`/mcp/servers/${server.id}/#scan`}
data-scan={scan.summary.state}
title={scan.summary.label}
>
<span class="visually-hidden">{server.name} scan </span>report
<span class="visually-hidden">{server.name} </span>scan
</a>
)}
</span>
Expand Down Expand Up @@ -513,7 +513,7 @@ const shortLabel = (id: string) => id.split(".").slice(2).join(".") || id;
text-underline-offset: 0.2em;
}

/* the row's own cells are the scan's result; this only opens the report behind them */
/* the row's own cells are the scan's result; this only opens the requests behind them */
.matrix .scan-link {
flex: none;
font-size: var(--tiny);
Expand Down
Loading
Loading