Skip to content

Repository files navigation

Paper + WebMCP

Available for Paper partners. Contact us if you'd like to discuss.

Set up

  1. Chrome 150+ is required
  2. You must opt into the WebMCP origin trial
  3. Paper must allow your origin to use our WebMCP tools
  4. Paper must allow your origin to access our auth domain

Wire up Paper + WebMCP

  1. Embed Paper using an iframe with the required permissions
<iframe
  src="https://app.paper.design"
  allow="tools; clipboard-read; clipboard-write; local-fonts"
></iframe>
  1. WebMCP tools are available after it's successfully loaded
const tools = await document.modelContext.getTools({
  fromOrigins: ["https://app.paper.design"],
});
const tool = tools.find((t) => t.name === "get_basic_info");

ctx.executeTool(tool, "{}");
  1. Wire up the tools to your agent library of choice
"use client";

import { useChat } from "@ai-sdk/react";
import {
  DefaultChatTransport,
  lastAssistantMessageIsCompleteWithToolCalls,
} from "ai";

const tools = await document.modelContext.getTools({
  fromOrigins: ["https://app.paper.design"],
});
const toolsByName = new Map(tools.map((t) => [t.name, t]));

const { messages, sendMessage, addToolResult } = useChat({
  transport: new DefaultChatTransport({
    api: "/api/chat",
    // 4. Send tool schemas to the server with each request
    prepareSendMessagesRequest: ({ body, messages }) => ({
      body: {
        ...body,
        messages,
        tools: tools.map(({ name, description, inputSchema }) => ({
          name,
          description,
          inputSchema,
        })),
      },
    }),
  }),
  sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,
  // 6. Tool calls come back down and run here, in the browser
  async onToolCall({ toolCall }) {
    const output = await document.modelContext.executeTool(
      toolsByName.get(toolCall.toolName),
      JSON.stringify(toolCall.input ?? {}),
    );
    addToolResult({
      tool: toolCall.toolName,
      toolCallId: toolCall.toolCallId,
      output,
    });
  },
});
import { anthropic } from "@ai-sdk/anthropic";
import { streamText, tool, jsonSchema, convertToModelMessages } from "ai";

export async function POST(req: Request) {
  const { messages, tools } = await req.json();

  const result = streamText({
    model: anthropic("claude-haiku-4-5"),
    messages: convertToModelMessages(messages),
    // 5. No `execute` → calls are streamed back to the browser's onToolCall
    tools: Object.fromEntries(
      tools.map((t) => [t.name, tool({ description: t.description, inputSchema: jsonSchema(t.inputSchema) })]),
    ),
  });

  return result.toUIMessageStreamResponse();
}

About

An example harness implementation utilizing Paper's WebMCP integration through an iframe.

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages