GET /api
Self-describing index: conventions, every endpoint, parameters and examples. Pass `?lang=fr` for French text. HTML docs stay at /docs.
Public API
Open CORS, cached at the edge, no key. The same rules as the site: unknown is not zero, and disputed claims stay in the label.
No key · CORS open · GET only
A null `affected` means the number is unknown or unpublished. It is never 0. Do not sum it, do not plot it as none, and do not treat a missing CSV cell as an empty incident.
An incident with status "disputed" is a real breach whose headline scope is contested. Attacker-claimed figures live in `affectedLabel` text and never in the numeric field, and never in an aggregate.
Attackers are only named where the cited source supports it. Do not infer a group from the method or the sector.
This is a curated set of major publicly reported incidents, not every French breach. Cite the original `sourceUrl` on each record.
GET /api
Self-describing index: conventions, every endpoint, parameters and examples. Pass `?lang=fr` for French text. HTML docs stay at /docs.
GET /api/incidents
List and search incidents. JSON includes full localized `detail` on each row (unlike MCP `list_incidents`, which returns compact rows). Unknown counts sort last when `sort=affected`, never as zero. CSV leaves the affected cell empty. Feed and llms routes also accept `?lang=fr`.
Try this request
Examples
Set the fields, then send. This calls the live API.
GET /api/incidents/{id}
One incident with its localized `detail` (lead, timeline, how, taken, notTaken, impact, response, methodDisclosure, optional revision and quotes) plus `lastResearched`, `source` and `sources`. Each source has `kind` (primary | official | secondary), `publisher` and optional `published`. Unknown ids return 404 and list the valid ones. CSV is not offered here; list CSV stays compact and still leaves unknown `affected` empty.
Try this request
Examples
Set the fields, then send. This calls the live API.
GET /api/summary
ANSSI and CNIL national figures, plus counts derived from this list. Aggregates never include unknown or disputed attacker claims.
Try this request
Examples
Set the fields, then send. This calls the live API.
GET /api/sources
Every source, with `kind` (primary | official | secondary), `publisher`, optional `published` date, and the incidents that cite it.
GET /api/patterns
Recurring weaknesses and the priority control for each.
Try this request
Examples
Set the fields, then send. This calls the live API.
GET /api/recommendations
Guidance for organisations and the public. Public items include explainerSlug when a matching /learn guide exists.
Try this request
Set the fields, then send. This calls the live API.
The same read-only dataset, as a remote MCP server. No key. Stateless JSON over Streamable HTTP, so it runs on ordinary serverless functions and still speaks to 2025-era clients.
POST https://cyberwatch-france.timbenniks.dev/mcp
Point any MCP host at this URL. No token. After adding it, reload the host and confirm the tools appear.
Cursor — save as .cursor/mcp.json in a project, or ~/.cursor/mcp.json for every workspace. Then Cursor Settings → Tools & MCP, enable the server.
{
"mcpServers": {
"france-cyberwatch": {
"url": "https://cyberwatch-france.timbenniks.dev/mcp"
}
}
}Add to CursorClaude Desktop — merge into claude_desktop_config.json (Claude → Settings → Developer). Claude.ai custom connectors can use the same URL.
{
"mcpServers": {
"france-cyberwatch": {
"type": "http",
"url": "https://cyberwatch-france.timbenniks.dev/mcp"
}
}
}VS Code — save as .vscode/mcp.json, or add the server in MCP: Add Server.
{
"servers": {
"france-cyberwatch": {
"type": "http",
"url": "https://cyberwatch-france.timbenniks.dev/mcp"
}
}
}list_incidents
Search and filter incidents. Returns compact rows; unknown `affected` stays null, never 0. Unknown counts sort last when sort is affected.
Input — q, kind, severity, status, sector, year, from, to, sort (date|affected|org), order, limit (≤200), offset, lang (en|fr). Same filters as GET /api/incidents.
get_incident
One incident with localized detail, lastResearched, and every cited source. Unknown ids list the valid ones.
Input — id (required), lang (en|fr).
get_summary
ANSSI and CNIL national figures, plus counts from this list. Aggregates never include unknown or disputed attacker claims.
Input — lang (en|fr).
list_sources
Every source, with kind (primary | official | secondary), publisher, optional published date, and the incidents that cite it.
list_patterns
Recurring weaknesses and the priority control for each.
Input — lang (en|fr).
list_recommendations
Guidance for organisations and the public. Public items include explainerSlug when a matching /learn guide exists.
Input — audience (organizations | public), lang (en|fr).
cyberwatch://conventions
How to use the numbers: unknown is not zero, disputed scope stays disputed, no inferred attribution.
cyberwatch://incident/{id}
Full localized record for one incident, same payload as get_incident.
explain_incident
Ask the model to explain one record without inventing counts or attackers.
Input — id, lang (en|fr).
brief_the_dossier
A short briefing of the curated list, with the data rules stated first.
Input — lang (en|fr).
When a browser agent is already on a page, these tools run in the tab: they can filter the timeline, open a record, and read the dataset that was prerendered into the page. No extra server call.
WebMCP is a proposed web standard. Chrome exposes it behind a flag (and an origin trial). A WebMCP bridge or the Model Context Tool Inspector extension can also see tools registered on this page.
In Chrome 146+, open chrome://flags/#enable-webmcp-testing, set it to Enabled, and relaunch. Open this site, then inspect navigator.modelContext (or document.modelContext) in DevTools, or use the inspector extension.
chrome://flags/#enable-webmcp-testing
Model Context Tool Inspector (Chrome Web Store) lists the tools, lets you call them, and can chat against them.
If you use a WebMCP bridge (for example Cursor talking to the open tab), pin this origin and the page tools show up as web.* tools.
get_page_context
Where the reader is: path, locale, page kind, and the open incident id if any.
list_incidents
Search the dataset already loaded in this page. Unknown affected stays null.
Input — q, kind, severity, status, sector, year, limit, lang.
get_incident
One record from the in-page dataset, in the current (or requested) language.
Input — id (required), lang.
get_summary
National ANSSI/CNIL figures and curated counts from the in-page dataset.
open_incident
Navigate this tab to the incident article.
Input — id (required).
open_page
Navigate this tab to a site section (home, incidents, guidance, numbers, docs, learn, quiz).
Input — page: home | incidents | guidance | numbers | docs | learn | quiz.
filter_timeline
Apply timeline filters and open /incidents (or /fr/incidents) with a shareable query string.
Input — q, kind, severity, status, sector, year.
reset_filters
Clear timeline filters and open the full incident list.
Raw dataset
The complete source file this site and the API are built from.
/data/france-cyberwatch-data.json