> ## Documentation Index
> Fetch the complete documentation index at: https://mcp-b-sync-npm-packages-docs-bf03420.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Build Your First Tool

> Create a single WebMCP tool on a plain HTML page and verify it works, with no build step or framework.

In this tutorial, we will create a plain HTML page that registers one WebMCP tool and then verify that the tool works by calling it from the browser console. By the end, you will have a working page with a tool that any AI agent can discover and call.

## Prerequisites

* A text editor
* A modern web browser (Chrome, Edge, Firefox, or Safari)

No build tools, no Node.js, no framework.

## What we will build

A single HTML page that:

1. Loads the strict `@mcp-b/webmcp-polyfill` runtime via a script tag
2. Registers a tool called `get-page-title`
3. Displays confirmation in the page when the tool is ready

<Steps>
  <Step title="Create the HTML file">
    Create a new file called `index.html` and paste in this starting point:

    ```html index.html theme={null}
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>My First WebMCP Tool</title>
      <script src="https://unpkg.com/@mcp-b/webmcp-polyfill@latest/dist/index.iife.js"></script>
    </head>
    <body>
      <h1>My First WebMCP Tool</h1>
      <p id="status">Loading...</p>

      <script>
        // We will register our tool here in the next step.
      </script>
    </body>
    </html>
    ```

    The `<script>` tag in the `<head>` loads `@mcp-b/webmcp-polyfill`, which installs the strict WebMCP surface on `navigator.modelContext`. No import statements or bundler needed.
  </Step>

  <Step title="Register a tool">
    Replace the empty `<script>` block at the bottom of the page with:

    ```html index.html theme={null}
    <script>
      navigator.modelContext.registerTool({
        name: "get-page-title",
        description: "Get the current page title",
        inputSchema: { type: "object", properties: {} },
        async execute() {
          return {
            content: [{ type: "text", text: document.title }]
          };
        }
      });

      document.getElementById("status").textContent =
        'Tool "get-page-title" registered.';
    </script>
    ```

    This registers a single tool on `navigator.modelContext`. The tool returns the current page title. The `execute` function is what runs when an AI agent, or you through the testing API, calls the tool.
  </Step>

  <Step title="Open the page in your browser">
    Open `index.html` directly in your browser (double-click the file, or drag it into a browser window). You should see:

    ```
    My First WebMCP Tool
    Tool "get-page-title" registered.
    ```

    If the status still says "Loading...", open the browser console (F12) and check for errors.
  </Step>

  <Step title="Verify the tool from the console">
    Open the browser console (F12, then click the Console tab). Type the following:

    ```javascript theme={null}
    const tools = navigator.modelContextTesting.listTools();
    console.log(tools);
    ```

    You should see an array containing your tool:

    ```javascript theme={null}
    [{ name: "get-page-title", description: "Get the current page title", inputSchema: "..." }]
    ```

    Now call the tool:

    ```javascript theme={null}
    const result = await navigator.modelContextTesting.executeTool(
      "get-page-title",
      "{}"
    );
    console.log(JSON.parse(result));
    ```

    The output should be an object containing your tool's response:

    ```json theme={null}
    {
      "content": [{ "type": "text", "text": "My First WebMCP Tool" }],
      "isError": false
    }
    ```

    You have registered a WebMCP tool and executed it through the testing API.
  </Step>

  <Step title="Try changing the tool">
    Back in your editor, change the tool so it returns a fixed message instead of `document.title`:

    ```javascript theme={null}
    async execute() {
      return {
        content: [{ type: "text", text: "Tool is working" }]
      };
    }
    ```

    Refresh the browser and run the `executeTool` call again from the console. Notice the text field in the response now says `"Tool is working"` instead of the page title.
  </Step>
</Steps>

## The complete page

Here is the full `index.html` for reference:

```html index.html theme={null}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>My First WebMCP Tool</title>
  <script src="https://unpkg.com/@mcp-b/webmcp-polyfill@latest/dist/index.iife.js"></script>
</head>
<body>
  <h1>My First WebMCP Tool</h1>
  <p id="status">Loading...</p>

  <script>
    navigator.modelContext.registerTool({
      name: "get-page-title",
      description: "Get the current page title",
      inputSchema: { type: "object", properties: {} },
      async execute() {
        return {
          content: [{ type: "text", text: document.title }]
        };
      }
    });

    document.getElementById("status").textContent =
      'Tool "get-page-title" registered.';
  </script>
</body>
</html>
```

## What you learned

* `@mcp-b/webmcp-polyfill` installs the strict WebMCP surface when loaded via a script tag
* `registerTool()` registers a single tool with a name, description, input schema, and execute function
* `navigator.modelContextTesting` provides `listTools()` and `executeTool()` for verifying tools from the console
* Tools return a `content` array with `{ type: "text", text: "..." }` objects

## Next steps

* [Build Your First React Tool](/tutorials/first-react-tool) to register tools from React components
* [Choose a Runtime](/how-to/choose-runtime) to see when to move from the strict core to the full MCP-B runtime
* [Try the Native Chrome Preview](/tutorials/first-native-preview) to use the browser's built-in WebMCP API
* [Connect a Desktop Agent](/tutorials/desktop-agent-relay) to let Claude or Cursor call your tools
