diff --git a/index.html b/index.html
index 83e9275..49a3566 100644
--- a/index.html
+++ b/index.html
@@ -5,7 +5,7 @@
-
+
diff --git a/package.json b/package.json
index 0778f7c..5d56804 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "amerc-site",
- "version": "0.80.0-agentready",
+ "version": "0.81.0-webmcp",
"private": true,
"type": "module",
"scripts": {
diff --git a/src/Scene2D.jsx b/src/Scene2D.jsx
index 0153415..935dae9 100644
--- a/src/Scene2D.jsx
+++ b/src/Scene2D.jsx
@@ -10,7 +10,7 @@ import { api } from './api.js';
const Menu = ({ size = 20 }) => ();
const X = ({ size = 20 }) => ();
-const RELEASE = '0.80.0-agentready';
+const RELEASE = '0.81.0-webmcp';
const NAV = [
{ id: 'home', label: 'Tavern' },
diff --git a/src/main.jsx b/src/main.jsx
index c27eefe..c2f6637 100644
--- a/src/main.jsx
+++ b/src/main.jsx
@@ -19,6 +19,8 @@ if (App === Scene2DApp) {
for (const n of ['inside', 'castlefloors', 'victoria', 'barrel', 'princess', 'soldier', 'soldier_altcolor']) {
const im = new Image(); im.src = `/scene2d/lpc/${n}.png`;
}
+ // Expose amerc to AI agents as WebMCP tools (no-op where unsupported).
+ import('./webmcp.js').then((m) => m.registerAmercTools()).catch(() => {});
}
// Catch render errors so a single bad component can't blank the whole app.
diff --git a/src/webmcp.js b/src/webmcp.js
new file mode 100644
index 0000000..15625ec
--- /dev/null
+++ b/src/webmcp.js
@@ -0,0 +1,54 @@
+// WebMCP — expose amerc as callable tools to AI agents browsing the page
+// (Web Model Context Protocol, navigator.modelContext). amerc is for agents,
+// so an agent on amerc.ai can browse/query/navigate the tavern as tools
+// instead of scraping the DOM. No-ops gracefully where WebMCP isn't supported.
+import { api } from './api.js';
+
+export function registerAmercTools() {
+ if (typeof navigator === 'undefined' || !('modelContext' in navigator)) return;
+ const mc = navigator.modelContext;
+ if (!mc || typeof mc.registerTool !== 'function') return;
+ try {
+ mc.registerTool({
+ name: 'amerc_browse_agents',
+ description: 'Browse the amerc roster of hireable AI agents. Optionally filter by a free-text query or a single tag (e.g. backend, web, model).',
+ inputSchema: {
+ type: 'object',
+ properties: {
+ query: { type: 'string', description: 'free-text search over name, description and tags' },
+ tag: { type: 'string', description: 'restrict to one tag' },
+ },
+ },
+ annotations: { readOnlyHint: true },
+ execute: async (input = {}) => {
+ const d = await api(`/agents/classes${input.tag ? `?tag=${encodeURIComponent(input.tag)}` : ''}`);
+ let classes = d.classes || [];
+ if (input.query) {
+ const q = String(input.query).toLowerCase();
+ classes = classes.filter((c) => `${c.name} ${(c.tags || []).join(' ')} ${c.description || ''}`.toLowerCase().includes(q));
+ }
+ return { count: classes.length, agents: classes.map((c) => ({ id: c.id, name: c.name, kind: c.kind, tags: c.tags, description: c.description, owner: c.owner, online: c.onlineCount, instances: c.instanceCount })) };
+ },
+ });
+ mc.registerTool({
+ name: 'amerc_platform_stats',
+ description: 'Get live amerc platform stats: agent classes published, instances online now, and active sessions.',
+ inputSchema: { type: 'object', properties: {} },
+ annotations: { readOnlyHint: true },
+ execute: async () => api('/agents/stats'),
+ });
+ mc.registerTool({
+ name: 'amerc_get_agent',
+ description: 'Get full details of one amerc agent class by its numeric id, including its instances and their online status.',
+ inputSchema: { type: 'object', properties: { id: { type: 'integer', description: 'the agent class id' } }, required: ['id'] },
+ annotations: { readOnlyHint: true },
+ execute: async (input) => api(`/agents/classes/${Number(input.id)}`),
+ });
+ mc.registerTool({
+ name: 'amerc_open',
+ description: 'Open a section of the amerc tavern in the browser so the user can see it.',
+ inputSchema: { type: 'object', properties: { section: { type: 'string', enum: ['home', 'agents', 'mansion', 'booth', 'account', 'status', 'changelog', 'signin'], description: 'which section to open' } }, required: ['section'] },
+ execute: async (input) => { window.location.hash = `#/${input.section}`; return { ok: true, opened: input.section }; },
+ });
+ } catch { /* WebMCP API shape differs — ignore, the site is unaffected */ }
+}