Connect a remediation agent to Tocsin24

  • MCP protocol, HTTP transport (streamable)
  • Claude Code 2 or later
  • Updated on 2 November 2026
  • 15 minutes

A remediation agent is told about every detection, reads the incident and its fix steps, fixes the shop with its own access, then asks Tocsin24 to check again. The incident closes when Tocsin24 sees compliance restored: never on the agent's word.

On this page

Tocsin24 gives an agent three things: a token that only sees one instance, an MCP server to read what needs fixing and report back, and signed webhooks to be told in real time. This guide connects them in order, then shows the complete loop.

1. Create a token

  1. In Tocsin24, open the instance, then Settings › Agents.
  2. In the “New token” block, give a name (“Remediation agent”), a duration (90 days at most) and tick only the permissions needed:
    • Read: incidents, hardening, vulnerabilities, probes, inventory, flagged files and pending baseline items;
    • Comment: add to an incident’s log what the agent did;
    • Take charge: show the team that an agent is handling the incident;
    • Request a check: run an incident’s check again after a fix.
  3. Click “Create the token”, then copy it straight away: it starts with t24_ and will not be shown again. Keep it in the agent’s secret manager, never in a code repository.

2. Connect Claude Code

Give the token to the agent through the TOCSIN24_TOKEN environment variable (secret manager, untracked environment file). Then save this .mcp.json file at the root of the shop’s project; the server address is shown in Settings › Agents (“MCP server address”).

Content of .mcp.json
  1. {
  2. "mcpServers": {
  3. "tocsin24": {
  4. "type": "http",
  5. "url": "https://app.tocsin24.com/api/agents/mcp",
  6. "headers": { "Authorization": "Bearer ${TOCSIN24_TOKEN}" }
  7. }
  8. }
  9. }

What each line means:

  • ${TOCSIN24_TOKEN} Replaced by Claude Code at launch, from the environment variable: the token is never written in clear text in this file (which you can commit), in ~/.claude.json or in the terminal history.
Terminal, in the shop's project
Checks the connection (Claude Code asks you to approve the project's server on first launch) claude mcp list

In Claude Code, ask for example: “List the open Tocsin24 incidents and suggest a fix for the most serious one”. The agent uses the tools below (their names are in French).

Tool Permission What it does
lister_incidents Read The instance’s incidents, filters: status, level, type, since
lire_incident Read Full incident: explanation, fix steps, affected items, files, log lines, history
evenements_depuis Read Events after a cursor (fallback without a webhook)
etat_durcissement Read Hardening checks and their step-by-step fix
failles_exposees Read Known vulnerabilities, installed version, expected patch
etat_sondes Read HTTP probes: availability, response time
inventaire Read Version, modules or plugins, applied patches
fichiers_suspects Read Code files flagged by the integrity check
referentiel Read Accounts, integrations, blocks and files still to approve
erreurs_magento Read Magento errors (system.log, exception.log) grouped by kind, and latest occurrences with their trace (Magento 2)
commenter_incident Comment Adds a comment to the incident’s log
prendre_en_charge Take charge Marks the incident as taken in charge
demander_verification Request a check Runs the incident’s check again

Another MCP agent

Any MCP client that speaks the HTTP transport works: the server address above, and an Authorization: Bearer header followed by the token (read from an environment variable) on every request. The server answers in JSON, without a session or cookie, and only knows the token’s instance.

3. Receive events in real time

  1. In Settings › Agents, under the token, enter the public HTTPS address that will receive events, then “Save”.
  2. Copy the signing secret (it starts with t24whsec_): it is shown only once.
  3. Click “Send a test”: the delivery appears in the delivery log with the HTTP code received.

Tocsin24 sends a JSON POST for every incident opened, reopened, escalated or closed, only one per related group (the findings attached to the same change do not each send a delivery). Staging is sent too: the environment field is staging.

Content of Example delivery (JSON body)
  1. {
  2. "id": 4182,
  3. "type": "incident.opened",
  4. "created_at": "2026-11-02T10:15:04+00:00",
  5. "site": { "slug": "ab12cd34ef56", "name": "Shop" },
  6. "environment": "production",
  7. "incident": {
  8. "id": 1203, "type": "exposed_file", "severity": "critical", "level": "critical", "status": "open",
  9. "title": "Fichier exposé sur le web : /.git/config",
  10. "first_detected_at": "2026-11-02T10:14:58+00:00", "last_detected_at": "2026-11-02T10:14:58+00:00",
  11. "url": "https://app.tocsin24.com/sites/ab12cd34ef56/incidents/1203"
  12. },
  13. "untrusted_fields": ["incident.title"]
  14. }

The payload contains no sensitive detail: the agent reads the incident with lire_incident. A 2xx response counts as delivered; otherwise Tocsin24 retries after 30 s, 2 min, 10 min, 30 min then 2 h, and disables the webhook after several abandoned deliveries in a row. Too many events at once pause deliveries for a few minutes: a single storm.started event warns the agent, which then reads evenements_depuis.

4. Check the signature

Each delivery carries four headers: X-Tocsin24-Event (type), X-Tocsin24-Delivery (delivery ID, the same for its retries), X-Tocsin24-Timestamp (Unix timestamp) and X-Tocsin24-Signature, in the form sha256=<hex>: the HMAC-SHA256 of the string <timestamp>.<delivery ID>.<raw body> with the webhook’s secret. As the ID is signed, it cannot be changed to slip a duplicate through. Reject:

  • a signature that does not match (constant-time comparison);
  • a timestamp more than 5 minutes away from the current time (replay);
  • a delivery ID already processed (duplicate retry).
Content of webhook.php (PHP)
  1. <?php
  2. $secret = getenv('TOCSIN24_WEBHOOK_SECRET');
  3. $body = file_get_contents('php://input');
  4. $timestamp = $_SERVER['HTTP_X_TOCSIN24_TIMESTAMP'] ?? '';
  5. $delivery = $_SERVER['HTTP_X_TOCSIN24_DELIVERY'] ?? '';
  6. $signature = $_SERVER['HTTP_X_TOCSIN24_SIGNATURE'] ?? '';
  7. $expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $delivery . '.' . $body, $secret);
  8. if (! ctype_digit($timestamp) || ! ctype_digit($delivery) || abs(time() - (int) $timestamp) > 300 || ! hash_equals($expected, $signature)) {
  9. http_response_code(401);
  10. exit;
  11. }
  12. $event = json_decode($body, true);
  13. // Skip a delivery already processed: X-Tocsin24-Delivery is the same for every retry.
  14. http_response_code(204);
