For AI agents: the complete documentation index is available at /zh/llms.txt, the full documentation bundle is available at /zh/llms-full.txt, and this page is available as Markdown at /zh/guides/advanced-features/mcp-apps/interactions.md.
  • 简体中文
  • 卡片交互

    卡片除了展示工具结果,还能调用服务端工具、向宿主对话发送消息、同步模型上下文。Modern.js 为应用内组件和 MF 组件注入已连接的 mcpApp,它是 MCP Apps SDK 的 App 实例,两种模式使用相同接口。

    如何获取 mcpApp

    组件从 props 接收 mcpApp,类型通过 import type { App } from '@modern-js/mcp-apps/react' 获取。模板将该属性声明为可选,组件应在连接不可用时禁用操作。复用注入的实例,无须在卡片内再次调用 useApp() 或创建 new App()。

    需求方法结果
    按钮点击后查询、计算或提交业务操作callServerTool()返回指定工具的执行结果,由卡片处理并更新状态
    让用户从卡片向对话发起后续请求sendMessage()请求宿主将消息加入对话;后续模型响应由宿主处理
    让模型了解当前选择或筛选条件updateModelContext()更新后续对话可用的上下文,不立即触发模型回复
    打开外部页面openLink()请求宿主打开链接
    切换卡片展示方式requestDisplayMode()请求切换模式,以宿主实际返回的模式为准
    向宿主发送调试日志sendLog()发送日志,不向对话发送消息

    callServerTool:调用业务工具

    使用 mcp_apps.ts 中的工具名和符合 inputSchema 的参数。请求经宿主转发到 MCP 服务端,无须在卡片中拼接 /mcp URL。下面的组件适用于两种 UI 模式:

    src/components/Sum.tsx
    import type { App } from '@modern-js/mcp-apps/react';
    import { useState } from 'react';
    
    export default function Sum({ sum, mcpApp }: { sum?: number; mcpApp?: App }) {
      const [value, setValue] = useState<number>();
      const [pending, setPending] = useState(false);
      const [error, setError] = useState<string>();
    
      async function calculate() {
        if (!mcpApp) return;
        setPending(true);
        setError(undefined);
        try {
          const result = await mcpApp.callServerTool({
            name: 'add_numbers',
            arguments: { a: 2, b: 3 },
          });
          if (result.isError) throw new Error('Calculation failed');
          const output = result.structuredContent;
          if (
            !output ||
            typeof output !== 'object' ||
            !('sum' in output) ||
            typeof output.sum !== 'number'
          ) {
            throw new Error('Invalid calculation result');
          }
          setValue(output.sum);
        } catch (error) {
          setError(error instanceof Error ? error.message : 'Request failed');
        } finally {
          setPending(false);
        }
      }
    
      return (
        <section>
          <p>{value ?? sum ?? 'Ready'}</p>
          <button type="button" disabled={!mcpApp || pending} onClick={calculate}>
            {pending ? 'Calculating…' : 'Calculate 2 + 3'}
          </button>
          {error && <p role="alert">{error}</p>}
        </section>
      );
    }

    callServerTool() 返回的结果由当前交互代码消费。使用返回值更新 React 状态,不依赖宿主再次发送 ontoolresult 来刷新界面。业务失败可能返回 isError: true;连接、协议等异常可能抛出错误,两类情况都要处理。structuredContent 的类型是 unknown,读取前应检查业务数据结构。

    工具需要允许 App 调用。visibility 默认是 ['model', 'app'];设置为 ['app'] 可表达仅供卡片调用的工具,实际可见性由宿主遵循,不能代替服务端鉴权。

    sendMessage:向对话发送后续请求

    适合“解释这条结果”“根据当前选择继续分析”等由用户点击触发的操作。下面的函数可在按钮事件中调用,并将异常显示在卡片上:

    import type { App } from '@modern-js/mcp-apps/react';
    
    export async function askForExplanation(mcpApp: App, sum: number) {
      if (!mcpApp.getHostCapabilities()?.message?.text) {
        throw new Error('This host does not support text messages from cards');
      }
      const result = await mcpApp.sendMessage({
        role: 'user',
        content: [{ type: 'text', text: `Explain the calculation result: ${sum}` }],
      });
      if (result.isError) throw new Error('The host rejected the message');
    }

    它发送的是对话消息,不保证调用某个指定工具,也不直接返回模型回复。需要确定地执行某个业务工具时使用 callServerTool();需要模型理解用户意图并继续对话时使用 sendMessage()。避免在组件渲染或挂载时自动发送,造成重复消息。

    updateModelContext:同步卡片状态

    用户修改筛选条件或选中条目后,可以将当前状态提供给模型,供后续对话使用:

    import type { App } from '@modern-js/mcp-apps/react';
    
    export async function syncSelection(mcpApp: App, selectedIds: string[]) {
      if (!mcpApp.getHostCapabilities()?.updateModelContext?.structuredContent)
        return;
      await mcpApp.updateModelContext({
        structuredContent: { selectedIds },
      });
    }

    此调用不会立即触发模型回复;后一次更新替换当前卡片此前提供的上下文,所以应提交希望保留的完整当前状态。需要随后发起分析时,可由用户操作触发 sendMessage()。调用方仍需处理请求失败。

    其他能力与宿主差异

    • 使用 mcpApp.openLink({ url: 'https://example.com' }) 请求宿主打开外部页面,并处理返回的 isError 和异常。
    • 通过 mcpApp.getHostContext()?.availableDisplayModes 检查可用模式,再调用 requestDisplayMode({ mode: 'fullscreen' }),根据返回的 mode 更新界面。
    • 使用 sendLog({ level: 'info', data: 'Calculation completed' }) 发送调试日志;是否展示或记录由宿主决定。

    宿主能力并不完全一致。连接后通过 getHostCapabilities() 和 getHostContext() 检查支持情况;不支持的操作应隐藏、禁用或提供替代行为。新增交互后,在目标宿主中验证成功、拒绝和失败路径,本地协议脚本不会覆盖这些卡片到宿主的调用。

    完整方法和参数见 MCP Apps SDK App API,宿主连接步骤见创建项目。