---
title: "Analytics"
manual: "WebMCP tool for TYPO3"
version: "0.4"
permalink: "https://docs.typo3.org/permalink/neoblack/webmcp:analytics@0.4"
source: "Analytics/Index.rst"
rendered: "2026-10-07T22:31:54+00:00"
---

> [!WARNING]
> **Experimental.** This extension is experimental and not yet ready for
> production use. It is built on top of
> [WebMCP](https://github.com/webmachinelearning/webmcp), which is itself
> an experimental, early-stage proposal. Both the underlying specification
> and this extension's API may change or break at any time without notice.
> Use at your own risk.

# Analytics {#analytics}

The extension ships an optional, first-party usage log: one anonymous row per
tool call, visualised in a backend module. It is enabled by default and can be
switched off entirely (see [Configuration](https://docs.typo3.org/permalink/neoblack/webmcp:configuration@0.4)).

-   [What is recorded](https://docs.typo3.org/permalink/neoblack/webmcp:what-is-recorded@0.4)
-   [Data model](https://docs.typo3.org/permalink/neoblack/webmcp:data-model@0.4)
-   [Retention and deletion](https://docs.typo3.org/permalink/neoblack/webmcp:retention-and-deletion@0.4)
-   [The ingest endpoint](https://docs.typo3.org/permalink/neoblack/webmcp:the-ingest-endpoint@0.4)
-   [Backend module](https://docs.typo3.org/permalink/neoblack/webmcp:backend-module@0.4)

## What is recorded {#what-is-recorded}

Every tool call fires a small same-origin beacon (`navigator.sendBeacon`,
with a `fetch` fallback) to the ingest endpoint. The
`\Neoblack\Webmcp\Middleware\EventMiddleware` appends exactly two values per
call:

-   the **tool name** – validated against the registered providers
    (`\Neoblack\Webmcp\Registry\ToolRegistry::toolNames()`); events for
    unknown names are not stored.
-   a **client hint** – a short, self-reported or coarsely detected label of the
    calling agent.

> [!NOTE]
> Nothing else is stored: no free text (search queries, e-mail contents,
> names), no cookies, no IP address, no user agent string.

### The client hint {#the-client-hint}

The hint is best-effort and resolved client-side in this order:

1.  the agent's optional `client` argument, if it sets one (the runtime adds a
    `client` property to every tool's input schema automatically);
1.  otherwise a coarse match against known agent markers in the User-Agent
    (e.g. `Claude`, `ChatGPT`, `GeminiBot`);
1.  otherwise the literal `unbekannt` ("unknown").

Server-side the value is sanitised (letters, digits, space and `._-/()` only)
and capped at 64 characters before it is stored.

## Data model {#data-model}

Events live in a single append-only table, created by `extension:setup`:

| Column | Type | Meaning |
| --- | --- | --- |
| `uid` | `int` | Primary key. |
| `crdate` | `int` | Unix timestamp of the call. |
| `tool` | `varchar(64)` | Registered tool name. |
| `client` | `varchar(64)` | Sanitised client hint. |

> [!TIP]
> There is no `pid`, no relation to a user or session, and no additional
> payload column — the schema itself makes it impossible to store PII.

## Retention and deletion {#retention-and-deletion}

The table is append-only; the extension does **not** prune it automatically.
Because a row is fully anonymous, there is no per-subject deletion to perform,
but you remain free to trim or clear the log at any time, for example:

**Trim or clear the usage log**

```sql
-- drop everything older than 90 days
DELETE FROM tx_neoblackwebmcp_event WHERE crdate < UNIX_TIMESTAMP() - 90*86400;

-- clear the log completely
TRUNCATE tx_neoblackwebmcp_event;
```

If you schedule this, a simple periodic `DELETE` on `crdate` is enough; the
`crdate` column is indexed.

## The ingest endpoint {#the-ingest-endpoint}

`POST /webmcp-event` is public by design (agents call it without a backend
session). It is protected by several deliberate constraints:

| Constraint | Behaviour |
| --- | --- |
| Feature-gated | When `analyticsEnabled` is off the endpoint is inert and the request is passed through untouched. |
| Same-origin guard | Requests whose `Sec-Fetch-Site` header is present and neither `same-origin` nor `same-site` are rejected with `204`. |
| Rate limited | At most `analyticsRateLimit` calls per client IP per minute (default 60; `0` disables). Excess calls receive `429`. The limiter stores only a hashed IP + time-window counter that expires after two windows — the plaintext IP is never persisted. |
| Whitelisted | Only registered tool names are logged; anything else is handed down the middleware stack. |

## Backend module {#backend-module}

When analytics is enabled, the **System > WebMCP** module shows calls per
tool and per client, a per-day timeline and the most recent events. The
timeframe (7 / 30 / 90 days) and tool/client filters are selectable; the accepted
tool names are derived automatically from the registered providers.

> [!NOTE]
> The module is not page-related: it has no page tree and shows site-wide
> figures, mirroring the event table which has no `pid`. It lives in the
> **System** menu; there is nothing to pick in a tree.

![The WebMCP backend module showing calls per tool and a timeline](../Images/BackendModule.png)

![The WebMCP backend module showing also the recent calls in a table](../Images/BackendModule2.png)

> [!NOTE]
> **See also**
>
> -   [Configuration](https://docs.typo3.org/permalink/neoblack/webmcp:configuration@0.4) – switches for `analyticsEnabled` and
>     `analyticsRateLimit`.
> -   [Architecture](https://docs.typo3.org/permalink/neoblack/webmcp:architecture@0.4) – how the ingest flow fits together.
