# ChatGPT site tools: a readiness checklist

> ChatGPT's desktop browser reads WebMCP tools as "Site tools" since August 25, 2026. This checklist covers what to build, how to register it correctly, and how to test it.

Updated 2026-09-24 · https://convertwebmcp.com/guides/chatgpt-site-tools-checklist

Since August 25, 2026, ChatGPT's desktop app reads WebMCP tools registered on pages it visits and surfaces them as "Site tools." An arrow appears in the address bar; the agent can call your tools directly instead of clicking through your UI. Your Shopify store already has 10 default tools registered. If you have built custom tools (for order status, returns, or anything Shopify's defaults do not cover) this checklist is how you make sure ChatGPT can actually see and use them.

## Who can use Site tools

ChatGPT Site tools require the desktop app. They work only with GPT-5.6 Sol and Terra as of August 25, 2026. GPT-5.6 Luna has WebMCP disabled. Enterprise and Edu accounts do not have access.

Safari and Firefox do not support WebMCP. Chrome 149–156 supports it via an origin trial. Chrome 157, targeted for around November 3, 2026, is the expected default-on release: that date has not been committed.

## Registration checklist

### Use the right entry point

- [ ] Register tools with `document.modelContext.registerTool()`, not `navigator.modelContext`. The `navigator.modelContext` entry point is deprecated and being removed in Chrome 150–152.
- [ ] Do not call `provideContext()`, which was removed in March 2026.
- [ ] Do not call `unregisterTool()`, which was removed in April 2026. Use an AbortSignal to unregister.
- [ ] Wrap registration in a feature check (`if (!('modelContext' in document)) return;` inside your setup function) so the code does nothing on browsers without WebMCP.

### Register on the top-level page, early

- [ ] Register tools in JavaScript on the top-level page, not inside an iframe. ChatGPT's agent reads only tools registered on the top-level document.
- [ ] Don't rely on HTML form attributes alone. Chrome reads them, but ChatGPT's agent does not.
- [ ] Register within roughly 3 seconds of page load. Ora's September 2026 audit of a major storefront found a tool registering at 3.6 seconds, after the agent had already read the page. Do not wait for heavy application bundles, third-party scripts, or consent popups to resolve before registering.
- [ ] If you show a cookie consent banner or modal that blocks the page, ensure it does not delay tool registration or prevent the agent from reading the tool list.

### Name and describe tools clearly

- [ ] Keep tool names short, specific, and lowercase with underscores or hyphens: `get_order_status`, not `getOrderStatusForCurrentUser_v2`.
- [ ] Tool names must be 1–128 characters, ASCII alphanumeric, underscores (`_`), hyphens (`-`), or periods (`.`) only.
- [ ] Write descriptions that answer: what does this tool do, when should the agent use it, what does it require? One or two short sentences is usually enough. Avoid padding.
- [ ] Do not write descriptions so long that the agent spends tokens parsing them. Ora found over-long descriptions as a recurring problem in its audit.

### Write tight input schemas

- [ ] Use JSON Schema with a `type: "object"` wrapper.
- [ ] List every required field in the `required` array. Do not leave fields optional that the tool needs to run.
- [ ] Use `enum` for fields with a fixed set of valid values (reasons, statuses, categories). Enums help the agent pick a valid value instead of guessing.
- [ ] Add a short `description` to each property explaining what value belongs there and in what format.

### Set annotations

Browsers don't enforce annotations, but they tell agents how careful to be. ChatGPT shows whether a site tool reads data or makes changes, and asks the user to confirm actions such as purchases, deleting data, sending messages or sharing personal information.

- [ ] Set `readOnlyHint: true` on lookup tools (order status, product lookups, FAQ answers). Signals that the tool changes nothing.
- [ ] Set `consequentialHint: true` on tools that change state: submitting a return, sending a message, anything the shopper cannot undo easily.
- [ ] Set `untrustedContentHint: true` on tools whose output includes user-generated content (reviews, support text) to flag prompt-injection risk.

### Handle results and errors

- [ ] Return concise JSON or plain text. The agent passes your result to a language model. A 10 KB blob costs tokens; a focused result costs less and signals more clearly.
- [ ] Return a clear error message when the tool cannot complete its task: `{ "error": "Order not found or you are not logged in." }` is more useful to the agent than an HTTP 500 or a silent empty result.
- [ ] Do not throw unhandled exceptions from `execute()`. Return a structured error instead.

### Respect the AbortSignal

- [ ] Pass a `signal` from an `AbortController` to `registerTool()`. This is how you unregister a tool when a single-page app changes route or a component unmounts.
- [ ] Check `signal.aborted` at the start of `execute()` for long-running operations, and exit cleanly if the signal has fired.

## Code example

Two tools: one read-only lookup, one consequential action. Both registered with a shared AbortController so they unregister together on cleanup.

