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

  • name is how the agent refers to the tool. Use snake_case, and make it specific to your application when it could collide with someone else’s.
  • description is what the agent reads to decide whether this is the tool it wants. It is the most important line you write.
  • inputSchema is a JSON Schema for the arguments. Describe every property; mark the required ones.
  • annotations are behavioural hints. readOnlyHint: true is honest here: writing a greeting on the screen changes nothing the application stores. openWorldHint: false says the tool reaches nothing beyond this page.
  • execute does 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:

A permission card in the chat, headed with the request “Use the say_hello tool on the open page to greet Ada.” and labelled Approval. It reads: An agent wants permission to use this site's page tools on xataworks.ca. Let the agent see and call the tools this website publishes to it. Requested by browser_list_webmcp_tools, in the tab “Hello, WebMCP” at http://xataworks.ca/developers/examples/hello/. Allowing this applies to xataworks.ca only. Under “What this site publishes”: say_hello, Read only. Buttons: Allow this call only, with an arrow for more, and Deny this call, with an arrow for more.
The site-permission question, photographed as it appeared. The figures were captured from a local copy of this site, which is why the address reads 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

The XataWorks window. The chat shows the request, a folded line reading 2 tools, a line reading Browsing list WebMCP tools, the agent noting that say_hello expects a name parameter, a line reading Calling Say Hello via WebMCP, and the reply: The page says “Hello, Ada!”. The browser panel on the right shows the Hello, WebMCP page, whose status line now reads Hello, Ada!
The page changed because your execute ran in it; the chat shows the agent finding the tab, listing its tools, and calling say_hello.
The whole run: the request, the site-permission question with its approving and refusing answers opened in turn, Allow read-only tools on this website, and the greeting arriving on the page. No sound.

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.