# WebMCP Opens Your Website to Agents

> **Source:** https://upstash.com/blog/webmcp-context7
> **Date:** 2026-08-26
> **Author(s):** Fahreddin Ozcan
> **Reading time:** 9 min read
> **Tags:** context7, webmcp, ai, documentation
> **Format:** text/markdown — machine-readable content for agents and LLMs
> **Ask:** Use https://upstash.com/ask?q=your+question to search the Upstash answer index (docs, pricing, blog, product pages). Returns relevant pages with title, text, url, and publishedAt.
>
> Examples:
> https://upstash.com/ask?q=how+is+redis+priced
> https://upstash.com/ask?q=how+is+upstash+box+priced
> https://upstash.com/ask?q=what+is+the+redis+free+tier
> https://upstash.com/ask?q=compare+upstash+redis+to+elasticache
> https://upstash.com/ask?q=does+upstash+support+hipaa
> https://upstash.com/ask?q=what+regions+does+upstash+support
> https://upstash.com/ask?q=how+to+use+upstash+with+vercel
> https://upstash.com/ask?q=how+to+use+upstash+from+cloudflare+workers

Open your documentation site to ChatGPT, Codex and other browser agents with WebMCP and Context7.

---

[ChatGPT and Codex support WebMCP through site tools](https://learn.chatgpt.com/docs/webmcp). In the ChatGPT desktop app's built-in browser, supported accounts can discover and call tools provided by the open page.

Without a site tool, an agent has to read the page, find buttons and fill out forms. It has to infer what the website can do from an interface built for people.

[WebMCP](https://webmachinelearning.github.io/webmcp/) gives it a direct path. A website can register structured tools that browser agents discover and call from the page.

For a library website, that tool could search the docs. Instead of making an agent scan page after page, we can expose a `search_upstash_docs` tool and use [Context7](https://context7.com) to retrieve the right documentation in the background.

Let's build it.

## How It Works

ChatGPT calls WebMCP tools **site tools**. Here is how the flow works in the ChatGPT desktop app:

1. The user opens the library website in ChatGPT's built-in browser.
2. The page registers `search_upstash_docs` as a site tool.
3. ChatGPT Work or Codex discovers the tool and selects it when it is relevant to the user's request.
4. The built-in browser checks the tool request before the website carries it out.
5. The tool sends the question to your server endpoint.
6. The server asks Context7 for relevant documentation and returns it to the agent.

The user and agent work with the same live page and signed-in session. Your website defines the available actions, while Context7 handles ingesting, updating and retrieving the documentation behind this tool.

This is different from a regular MCP server, whose tools can work without an open webpage. WebMCP makes predefined tools available when the agent visits the website. A website can support both approaches.

## Before You Start

You need:

- A library indexed by Context7
- A Context7 API key
- A Next.js application using the App Router
- ChatGPT desktop with site tools available, or Chrome 149 or later for local testing

This guide uses Next.js, but the same browser and server split works with other frameworks.

You can find the library ID in the URL of its Context7 page. We will use `/upstash/docs`, which contains the official documentation for Upstash products.

Keep this ID on the server. The tool only needs to answer questions about your library, so the agent should not be able to choose a different ID.

## Create a Context7 Endpoint

First, add a server endpoint that takes a question and sends it to the [Context7 API](https://context7.com/docs/api-guide).

Install the [Context7 TypeScript SDK](https://context7.com/docs/sdks/ts/getting-started) and Zod:

```bash title="Terminal"
pnpm add @upstash/context7-sdk zod
pnpm add -D webmcp-types
```

The following example uses a Next.js route handler:

```ts title="app/api/agent/docs/route.ts"
import { Context7 } from "@upstash/context7-sdk";
import { z } from "zod";

const CONTEXT7_LIBRARY_ID = "/upstash/docs";
const context7 = new Context7();

const QuerySchema = z
  .object({
    query: z.string().trim().min(1).max(500),
  })
  .strict();

export async function POST(request: Request) {
  const body = await request.json().catch(() => null);
  const parsed = QuerySchema.safeParse(body);

  if (!parsed.success) {
    return Response.json(
      { error: "Query must contain between 1 and 500 characters." },
      { status: 400 },
    );
  }

  try {
    const context = await context7.getContext(
      parsed.data.query,
      CONTEXT7_LIBRARY_ID,
      { type: "txt" },
    );

    return Response.json({
      libraryId: CONTEXT7_LIBRARY_ID,
      context,
    });
  } catch {
    return Response.json(
      { error: "Context7 documentation lookup failed." },
      { status: 502 },
    );
  }
}
```

The SDK's [`getContext` method](https://context7.com/docs/sdks/ts/commands/get-context) accepts the question and library ID. Passing `{ type: "txt" }` returns a single plain-text result that the tool can send back to the agent.

Add your Context7 API key to the server environment:

```bash title=".env.local"
CONTEXT7_API_KEY=ctx7sk_your_key
```

The SDK reads `CONTEXT7_API_KEY` from the environment. Keep the key out of client-side code. WebMCP tools run in the page, where users, browser extensions and other scripts could read it.

## Register the WebMCP Tool

The [WebMCP Imperative API](https://developer.chrome.com/docs/ai/webmcp/imperative-api) must be called by client-side JavaScript after the page loads. In a Next.js application, put the registration in a [Client Component](https://nextjs.org/docs/app/getting-started/server-and-client-components) at `components/register-upstash-docs-tool.tsx`. The `webmcp-types` development dependency adds the experimental `document.modelContext` API to TypeScript's browser types.

```tsx title="components/register-upstash-docs-tool.tsx"
/// <reference types="webmcp-types" />

"use client";

import { useEffect } from "react";

export function RegisterUpstashDocsTool() {
  useEffect(() => {
    const registration = new AbortController();

    async function registerTool() {
      const modelContext = document.modelContext;

      if (!modelContext) {
        return;
      }

      await modelContext.registerTool(
        {
          name: "search_upstash_docs",
          title: "Search Upstash documentation",
          description:
            "Searches current Upstash documentation for implementation guidance and code examples. Use it for questions about Upstash products and SDKs.",
          inputSchema: {
            type: "object",
            properties: {
              query: {
                type: "string",
                description:
                  "A specific implementation or API question about an Upstash product.",
                minLength: 1,
                maxLength: 500,
              },
            },
            required: ["query"],
            additionalProperties: false,
          },
          annotations: {
            readOnlyHint: true,
            untrustedContentHint: true,
          },
          async execute({ query }, { signal }) {
            const response = await fetch("/api/agent/docs", {
              method: "POST",
              headers: {
                "Content-Type": "application/json",
              },
              body: JSON.stringify({ query }),
              signal,
            });

            if (!response.ok) {
              const result = await response.json().catch(() => null);
              throw new Error(result?.error ?? "Documentation lookup failed.");
            }

            const result = await response.json();
            return result.context;
          },
        },
        { signal: registration.signal },
      );
    }

    void registerTool().catch((error) => {
      if (!registration.signal.aborted) {
        console.error("Could not register WebMCP tools", error);
      }
    });

    return () => registration.abort();
  }, []);

  return null;
}
```

Render this component from your root layout so the tool is available on every page:

```tsx title="app/layout.tsx"
import { RegisterUpstashDocsTool } from "@/components/register-upstash-docs-tool";
import type { ReactNode } from "react";

export default function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html lang="en">
      <body>
        <RegisterUpstashDocsTool />
        {children}
      </body>
    </html>
  );
}
```

If the tool should only be available in your documentation, render `RegisterUpstashDocsTool` from the docs section's layout instead.

The feature check keeps the website working in browsers without WebMCP. Everyone still gets the normal website. Supported agents also get the structured tool.

The verb-based name and focused description help an agent decide when to call the tool. Chrome's [WebMCP best practices](https://developer.chrome.com/docs/ai/webmcp/best-practices) provide more guidance for names, descriptions and schemas.

The annotations tell compatible agents that this tool only reads data and that its result comes from an external source. They are hints, not authorization checks. The registration `AbortSignal` removes the tool when the page or component is no longer active. The execution signal is passed to `fetch`, so cancelling a tool call also cancels the Context7 request.

## Test Locally in Chrome

WebMCP is still experimental. Chrome's [official WebMCP setup guide](https://developer.chrome.com/docs/ai/webmcp/) lists the current browser requirements and testing tools.

### 1. Enable WebMCP for local development

In Chrome 149 or later, open:

```text
chrome://flags/#enable-webmcp-testing
```

Set the flag to **Enabled**, then relaunch Chrome. Restarting the tab is not enough.

Start the Next.js application and open the page in the relaunched browser:

```bash title="Terminal"
pnpm dev
```

```text
http://localhost:3000
```

Use the full local URL, including its port. Open DevTools on the application page after it has loaded. The Client Component must hydrate before it can register the tool.

### 2. Verify the browser API and tool registration

Run this in the Console:

```js
document.modelContext;
```

It should return a `ModelContext` object. If it returns `undefined`, check that:

- Chrome is version 149 or later
- The flag is enabled and Chrome was relaunched
- DevTools is attached to your application page, not a Chrome error page
- The page is not opting out of origin isolation

Next, inspect the tools registered by the page:

```js
const tools = await document.modelContext.getTools();
const tool = tools.find(({ name }) => name === "search_upstash_docs");

if (!tool) {
  throw new Error("search_upstash_docs is not registered");
}

tool;
```

The result should include the name, description and input schema from the Client Component. If the tool is missing, check the browser console for registration errors and confirm that `RegisterUpstashDocsTool` is rendered by the current layout.

### 3. Execute the tool

Call it directly from the Console:

```js
const result = await document.modelContext.executeTool(
  tool,
  JSON.stringify({ query: "How do I authenticate with Upstash Redis?" }),
);

console.log(result);
```

The [Imperative API](https://developer.chrome.com/docs/ai/webmcp/imperative-api) expects the arguments as a valid JSON string. A successful call should return documentation from Context7. An invalid query or failed lookup should return the descriptive error from the endpoint.

For an end-to-end check, use Chrome's [Model Context Tool Inspector](https://developer.chrome.com/docs/ai/webmcp/#imitate-agent-chat-with-the-inspector-extension). It shows the tools registered on the page, validates their schemas, lets you call them manually and accepts natural-language prompts so you can check whether an agent selects the right tool.

Try prompts such as:

- How do I connect to Upstash Redis from Next.js?
- How do I verify a QStash signature?
- How do I create an Upstash Workflow endpoint?

Confirm that `search_upstash_docs` is selected, the response is relevant and cancellation and error responses work.

### 4. Check the page requirements

WebMCP also requires an origin-isolated document. Do not disable origin isolation with `document.domain` or an `Origin-Agent-Cluster: ?0` header.

The tool is available to the top-level page and same-origin frames by default. If you embed it in a cross-origin frame, you must explicitly allow the `tools` permissions policy for that frame.

### 5. Enable a deployed origin

The testing flag only enables WebMCP in your own browser. To make the feature available to compatible Chrome clients during the experiment:

1. Register the exact deployed origin for the [WebMCP origin trial](https://developer.chrome.com/origintrials/#/view_trial/4163014905550602241).
2. Add the issued token to every page that registers the tool, either with an `Origin-Trial` response header or an origin-trial meta tag.
3. Open the deployed page and verify the token in the Application panel of Chrome DevTools.
4. Track the token's expiry date and renew it when necessary.

Chrome's [origin trial setup guide](https://developer.chrome.com/docs/web-platform/origin-trials) covers token registration, delivery and troubleshooting.

The tool calls a same-origin route and may use the visitor's signed-in session. Treat the endpoint like any other application API. Validate authorization on the server instead of relying on tool annotations or the agent.

Before deploying, add the usual server protections:

- Authentication and authorization where needed
- Rate limiting and abuse protection
- Input validation
- Request logging
- Response caching
- Context7 error and quota handling

## Conclusion

WebMCP gives browser agents a structured way to use the current website. Context7 gives those tools current, version-specific library documentation.

Together, they make a documentation site agent-ready with a small amount of code. People keep the same website, agents get a reliable docs tool, and the library team does not have to build its own crawling and retrieval system.

WebMCP is still experimental, so ship this as a progressive enhancement and follow the [WebMCP repository](https://github.com/webmachinelearning/webmcp) for API changes.