Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions public/webmcp-setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/**
* IC Skills WebMCP setup — registers list_skills, get_skill, and search_skills
* with navigator.modelContext so AI agents (Chrome 146+, @dfinity/webmcp
* polyfill, WebMCP-aware browser extensions) can call them as tools.
*
* This script uses the site's existing /.well-known/skills/ static endpoints,
* so it works without a deployed skills canister. When the skills canister
* (PR #148) is deployed and @dfinity/webmcp is published to npm, this script
* can be replaced with the full ICWebMCP integration from webmcp.json.
*
* Discovery: <link rel="modelcontext" href="/.well-known/webmcp.json"> in the
* page head signals Chrome 146+ to load the manifest automatically.
*/
(function () {
"use strict";

// Install navigator.modelContext polyfill for non-Chrome-146+ environments.
if (typeof navigator !== "undefined" && !navigator.modelContext) {
var _registry = new Map();
Object.defineProperty(navigator, "modelContext", {
value: {
registerTool: function (tool) {
_registry.set(tool.name, tool);
return Promise.resolve();
},
unregisterTool: function (name) {
_registry.delete(name);
return Promise.resolve();
},
_registry: _registry,
},
writable: true,
configurable: true,
});
}

if (typeof navigator === "undefined" || !navigator.modelContext) return;

var ctx = navigator.modelContext;

ctx.registerTool({
name: "list_skills",
description:
"List all available Internet Computer skill topics with their " +
"names, titles, descriptions, and categories.",
inputSchema: {
type: "object",
properties: {},
additionalProperties: false,
},
execute: function () {
return fetch("/.well-known/skills/index.json").then(function (r) {
return r.json();
});
},
});

ctx.registerTool({
name: "get_skill",
description:
'Get the full documentation for a specific IC skill by name ' +
'(e.g. "motoko", "asset-canister", "internet-identity"). ' +
"Returns the complete SKILL.md content. Returns null if not found.",
inputSchema: {
type: "object",
properties: {
name: {
type: "string",
description:
'Skill name in lowercase-hyphenated format, ' +
'e.g. "motoko", "ckbtc", "https-outcalls", "webmcp"',
},
},
required: ["name"],
additionalProperties: false,
},
execute: function (params) {
var url =
"/.well-known/skills/" +
encodeURIComponent(params.name) +
"/SKILL.md";
return fetch(url).then(function (r) {
return r.ok ? r.text() : null;
});
},
});

ctx.registerTool({
name: "search_skills",
description:
"Search Internet Computer skill documentation by keyword. " +
"Matches against skill names and descriptions. " +
"Returns summaries of matching skills.",
inputSchema: {
type: "object",
properties: {
query: {
type: "string",
description: "Keyword to match against skill names and descriptions",
},
},
required: ["query"],
additionalProperties: false,
},
execute: function (params) {
return fetch("/.well-known/skills/index.json")
.then(function (r) {
return r.json();
})
.then(function (data) {
var q = String(params.query).toLowerCase();
return {
skills: data.skills.filter(function (s) {
return (
s.name.toLowerCase().includes(q) ||
s.description.toLowerCase().includes(q)
);
}),
};
});
},
});
})();
2 changes: 2 additions & 0 deletions src/layouts/BaseLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ const defaultJsonLd = JSON.stringify({
<meta name="twitter:image" content={ogImage}>

<link rel="alternate" type="text/plain" title="LLMs.txt" href="/llms.txt">
<link rel="modelcontext" href="/.well-known/webmcp.json">

<meta name="theme-color" content="#000000">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
Expand All @@ -86,6 +87,7 @@ const defaultJsonLd = JSON.stringify({
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">

<script is:inline src="/matomo.js" async></script>
<script is:inline src="/webmcp-setup.js" defer></script>
</head>
<body>
<script is:inline>
Expand Down
Loading