Skip to main content
import { registerAppTool } from "sunpeak/mcp";

Overview

registerAppTool is a convenience wrapper around the MCP SDK’s server.registerTool() that normalizes UI metadata for compatibility across hosts. Use it instead of registerTool when your tool should render a View.

Signature

function registerAppTool(
  server: McpServer,
  name: string,
  config: McpUiAppToolConfig,
  cb: ToolCallback,
): RegisteredTool

Parameters

server
McpServer
required
The MCP server instance.
name
string
required
Tool name/identifier.
config
McpUiAppToolConfig
required
Tool configuration. Extends the standard MCP ToolConfig with a required _meta field containing UI metadata.
title
string
Human-readable tool title.
description
string
Tool description for the model.
inputSchema
ZodRawShape | AnySchema
Zod schema or JSON Schema for tool arguments.
outputSchema
ZodRawShape | AnySchema
Zod schema or JSON Schema for tool output.
annotations
ToolAnnotations
MCP tool annotations (e.g., readOnlyHint).
_meta
object
required
UI metadata linking this tool to a resource.
ui
McpUiToolMeta
required
resourceUri
string
URI of the UI resource to display (e.g., "ui://weather/view.html").
visibility
McpUiToolVisibility[]
Who can access this tool. Default: ["model", "app"].
cb
ToolCallback
required
Tool handler function. Receives parsed arguments and returns a CallToolResult.

Usage

Basic tool with UI

import { registerAppTool } from "sunpeak/mcp";
import { z } from "zod";

registerAppTool(
  server,
  "get-weather",
  {
    title: "Get Weather",
    description: "Get current weather for a location",
    inputSchema: { location: z.string() },
    _meta: {
      ui: { resourceUri: "ui://weather/view.html" },
    },
  },
  async (args) => {
    const weather = await fetchWeather(args.location);
    return { content: [{ type: "text", text: JSON.stringify(weather) }] };
  },
);

App-only tool (hidden from model)

Set visibility: ["app"] for tools that should only be callable from the View — useful for polling, pagination, or UI-driven server interactions:
registerAppTool(
  server,
  "update-quantity",
  {
    description: "Update item quantity in cart",
    inputSchema: { itemId: z.string(), quantity: z.number() },
    _meta: {
      ui: {
        resourceUri: "ui://shop/cart.html",
        visibility: ["app"],
      },
    },
  },
  async ({ itemId, quantity }) => {
    const cart = await updateCartItem(itemId, quantity);
    return { content: [{ type: "text", text: JSON.stringify(cart) }] };
  },
);

Model-only tool

Set visibility: ["model"] for tools that the model can call but the View cannot:
registerAppTool(
  server,
  "show-cart",
  {
    description: "Display the user's shopping cart",
    _meta: {
      ui: {
        resourceUri: "ui://shop/cart.html",
        visibility: ["model"],
      },
    },
  },
  async () => {
    const cart = await getCart();
    return { content: [{ type: "text", text: JSON.stringify(cart) }] };
  },
);

Metadata Normalization

registerAppTool automatically normalizes between the current format (_meta.ui.resourceUri) and the deprecated flat key (_meta["ui/resourceUri"]) for backward compatibility with older hosts.