Hands on
Hello, WebMCP
You want to see a WebMCP tool work, end to end, before you add one to your own application. This is one page with one tool, and every step of testing it in XataWorks.
Try it first
The example is hosted with this guide. In XataWorks, paste this into the browser’s address bar:
https://xataworks.ca/developers/examples/hello/
Then, in a chat, ask: “Use the say_hello tool on the open page to greet Ada.”
The rest of this article is what happens next, and the code that makes it happen.
The whole application
This is the hosted file, complete. It is an ordinary HTML page with one script, and the script does one thing: register a tool.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Hello, WebMCP</title>
<meta name="description" content="The smallest WebMCP application: one tool.">
<link rel="stylesheet" href="../example.css">
</head>
<body>
<header>
<h1>Hello, WebMCP</h1>
<p>An example application for the XataWorks developer guide.
<a href="../../your-first-webmcp-tool.html">How it works</a></p>
</header>
<main>
<p class="out" id="out">No one has been greeted yet.</p>
</main>
<script>
function register(registry) {
registry.registerTool({
name: "say_hello",
description: "Greet a person by name on this page " +
"and return the greeting.",
inputSchema: {
type: "object",
properties: {
name: { type: "string", description: "Who to greet" }
},
required: ["name"]
},
annotations: { readOnlyHint: true, openWorldHint: false },
execute: async ({ name }) => {
const text = "Hello, " + name + "!";
document.getElementById("out").textContent = text;
return { content: [{ type: "text", text }] };
}
});
}
// Where WebMCP is missing, the page still works; it offers no tools.
const registry = document.modelContext || navigator.modelContext;
if (registry) register(registry);
</script>
</body>
</html>
Save it as index.html anywhere and it runs as it is
— there is nothing to install and nothing to build.
What each part does
nameis how the agent refers to the tool. Usesnake_case, and make it specific to your application when it could collide with someone else’s.descriptionis what the agent reads to decide whether this is the tool it wants. It is the most important line you write.inputSchemais a JSON Schema for the arguments. Describe every property; mark the required ones.annotationsare behavioural hints.readOnlyHint: trueis honest here: writing a greeting on the screen changes nothing the application stores.openWorldHint: falsesays the tool reaches nothing beyond this page.executedoes the work, in your page, and returns a result for the agent. Whatever it returns is what the agent receives.
The last two lines pick the registry. The current draft of the
standard puts it on document.modelContext; an earlier
draft put it on navigator.modelContext; XataWorks
supports both. In a browser with neither, the page still works and
offers no tools.
What you are asked
The first time an agent in a chat wants this site’s tools, XataWorks stops and asks the person — here, you. The question names the site, the tool the agent used to ask, the tab, and every tool the site publishes with its class:
http://; yours will read https://, and nothing else differs.Open the arrow beside Allow this call only and choose Allow read-only tools on this website. That lets this call run, and every later call to a read-only tool on this site in this chat, without asking again. Every answer, and what each one does, is in answering a permission request.
The answer
execute ran in it; the chat shows the agent finding the tab, listing its tools, and calling say_hello.Running it on your own machine
Save the file, serve its folder with any static server — for
example python3 -m http.server 8000 — and open
http://localhost:8000/ in XataWorks.
You will not be asked the site-permission question on
localhost. XataWorks treats a page served from
localhost, 127.0.0.1 or ::1
as its own, so an agent may use that page’s tools without
the question above. This is convenient while you build, and it
means you are not seeing what your users will see. To see the
question, use the hosted copy, or serve your page under a real
host name.
Putting it in your own application
Three things carry over: register on the page where the operation
lives; make execute call the same code your own button
calls, so your existing checks apply; and declare honest hints.
The next example has tools that change things, and a deletion the
person is asked about each time —
a tool that changes
things.