WebMCP 

Extension key

neoblack_webmcp

Package name

neoblack/webmcp

Version

0.1

Language

en

Author

Frank Nägler & contributors

License

This document is published under the Creative Commons BY 4.0 license.


A declarative WebMCP tool framework for TYPO3. Define agent tools server-side as small PHP providers; the extension collects them into a per-page manifest and a generic JavaScript runtime registers each tool against document.modelContext / navigator.modelContext. Optional, privacy-preserving first-party analytics are included.


Table of contents:

Introduction 

What is WebMCP? 

WebMCP exposes page-scoped tools to AI agents through the browser's ModelContext interface (document.modelContext, with a fallback to navigator.modelContext used by Chrome's origin trial). An agent operating the page can discover these tools and call them – for example to search content, navigate to a section, or open a pre-filled contact e-mail.

What this extension does 

The extension turns tool definitions into a declarative concern:

  • A tool is a small PHP class implementing \Neoblack\Webmcp\Tool\ToolProviderInterface. It returns a Manifest describing the tool's name, JSON schema and behaviour.
  • Behaviour is expressed through one of four primitivesnavigate, search, mailto and static – whose generic interpreters live in a single JavaScript runtime (webmcp.js). No per-tool JavaScript is required.
  • A ToolManifestProcessor (a TYPO3 data processor) collects every registered provider into one JSON block per page; the runtime reads it and registers the tools.

Because tools are pure server-side configuration, other extensions and site packages can add their own without touching JavaScript. For behaviour that no primitive covers, a tool may point at its own ES module (escape hatch).

Privacy-preserving analytics 

An optional first-party ingest endpoint (/webmcp-event) logs one row per tool call – only the tool name and a coarse, self-reported client hint, never free text, cookies or IP. A backend module visualises the usage. Analytics can be disabled in the extension configuration.

Feature detection & progressive enhancement 

Everything degrades gracefully: without a ModelContext implementation, without configuration, or with a malformed manifest, nothing is registered and regular visitors are unaffected.

Installation 

Composer 

Install the extension with Composer:

composer require neoblack/webmcp
Copied!

Then set up the extension (creates the analytics table tx_neoblackwebmcp_event):

vendor/bin/typo3 extension:setup --extension neoblack_webmcp
Copied!

Requirements 

  • TYPO3 v14.3+
  • PHP 8.2+

The extension ships no site configuration of its own. It only becomes active once at least one tool provider is registered and the ToolManifestProcessor plus the webmcp.js runtime are wired into your site (see Configuration). Registering the tools themselves is described in Writing tools.

Configuration 

Extension configuration 

analyticsEnabled

analyticsEnabled
Type
boolean
Default
1

Log each WebMCP tool call (tool name + coarse client hint, no PII) to tx_neoblackwebmcp_event and expose the backend module. Turn off to disable the ingest endpoint (/webmcp-event is then passed through).

analyticsRateLimit

analyticsRateLimit
Type
integer
Default
60

Maximum accepted ingest calls per client IP per minute (0 = unlimited). Protects the public endpoint against flooding and statistics pollution; excess calls are answered with 429 Too Many Requests. The limiter uses the extension's own cache and only stores a hashed, short-lived counter — no plaintext IP.

Wiring the manifest into your site 

Three pieces connect the tools to the page. All of them live in your site package / TypoScript, so you stay in control of where the tools are exposed.

1. Emit the manifest 

Add the data processor to the page's FLUIDTEMPLATE (or PAGEVIEW). If a tool provider relies on an earlier data processor (e.g. a MenuProcessor), make sure that runs first.

page.10.dataProcessing {
    # optional: a menu a navigate tool can build on
    35 = menu
    35 {
        entryLevel = 0
        levels = 1
        as = webmcpTopics
    }
    40 = Neoblack\Webmcp\DataProcessing\ToolManifestProcessor
    40 {
        endpoint = /webmcp-event
        as = webmcpConfigJson
    }
}
Copied!

endpoint

endpoint
Type
string
Default
/webmcp-event

Analytics beacon target written into the manifest.

as

as
Type
string
Default
webmcpConfigJson

Variable the JSON manifest is assigned to.

2. Render the JSON block 

Output the manifest once per page inside a <script> tag with the id webmcp-config (the id the runtime looks for):

<f:if condition="{webmcpConfigJson}">
    <script type="application/json" id="webmcp-config"><f:format.raw>{webmcpConfigJson}</f:format.raw></script>
</f:if>
Copied!

3. Include the runtime 

page.includeJSFooter {
    webmcp = EXT:neoblack_webmcp/Resources/Public/JavaScript/webmcp.js
    webmcp.defer = 1
}
Copied!

Backend module 

When analytics is enabled, the module Web > WebMCP shows calls per tool and client, a per-day timeline and the most recent events. The accepted tool names for the ingest endpoint are derived automatically from the registered providers.

Writing tools 

A tool is a PHP class implementing \Neoblack\Webmcp\Tool\ToolProviderInterface. Thanks to the #[AutoconfigureTag('webmcp.tool')] attribute on the interface, any autoconfigured service implementing it is picked up automatically – in this extension, a site package, or any third-party extension. No manual Services.yaml wiring is needed.

The interface 

interface ToolProviderInterface
{
    // Context-free, stable name. Used for the analytics whitelist before
    // the frontend is resolved. Must equal the Manifest name.
    public function name(): string;

    // Return the tool's Manifest, or null to omit it for this request
    // (e.g. a blog tool on a site without a blog). $processedData carries
    // the results of data processors that ran earlier in the same content
    // object, so you can build on them (e.g. a menu).
    public function manifest(ContentObjectRenderer $cObj, array $processedData): ?Manifest;
}
Copied!

The manifest 

new Manifest(
    name: 'search_articles',
    description: 'Search all articles …',
    inputSchema: [ /* JSON schema for the arguments */ ],
    primitive: Primitive::Search,
    data: [ /* primitive-specific payload, see below */ ],
    moduleUrl: null, // optional escape hatch, see below
);
Copied!

The runtime injects a client string property into every tool's input schema automatically (for the optional analytics hint), so you do not declare it yourself.

Primitives 

Every tool maps to exactly one primitive. The generic runtime interprets the data payload; you never write JavaScript.

mailto 

Build a pre-filled mailto: link and open it. No server storage.

primitive: Primitive::Mailto,
data: [
    'to' => base64_encode('me@example.org'),      // base64, kept out of source
    'subjectTemplate' => 'Request – {anliegen}',
    'bodyLines' => [                              // "Label: value" lines
        ['label' => 'Name', 'param' => 'name'],
        ['label' => 'Organisation', 'param' => 'org', 'optional' => true],
    ],
    'messageParam' => 'message',                  // free text block at the end
    'successTemplate' => 'A pre-filled e-mail to {to} has been opened.',
]
Copied!

static 

Return a curated list verbatim.

primitive: Primitive::StaticList,
data: [
    'items' => [['title' => 'Software', 'url' => 'https://…']],
    'resultKey' => 'services',
    'text' => ['heading' => 'Services:', 'line' => '{n}. {title} – {url}'],
]
Copied!

Template placeholders 

The text templates use {field} placeholders filled from the item (or, for headings, from {count} / {query}). {n} yields the 1-based index of the current line.

Escape hatch 

If no primitive fits, leave data minimal and set moduleUrl to the URL of an ES module exporting execute(args, ctx). The runtime imports it on first call and delegates to it.

Analytics 

Every tool call sends a same-origin beacon to the configured endpoint. The ToolRegistry::toolNames() list (all registered providers) is the whitelist the ingest middleware validates against – it follows your tools automatically.