Skip to content

Conversation

@ochafik
Copy link
Collaborator

@ochafik ochafik commented Dec 3, 2025

Summary

This PR enables Apps to register their own tools that agents can call, making apps introspectable and accessible to the model without DOM parsing.

Apps expose semantic interfaces (state queries, operations) via standard MCP tools. The agent discovers capabilities via tools/list, queries state, and drives interactions.

This is a different model from other approaches where apps keep the model informed through side channels (e.g., OAI Apps SDK sending widget state changes to the model, MCP-UI adding tool call results to chat history). Instead, the agent actively queries app state and executes operations through tools.

Example:

// Agent can query structured state
app.registerTool("get_board_state", {
  outputSchema: z.object({
    board: z.array(z.enum(['X', 'O', null])),
    currentPlayer: z.enum(['X', 'O']),
    winner: z.enum(['X', 'O', 'draw', null]).nullable()
  })
}, async () => ({
  structuredContent: { board, currentPlayer, winner: checkWinner(board) }
}));

// Agent can execute actions
app.registerTool("make_move", {
  inputSchema: z.object({ position: z.number().int().min(0).max(8) })
}, async ({ position }) => {
  board[position] = currentPlayer;
  currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
  return { structuredContent: { board, winner: checkWinner(board) } };
});

Agent interaction:

// Discover → Query → Act
const { tools } = await bridge.sendListTools({});
const state = await bridge.sendCallTool({ name: "get_board_state" });
await bridge.sendCallTool({ name: "make_move", arguments: { position: 4 } });

Changes

App side (app.ts)

  • registerTool() - Register tools with Zod validation
  • oncalltool / onlisttools - Handle tool requests
  • sendToolListChanged() - Notify on tool updates
  • Tools support enable(), disable(), update(), remove()

Host side (app-bridge.ts)

  • sendCallTool() - Call app tools
  • sendListTools() - List app tools
  • Fix: Use correct ListToolsResultSchema

Capabilities (types.ts)

  • Apps: tools: { listChanged?: boolean }
  • Hosts: serverTools: { listChanged?: boolean } (existing)

Tests: ✓ 27 passing, 100% coverage

Design

Reuses standard MCP messages: tools/call, tools/list, notifications/tools/list_changed

Similar to WebMCP but without turning the App (embedded page) into an MCP server - apps register tools within the App/Host architecture.

Lifecycle: App tools exist only while app loaded (ephemeral, sandboxed)

Separation: Server tools (persistent, trusted) vs App tools (ephemeral, sandboxed)

Breaking Changes

None. Purely additive.

Related

Implements the gist of #35 (WebMCP-style tool registration) while preserving the App/Host architecture.

🤖 Generated with Claude Code

ochafik and others added 2 commits December 3, 2025 00:50
This PR adds comprehensive tool support for MCP Apps, enabling apps
to register their own tools and handle tool calls from the host.

## Changes

### App (Guest UI) side:
- Add `registerTool()` method for registering tools with input/output schemas
- Add `oncalltool` setter for handling tool call requests from host
- Add `onlisttools` setter for handling tool list requests from host
- Add `sendToolListChanged()` for notifying host of tool updates
- Registered tools support enable/disable/update/remove operations

### AppBridge (Host) side:
- Add `sendCallTool()` method for calling tools on the app
- Add `sendListTools()` method for listing available app tools
- Fix: Use correct ListToolsResultSchema (was ListToolsRequestSchema)

### Tests:
- Add comprehensive tests for tool registration lifecycle
- Add tests for input/output schema validation
- Add tests for bidirectional tool call communication
- Add tests for tool list change notifications
- All 27 tests passing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@ochafik ochafik changed the title feat: Add tool registration and bidirectional tool support feat: Make Apps bidirectional with tool registration support Dec 3, 2025
@ochafik ochafik changed the title feat: Make Apps bidirectional with tool registration support feat: Add tool registration for Apps Dec 3, 2025
@ochafik ochafik marked this pull request as draft December 3, 2025 00:18
@ochafik ochafik changed the title feat: Add tool registration for Apps feat: Add tool registration for Apps (WebMCP-style!) Dec 3, 2025
@ochafik ochafik changed the title feat: Add tool registration for Apps (WebMCP-style!) spec: Add tool registration for Apps, to be called by Host (WebMCP-style!) Dec 3, 2025
@ochafik ochafik marked this pull request as ready for review December 3, 2025 00:53
ochafik and others added 2 commits December 3, 2025 16:55
Implement automatic `oncalltool` and `onlisttools` handlers that are
initialized when apps register tools. This removes the need for manual
handler setup and ensures tools work seamlessly out of the box.

- Add automatic `oncalltool` handler that routes calls to registered tools
- Add automatic `onlisttools` handler that returns full Tool objects with JSON schemas
- Convert Zod schemas to MCP-compliant JSON Schema using `zod-to-json-schema`
- Add 27 comprehensive tests covering automatic handlers and tool lifecycle
- Test coverage includes error handling, schema validation, and multi-app isolation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
const tool2 = app.registerTool("tool2", {}, async (_args: any) => ({
content: [],
}));
const tool3 = app.registerTool("tool3", {}, async (_args: any) => ({
const appCapabilities = { tools: { listChanged: true } };
app = new App(testAppInfo, appCapabilities, { autoResize: false });

const tool1 = app.registerTool(
ochafik and others added 2 commits December 3, 2025 17:22
- Always return inputSchema as object (never undefined)
- Keep filter for enabled tools only in list
- Update test to match behavior (only enabled tools in list)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants