Skip to content

Browser Client

This path is kept for compatibility with older links. In the current docs structure, browser-oriented integration content is split across the JavaScript SDK docs, the Chrome extension app docs, and the Playground.

Preferred entry points

What this example is good for

A browser client can expose:

  • page information as a GET endpoint
  • DOM search as a POST endpoint
  • page summarization as a prompt
  • page inspection workflow as a skill

This is the simplest path for proving the bridge model end to end.

Minimal HTML Example

WebSocket

html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>MDP Browser Client</title>
  </head>
  <body>
    <h1>MDP Browser Client</h1>
    <script src="https://cdn.jsdelivr.net/npm/@modeldriveprotocol/client@latest/dist/modeldriveprotocol-client.global.js"></script>
    <script>
      void (async () => {
        const readPageInfo = () => ({
          title: document.title,
          url: window.location.href,
          heading: document.querySelector("h1")?.textContent?.trim() ?? ""
        });
        const readInput = (request) => ({
          ...request.queries,
          ...(request.body && typeof request.body === "object" && !Array.isArray(request.body)
            ? request.body
            : {})
        });

        const client = MDP.createMdpClient({
          serverUrl: "ws://127.0.0.1:47372",
          auth: {
            token: "browser-session-token"
          },
          client: {
            id: "browser-01",
            name: document.title || "Browser Client"
          }
        });

        client.expose("/page/info", { method: "GET" }, async (_request, context) => ({
          ...readPageInfo(),
          authToken: context.auth?.token
        }));

        client.expose("/page/search", { method: "POST" }, async (request, context) => {
          const { query } = readInput(request);

          return {
            query,
            matches: typeof query === "string" && document.body.innerText.includes(query) ? 1 : 0,
            title: document.title,
            url: window.location.href,
            authToken: context.auth?.token
          };
        });

        client.expose(
          "/page/info/resource",
          { method: "GET", contentType: "application/json" },
          async () => ({
            mimeType: "application/json",
            text: JSON.stringify(readPageInfo(), null, 2)
          })
        );

        await client.connect();
        client.register();
      })();
    </script>
  </body>
</html>

HTTP Loop

html
<!doctype html>
<html>
  <body>
    <script src="https://cdn.jsdelivr.net/npm/@modeldriveprotocol/client@latest/dist/modeldriveprotocol-client.global.js"></script>
    <script>
      void (async () => {
        const readPageInfo = () => ({
          title: document.title,
          url: window.location.href
        });

        const client = MDP.createMdpClient({
          serverUrl: "http://127.0.0.1:47372",
          auth: {
            token: "browser-session-token"
          },
          client: {
            id: "browser-01",
            name: document.title || "Browser Client"
          }
        });

        client.expose("/page/info", { method: "GET" }, async (_request, context) => ({
          ...readPageInfo(),
          authToken: context.auth?.token
        }));

        await client.connect();
        client.register();
      })();
    </script>
  </body>
</html>

Repo Example

For browser websocket auth, passing auth is enough. The client will bootstrap /mdp/auth before connect().

The example starts by exposing GET /page/info, so the host can retrieve page title and URL before calling more specific endpoints.

See the Pages-hosted browser example for a concrete starter file. If you want the browser runtime to be consumed by a Pi-hosted agent, see Pi Agent Assistant. For a packaged browser integration, use Chrome Extension. For a docs-native configuration surface that can manage multiple connections, use the top-level Playground.

Model Drive Protocol