Architecture 

The extension has two independent flows: emitting tools at page render time and ingesting usage events at call time. They share only the tool registry.

Emitting tools (frontend render) 

Tool providerToolRegistryToolManifestProcessorPage HTMLwebmcp.jsModelContextTool providerToolRegistryToolManifestProcessorPage HTMLwebmcp.jsModelContextTool providerToolRegistryToolManifestProcessorPage HTMLwebmcp.jsModelContextmanifest($cObj, $processedData)providers returningnull are droppedlist of ManifestJSON block(JSON_HEX_* escaped)read #webmcp-configper tool: primitiveinterpreter or moduleUrlprovideContext({ tools })(else registerTool per tool)tools discoverable & callable
Emitting tools — from PHP provider to registered agent tool
Class Responsibility
\Neoblack\Webmcp\Tool\ToolProviderInterface The contract each tool implements. Tagged webmcp.tool (autoconfigured).
\Neoblack\Webmcp\Tool\Manifest / \Neoblack\Webmcp\Tool\Primitive The serialisable tool description and its behaviour selector.
\Neoblack\Webmcp\Registry\ToolRegistry Collects manifests for the current request and exposes toolNames() for the analytics whitelist.
\Neoblack\Webmcp\DataProcessing\ToolManifestProcessor Serialises the manifests into the page's JSON block.
Resources/Public/JavaScript/webmcp.js The generic runtime holding all four primitive interpreters and the escape-hatch loader.

Ingesting usage events (call time) 

Agent on pagewebmcp.jsEventMiddlewareRateLimiterToolRegistrytx_neoblackwebmcp_eventEditorDashboardControllerStatisticsServiceEventRepositoryAgent on pagewebmcp.jsEventMiddlewareRateLimiterToolRegistrytx_neoblackwebmcp_eventEditorDashboardControllerStatisticsServiceEventRepositoryAgent on pagewebmcp.jsEventMiddlewareRateLimiterToolRegistrytx_neoblackwebmcp_eventEditorDashboardControllerStatisticsServiceEventRepositorytool callPOST /webmcp-eventsendBeacon { tool, client }same-origin guard(Sec-Fetch-Site)allow(ip, limit)?tool in toolNames()?log(tool, client, ts)Backend moduleopen System > WebMCPcollect(filter)aggregate by tool / client / daySELECT
Ingesting usage events — from tool call to backend dashboard
Class Responsibility
\Neoblack\Webmcp\Middleware\EventMiddleware The public ingest endpoint. Inert when analytics is disabled; passes unknown tools down the stack so it can coexist with other handlers.
\Neoblack\Webmcp\Security\RateLimiter Fixed-window limiter keyed on a hashed IP + window number (no plaintext IP stored).
\Neoblack\Webmcp\Domain\Repository\EventRepository The only class that writes/reads the event table.
\Neoblack\Webmcp\Service\StatisticsService Aggregates rows into the DTOs the backend module renders.
\Neoblack\Webmcp\Controller\DashboardController Thin backend controller; reads the filter, delegates, renders.

Why the two flows are decoupled 

The middleware runs early, before the frontend page is resolved, so it cannot rely on a rendered manifest. It therefore validates incoming events against \Neoblack\Webmcp\Registry\ToolRegistry::toolNames() — the context-free provider names — rather than against the per-page manifest.