All posts
tutorial

Build an Agent-Ready AI Pass App with WebMCP

A coding agent can add AI Pass, keep the normal interface, and expose one safe browser tool. This walkthrough gives Codex the right instructions and shows the resulting code.

EiliyaAugust 31, 20264 min read

You do not need to teach a coding agent the AI Pass setup flow in a long prompt. Give it the product goal and the current integration skill.

For example:

Build a browser app that drafts product captions with AI Pass.

Follow:
https://aipass.one/skills/aipass-integration/v10/SKILL.md
https://aipass.one/skills/aipass-integration/v10/references/webmcp.md

Keep a complete button-and-textarea interface for people and unsupported
browsers. Expose one WebMCP tool named draft_product_caption. Ask for visible
confirmation before the tool makes a paid model call. Do not expose tokens,
wallet details, private storage, or the whole AI Pass SDK.

That prompt gives Codex two different customers: the person using the page and the agent using its tools. The finished app needs to work for both.

What the integration skill tells the agent

The AI Pass integration skill starts by inspecting the existing product. It chooses the browser SDK when the app already has a browser surface and preserves the app's current host, login, billing, and data model.

For a new browser client, the agent requests a user-approved device authorization for project setup. The user reviews that request. The agent never asks for a password, cookie, runtime OAuth token, provider key, or wallet credential.

After approval, the setup tools can provision a public, secretless OAuth client for the exact browser callback origins. Only public project metadata belongs in the code.

WebMCP comes after the ordinary AI action works. That order is deliberate. The agent first builds a real button that can complete the task, then exposes that app-specific action as a tool.

The human path first

The page can be small:

<label for="product">Product facts</label>
<textarea id="product"></textarea>

<label for="tone">Tone</label>
<select id="tone">
  <option value="plain">Plain</option>
  <option value="playful">Playful</option>
  <option value="premium">Premium</option>
</select>

<button id="draft">Draft caption</button>
<label for="caption">Caption</label>
<textarea id="caption"></textarea>
<p id="status" role="status"></p>

<script src="https://aipass.one/aipass-sdk.js"></script>

Initialize the SDK once with the public client ID created for the app:

AiPass.initialize({
  clientId: 'YOUR_PUBLIC_CLIENT_ID',
  scopes: ['api:access', 'profile:read']
});

The action itself should not know whether a person or an agent called it:

async function draftCaption({ product, tone, signal }) {
  if (typeof product !== 'string' || product.trim() === '') {
    throw new Error('Product facts are required.');
  }

  const response = await AiPass.generateCompletion({
    messages: [{
      role: 'user',
      content: `Write a ${tone} product caption using only these facts: ${product}`
    }],
    signal
  });

  const caption = response.choices[0].message.content;
  document.querySelector('#caption').value = caption;
  document.querySelector('#status').textContent = 'Caption ready';
  return { caption };
}

document.querySelector('#draft').addEventListener('click', async () => {
  await draftCaption({
    product: document.querySelector('#product').value,
    tone: document.querySelector('#tone').value
  });
});

The SDK opens the real AI Pass connection flow when the protected action needs it. The app does not pre-connect invisibly or store a provider API key.

Add one WebMCP tool

The tool calls the same function and keeps the result visible:

const tool = await AiPass.webMcp.registerTool({
  name: 'draft_product_caption',
  title: 'Draft product caption',
  description: 'Draft a product caption from supplied facts and put it in the editor.',
  inputSchema: {
    type: 'object',
    properties: {
      product: {
        type: 'string',
        description: 'Verified product facts to use in the caption.'
      },
      tone: {
        type: 'string',
        enum: ['plain', 'playful', 'premium']
      }
    },
    required: ['product', 'tone']
  },
  annotations: {
    readOnlyHint: false,
    untrustedContentHint: false
  },
  execute: ({ product, tone }, { signal }) =>
    draftCaption({ product, tone, signal })
}, {
  confirmation: ({ product }) => ({
    title: 'Draft this caption?',
    message: `Use your AI Pass wallet to draft a caption from: ${product}`,
    confirmLabel: 'Draft'
  })
});

if (!tool.supported) {
  console.info('WebMCP is unavailable; the Draft caption button still works.');
}

Do not mark this tool read-only. It spends wallet funds when it calls the model.

Let Codex test its own interface

OpenAI's WebMCP demo recommends dogfooding the tool set with Codex. The agent is the consumer of the names, descriptions, and schemas, so it can expose confusion that a browser unit test will miss.

Ask it to try prompts such as:

  • "Write a playful caption for these facts."
  • "Summarize the product but do not spend any credit."
  • "Publish this caption." when no publish tool exists
  • an ambiguous request that could mean drafting or editing

The expected behavior matters as much as the success case. Codex should choose draft_product_caption only when the user asks for a draft, ask for missing facts, and never invent a second tool.

For local WebMCP testing, Chrome documents a testing flag at chrome://flags/#enable-webmcp-testing. Its inspector extension can list tools, call them manually, and show structured results. Do not run a wallet-funded test until the user separately approves that specific spend.

Ship the app where it already belongs

AI Pass does not require its own hosting. The browser SDK works on an existing Vercel, Replit, Lovable, or private deployment once the exact callback origins are approved. AI Pass Spaces is available for a self-contained prototype when the user asks for it.

Before publishing, test the page with WebMCP disabled. The button must still work. Then enable WebMCP, inspect the one registered tool, decline the confirmation once, approve it once with explicit spending approval, and verify that both paths update the same textarea.