An Omeka-S module that implements the WebMCP standard to expose Omeka-S backend management capabilities to AI agents through the browser.
WebMCP is a W3C Community Group draft specification that lets web pages register tools via navigator.modelContext.registerTool() so AI agents (browser extensions, built-in browser agents) can discover and invoke them. Resources are registered with navigator.modelContext.registerResource().
Requirements: Docker Desktop 4+, Make
make up # start stack (foreground)
make upd # start stack (background)
make down # stop stack
make clean # stop + remove volumes
make test # run PHPUnit tests
make lint # run PHP code style checkerOpen http://localhost:8080. Users created automatically:
| Role | Password | |
|---|---|---|
admin@example.com |
global_admin | PLEASE_CHANGEME |
editor@example.com |
editor | 1234 |
- Clone or copy this repository into
modules/WebMCPinside your Omeka-S installation. - In the Omeka-S admin panel go to Modules and install WebMCP.
- Under Modules → WebMCP → Configure, enable the tool groups you want to expose.
- Install Chrome Canary.
- Enable the flag:
chrome://flags/#enable-web-mcp. - Install the WebMCP Chrome extension.
- Open the Omeka-S admin interface — tools and resources register automatically.
- Open the AI agent side-panel and start interacting.
All tool and resource callbacks route through a server-side PHP proxy at POST /admin/webmcp/proxy instead of calling /api/* directly. The proxy uses Omeka\ApiManager internally, so it runs inside Omeka-S's own request lifecycle and benefits from the authenticated PHP session.
Every request includes a CSRF token (X-CSRF-Token header) generated by Laminas\Validator\Csrf and injected into window.WebMCPConfig when the admin layout loads. The JS assets include a ?v=<mtime> cache-bust parameter so browsers always load the latest version after module updates.
Omeka-S's ValueHydrator requires a property_id on every property value. The proxy automatically injects "property_id": "auto" into any property-term value that lacks one, so the hydrator can resolve the ID from the vocabulary term key (e.g. dcterms:title → ID 1). This happens both in the PHP proxy (normalizePropertyData()) and in the JS helpers (literal(), normalizeProperties()).
Proxy payload format:
{ "op": "search|get|create|update|delete|batch_create|batch_delete",
"resource": "items|item_sets|media|sites|users|...",
"id": 42,
"query": {},
"data": {},
"ids": [] }| Tool | Description |
|---|---|
create-item |
Create a new item |
update-item |
Update an existing item |
delete-item |
Delete an item |
search-items |
Search by full-text, template, item set, or properties |
get-item |
Get a single item by ID |
catalog-item |
Set full catalog metadata: all Dublin Core fields, RDF resource class, resource template |
| Tool | Description |
|---|---|
upload-media |
Navigate to the item edit page for file upload via form |
list-media |
List media attached to an item |
add-media-url |
Fetch a file from a URL and store it locally (supports picsum.photos, pravatar.cc, etc.) |
add-media-html |
Attach an inline HTML snippet as media |
add-media-embed |
Attach an oEmbed resource (Vimeo, SoundCloud, Flickr, etc.) |
add-media-youtube |
Attach a YouTube video (with optional start/end times in seconds) |
add-media-iiif |
Attach a IIIF Image API resource (info.json URL) |
add-media-iiif-presentation |
Attach a IIIF Presentation manifest |
| Tool | Description |
|---|---|
create-item-set |
Create a new item set (collection) |
update-item-set |
Update an existing item set |
delete-item-set |
Delete an item set |
list-item-sets |
List all item sets |
| Tool | Description |
|---|---|
create-site |
Create a new Omeka-S site (slug auto-generated from title if omitted) |
update-site |
Update a site |
list-sites |
List all sites |
| Tool | Description |
|---|---|
create-user |
Create a new user |
update-user |
Update a user |
delete-user |
Delete a user |
list-users |
List all users |
| Tool | Description |
|---|---|
list-vocabularies |
List available vocabularies (Dublin Core, BIBO, FOAF, …) |
list-properties |
List properties of a vocabulary |
list-resource-classes |
List RDF classes (dctype:Image, foaf:Person, schema:Place, …) |
list-resource-templates |
List resource templates |
get-resource-template |
Get a resource template by ID |
| Tool | Description |
|---|---|
batch-create-items |
Create multiple items at once |
batch-delete-items |
Delete multiple items |
catalog-item accepts all 15 Dublin Core Terms fields by plain name, so the AI never has to write JSON-LD manually:
catalog-item id=42
title = "Photograph of the harbour, 1923"
creator = "Anonymous"
date = "1923"
type = "Photograph"
subject = ["harbour", "maritime", "1920s"]
rights = "Public Domain"
resource_class = "dctype:Image"
resource_template_id = 3
The resource_class field accepts a vocabulary term string (e.g. "dctype:Image", "foaf:Person", "schema:Place"). The tool resolves the numeric class ID automatically. Use list-resource-classes to browse what is available.
add-media-url downloads the file and stores it inside Omeka-S — it is not an external link. Any publicly accessible URL works, including placeholder services:
add-media-url item_id=42 url="https://picsum.photos/800/600" title="Cover photo"
add-media-url item_id=42 url="https://pravatar.cc/300" title="Portrait"
add-media-youtube item_id=42 url="https://youtu.be/dQw4w9WgXcQ"
add-media-embed item_id=42 url="https://vimeo.com/123456789"
| Resource | URI | Description |
|---|---|---|
omeka-dashboard |
omeka://dashboard |
Instance summary (item/media/site totals, recent items) |
omeka-item |
omeka://items/{id} |
Full JSON-LD representation of an item |
omeka-site-navigation |
omeka://sites/{id}/navigation |
Site navigation structure |
omeka-api-info |
omeka://api-info |
Current user identity and role |
WebMCP/
├── Module.php # Module bootstrap (CSRF injection, asset cache-bust, event wiring)
├── config/
│ ├── module.ini # Module metadata
│ └── module.config.php # Routes, controllers, default settings
├── src/
│ ├── Controller/Admin/
│ │ └── WebMCPProxyController.php # Proxy endpoint (POST /admin/webmcp/proxy)
│ └── Form/
│ └── ConfigForm.php # Admin configuration form
├── asset/js/
│ ├── webmcp.js # Tool registrations
│ └── webmcp-resources.js # Resource registrations
├── test/ # PHPUnit tests
├── language/ # Translations
├── docker-compose.yml # Dev stack
├── Makefile # Dev helpers
└── README.md
- Omeka S 4.x or later
- PHP 8.1+
Published under the GNU GPLv3 license. See LICENSE.