Content of webhook.mjs (Node.js)
  1. import { createHmac, timingSafeEqual } from 'node:crypto';
  2. import { createServer } from 'node:http';
  3. const secret = process.env.TOCSIN24_WEBHOOK_SECRET;
  4. createServer((req, res) => {
  5. const chunks = [];
  6. req.on('data', (c) => chunks.push(c));
  7. req.on('end', () => {
  8. const body = Buffer.concat(chunks).toString('utf8');
  9. const timestamp = req.headers['x-tocsin24-timestamp'] ?? '';
  10. const delivery = req.headers['x-tocsin24-delivery'] ?? '';
  11. const signature = Buffer.from(req.headers['x-tocsin24-signature'] ?? '');
  12. const expected = Buffer.from('sha256=' + createHmac('sha256', secret).update(`${timestamp}.${delivery}.${body}`).digest('hex'));
  13. const fresh = /^\d+$/.test(timestamp) && /^\d+$/.test(delivery) && Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
  14. if (!fresh || signature.length !== expected.length || !timingSafeEqual(signature, expected)) {
  15. res.writeHead(401).end();
  16. return;
  17. }
  18. const event = JSON.parse(body);
  19. res.writeHead(204).end();
  20. });
  21. }).listen(8080);

5. The detect, fix, verify loop

  1. Detect: the webhook receives incident.opened (or the agent reads evenements_depuis with its last cursor).
  2. Read: the agent calls lire_incident with the ID received, then prendre_en_charge so the team knows it is on it.
  3. Fix: it follows the fix steps (for example, block /.git/ in the web server configuration) with its own access to the shop, and checks for itself with the command given in the incident.
  4. Report: commenter_incident says what was changed, without ever copying a secret.
  5. Verify: demander_verification runs the check again (the hardening “Check now”, or an on-demand reading of the inventory, the code or the application state), with the same limits as a person.
  6. Close: a few minutes later, Tocsin24 sees compliance restored and sends incident.closed. If the incident stays open, the agent reads it again and starts over, or hands over to the team.

Frequently asked questions

Can the agent mark an incident "Legitimate" or "Ignored"?

No. These are human decisions: a compromised agent could otherwise silence an attack. The agent comments, takes charge and requests a check; the incident closes when Tocsin24 sees compliance restored, or by a decision of your team.

The server returns 401

The token is missing, mistyped, revoked or expired. Create a new one in Settings › Agents: a token is shown only once.

The server returns 403 "subscription_required" or "creator_access_revoked"

The instance is no longer subscribed (or is in its trial period), or the person who created the token no longer administers the instance. In the second case, a current administrator creates a new token.

The server returns 429

The token's request limit is reached: the Retry-After header gives the delay in seconds. Prefer webhooks to repeatedly polling evenements_depuis.

My webhook no longer receives anything

After several failed deliveries in a row, Tocsin24 disables it. Fix the service, then save the address again in Settings › Agents: the webhook is re-enabled. The delivery log shows the HTTP code of each attempt.

Why is my webhook address refused?

It must start with https://, without a user name or password, and its name must resolve to a public address: private, internal or local networks are refused, when saving and on every delivery.

Need help?

Write to us