```js
if (!document.modelContext) {
  // WebMCP not available: fail silently
} else {
  const controller = new AbortController();
  const { signal } = controller;

  // Read-only lookup: safe to retry, no side effects
  document.modelContext.registerTool(
    {
      name: "get_order_status",
      title: "Get order status",
      description:
        "Returns carrier, tracking number, and estimated delivery for an order. " +
        "Customer must be logged in. Use the order number from the confirmation email, e.g. #1042.",
      inputSchema: {
        type: "object",
        properties: {
          order_number: { type: "string", description: "Order number, e.g. #1042" }
        },
        required: ["order_number"]
      },
      // Your own endpoint; Shopify has no order-status tool by default.
      async execute({ order_number }, { signal }) {
        if (signal.aborted) return { error: "Cancelled." };
        const res = await fetch(
          `/tools/order-status?order=${encodeURIComponent(order_number)}`,
          { signal }
        );
        if (!res.ok) return { error: "Order not found or not logged in." };
        return res.json();
      },
      annotations: { readOnlyHint: true }
    },
    { signal }
  ).catch((e) => console.warn("get_order_status not registered:", e));

  // Consequential action: confirm with the customer before calling
  document.modelContext.registerTool(
    {
      name: "submit_return_request",
      title: "Submit return request",
      description:
        "Opens a return for one line item. Irreversible once submitted. " +
        "Confirm order number, item, and reason before calling.",
      inputSchema: {
        type: "object",
        properties: {
          order_number: { type: "string", description: "Order number, e.g. #1042" },
          line_item_id: { type: "string", description: "Line item ID from the order" },
          reason: {
            type: "string",
            enum: ["wrong_size", "defective", "not_as_described", "changed_mind"]
          }
        },
        required: ["order_number", "line_item_id", "reason"]
      },
      async execute({ order_number, line_item_id, reason }, { signal }) {
        if (signal.aborted) return { error: "Cancelled." };
        const res = await fetch("/tools/returns", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          signal,
          body: JSON.stringify({ order_number, line_item_id, reason })
        });
        if (!res.ok) return { error: "Return could not be started. Check eligibility." };
        return res.json();
      },
      annotations: { consequentialHint: true }
    },
    { signal }
  ).catch((e) => console.warn("submit_return_request not registered:", e));

  // Unregister both tools on teardown: controller.abort();
}
```

`registerTool()` returns a Promise that rejects if, for example, the name is already taken. Await it or attach `.catch()`, as above, so registration errors show up in your console.

## Testing checklist

- [ ] **Chrome flag.** Open `chrome://flags` in Chrome 146 or later and enable `#enable-webmcp-testing`. Install the "Model Context Tool Inspector" extension. Visit your store page. Confirm all expected tools appear in the inspector with correct names and schemas.
- [ ] **ChatGPT desktop.** Open the ChatGPT desktop app. Switch to GPT-5.6 Sol or Terra. Visit your store in the built-in browser. Check the address bar for the Site tools arrow. Ask the agent to use a tool by name and verify the result.
- [ ] **Timing.** Open your browser's DevTools and record how long after page load each tool registers. If any tool registers after 3 seconds, move the registration earlier or load it independently of slower scripts.
- [ ] **Popup and overlay test.** Check whether any modal, banner, or overlay appears within the first 3 seconds that would prevent an agent from reading the page. Cookie banners are the most common blocker.
- [ ] **Error paths.** Test each tool with missing required fields, invalid values, and a logged-out session. Verify that error messages are descriptive, not generic.
- [ ] **AbortSignal.** Navigate away mid-request and confirm the tools unregister without throwing.

## What to expect from Site tools now

Agent-placed orders remain essentially zero in real stores as of September 2026. One store owner reported none after adding WebMCP tooling to a live eSIM store (Indie Hackers, September 2, 2026). The practical value of custom tools is fewer support tickets, shoppers getting a direct answer to "where is my order?", not automated purchases. Build for that outcome first.

## Sources

- [Search Engine Journal: ChatGPT Adds WebMCP Support For Interactive Websites](https://www.searchenginejournal.com/chatgpt-adds-webmcp-support/587237/) (2026-08-27)
- [tech-tech.life: WebMCP: the why, the what, and the how](https://tech-tech.life/2026/09/05/webmcp-the-why-the-what-and-the-how/) (2026-09-05)
- [WebMCP specification (W3C Web Machine Learning Community Group)](https://webmachinelearning.github.io/webmcp/) (2026-09-17)
- [IMMACULATE: WebMCP Is Live on Every Liquid Storefront](https://immaculate.dev/blog/webmcp-live-on-every-liquid-storefront) (2026-08-20)
- [Ora: WebMCP audit of reebok.com](https://webmcp.ora.ai/reebok.com) (2026-09-01)
- [Indie Hackers: Added WebMCP to my live eSIM store](https://www.indiehackers.com/post/added-webmcp-to-my-live-esim-store-agent-checkout-that-reuses-the-human-checkout-efce5a27cb) (2026-09-02)
