> For AI agents: the complete documentation index is available at /zh/llms.txt, the full documentation bundle is available at /zh/llms-full.txt.

# 接入自建 MCP Server

如果已经维护 MCP Server，可以让 Modern.js 只负责构建 React 卡片。业务工具、鉴权和 MCP 协议服务继续由已有服务管理，通过 UI manifest 关联工具与构建产物。

## 配置纯 UI 构建

在 `modern.config.ts` 中启用纯 UI 模式，无需安装或启用 BFF 插件。此模式关闭默认页面入口扫描，因此无需创建首页：

```ts
import { appTools, defineConfig } from '@modern-js/app-tools';
import { mcpAppsPlugin } from '@modern-js/plugin-mcp-apps';

export default defineConfig({
  plugins: [appTools(), mcpAppsPlugin({ mode: 'ui-only' })],
  output: { assetPrefix: 'https://cdn.example.com/' },
});
```

使用 `mcp_apps.ts` 声明 UI，避免导入服务端 handler：

```ts
import { defineMcpUi } from '@modern-js/mcp-apps/config';

export default defineMcpUi({
  tools: [{ name: 'greet', view: { module: './src/components/Greeting.tsx' } }],
});
```

运行 `modern build` 后，产物包含 `dist/mcp-apps/ui-manifest.json` 和 `dist/mcp-apps/ui/`。将 JS、CSS 等静态产物发布到 `assetPrefix` 对应的地址，将 manifest 和 HTML 交给 MCP Server。开发模式的 manifest 位于 `<internalDirectory>/mcp-apps/ui-manifest.json`。

## 构建产物与工具映射

manifest 的 `version` 当前为 `1`，`tools` 按工具名索引，每项包含 `resourceUri`、`mimeType`、`html`、`assetBase` 和 `ui` 元数据。`html` 相对于 manifest 所在目录解析，显式指定的 HTTPS HTML 地址保持原样。manifest 仅描述本地视图，不包含业务 handler、源模块路径或 MF remote。MF 继续使用标准 MF manifest 和 expose 配置。

```json
{
  "version": 1,
  "tools": {
    "greet": {
      "resourceUri": "ui://app/greet",
      "mimeType": "text/html;profile=mcp-app",
      "html": "./ui/mcp-ui-231bf89d7268.html",
      "assetBase": "https://cdn.example.com/",
      "ui": {
        "csp": { "connectDomains": [], "resourceDomains": [] },
        "prefersBorder": true
      }
    }
  }
}
```

通过 `tools.greet.html` 查找文件即可，无需自行计算 hash 文件名。部署时保留 manifest 与 HTML 的相对目录关系。读取辅助函数会根据公开资源地址补充 CSP，下面示例展示具体接入方式。

## 接入已有 Node MCP Server

下面的函数为已有官方 MCP SDK `Server` 注册一个工具及 UI 资源。它使用与仓库一致的 `setRequestHandler` API。已有工具较多时，将这些分支合入现有处理器，避免覆盖原有处理器。

```ts
import { readFile } from 'node:fs/promises';
import path from 'node:path';
import {
  createMcpViewResult,
  readUiManifestResource,
  type McpUiManifest,
} from '@modern-js/mcp-apps/server';

const directory = path.resolve('dist/mcp-apps');
const manifest: McpUiManifest = JSON.parse(
  await readFile(path.join(directory, 'ui-manifest.json'), 'utf8'),
);
if (manifest.version !== 1) throw new Error('Unsupported UI manifest version');
const view = manifest.tools.greet;

export function registerGreeting(server) {
  server.setRequestHandler('tools/list', async () => ({
    tools: [
      {
        name: 'greet',
        description: 'Greet someone',
        inputSchema: {
          type: 'object',
          properties: { name: { type: 'string' } },
          required: ['name'],
          additionalProperties: false,
        },
        _meta: { ui: { resourceUri: view.resourceUri } },
      },
    ],
  }));
  server.setRequestHandler('resources/list', async () => ({
    resources: [
      {
        name: 'greet',
        uri: view.resourceUri,
        mimeType: view.mimeType,
        _meta: { ui: view.ui },
      },
    ],
  }));
  server.setRequestHandler('resources/read', async ({ params }) => {
    if (params.uri !== view.resourceUri) throw new Error('Unknown UI resource');
    return {
      contents: [
        await readUiManifestResource(manifest, 'greet', {
          directory,
          assetBase: 'https://cdn.example.com/',
        }),
      ],
    };
  });
  server.setRequestHandler('tools/call', async ({ params }) => {
    const name = params.arguments?.name;
    if (params.name !== 'greet' || typeof name !== 'string') {
      throw new Error('Invalid tool call');
    }
    const message = `Hello, ${name}!`;
    return createMcpViewResult(
      { name },
      {
        content: [{ type: 'text', text: message }],
        viewProps: { message },
      },
    );
  });
}
```

`readUiManifestResource()` 读取 HTML、注入静态资源的 base 地址，并将资源来源补充到 CSP。默认的 `assetBase: 'request'` 需要调用方显式传入公开地址，独立 Server 的 origin 不一定是 UI 的静态资源地址。

`createMcpViewResult(args, result)` 保留业务结果，将工具参数写入 `_meta['modernjs.dev/mcp-app'].args`，将视图参数写入 `_meta['modernjs.dev/mcp-app'].viewProps`。`createMcpView()` 按工具输入、结果 args、viewProps 的顺序合并 props，并注入 `mcpApp`。

非 Node Server 可以直接读取 JSON manifest，返回相同的 UI 资源和工具结果结构。若自行返回 HTML，也需要处理 base 地址和 CSP，确保脚本及样式可以加载。

集成模式同样输出 manifest，生产 BFF 优先使用其中的 HTML 映射。开发首次编译前和缺少 manifest 的旧产物继续使用约定路径绑定。

使用 `ui-only` 时也可以打开开发调试页，先模拟参数和 viewProps 预览本地卡片，或切换到 Live server 连接自建 MCP Server。详见[MCP Apps Inspector](/zh/guides/advanced-features/mcp-apps/inspector.md)。
