npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lark-apaas/nestjs-mcp

v0.1.1

Published

NestJS MCP Server module for Miaoda fullstack apps (Streamable HTTP + MCP Apps)

Downloads

2,992

Readme

@lark-apaas/nestjs-mcp

为妙搭全栈应用(NestJS)提供 MCP Server 能力:用装饰器把业务方法开放为 MCP 工具,SDK 负责协议、装配、入参校验、身份读取与清单导出,并将应用 Skills 暴露为标准 MCP Prompts,将交互界面暴露为 MCP Apps 资源。

  • 端点:POST /__innerapi__/mcp(Streamable HTTP 无状态模式,不含 SSE;协议版本由 @modelcontextprotocol/sdk 协商,当前 2025-11-25)。应用设置 CLIENT_BASE_PATH 时完整路径为 ${CLIENT_BASE_PATH}/__innerapi__/mcp
  • 底层:@modelcontextprotocol/sdk + @modelcontextprotocol/ext-apps(MCP Apps,ui:// 单文件 HTML 资源)
  • 身份:网关注入 x-larkgw-suda-webuser,UserContextMiddleware 解析到 req.userContext,工具通过 ctx.user 读取
  • 装配:已被 PlatformModule.forRoot() 自动引入,业务工程 import @lark-apaas/fullstack-nestjs-core 即获得

能力与目录

| 应用能力 | 定义方式 | 客户端发现与使用 | |---|---|---| | 工具(执行操作、查询数据) | @McpTools() + @McpTool() | tools/list → tools/call | | Skill(业务流程说明) | server/mcp/skills/<name>/SKILL.md | prompts/list → prompts/get | | Apps UI(交互界面,可选) | @McpUiResource() + 独立 HTML 入口 | 工具的 _meta.ui.resourceUri → resources/read |

server/mcp/
├── tools/order.tools.ts
├── skills/
│   ├── order-status/SKILL.md
│   └── order-review/SKILL.md
└── ui/                         # 可选,独立浏览器程序
    └── order-detail/index.html

工具与 UI 资源所在的类必须注册为已加载业务模块的单例 provider;Skill 文件自动发现,无需装饰器或 provider。一个 Skill 可以描述多个工具的使用流程,多个 Skill 也可以复用同一工具,不要求一一对应。

快速开始

1. 定义工具类

// server/mcp/tools/order.tools.ts
import { z } from 'zod';
import {
  McpTools, McpTool, McpToolError,
  type Infer, type McpContext, type McpToolResult,
} from '@lark-apaas/fullstack-nestjs-core';
import { OrderService } from '@server/modules/order/order.service';

export const GetOrderInput = { orderId: z.string().describe('订单 ID') };
export const GetOrderOutput = { orderId: z.string(), amount: z.number(), status: z.string() };

@McpTools({ prefix: 'order_' })
export class OrderMcpTools {
  constructor(private readonly orders: OrderService) {}

  @McpTool({
    title: '查询订单',
    description: '按订单 ID 查询订单金额与状态。用户询问某个订单的情况时调用。',
    inputSchema: GetOrderInput,
    outputSchema: GetOrderOutput,
    annotations: { readOnlyHint: true },
  })
  async get(input: Infer<typeof GetOrderInput>, ctx: McpContext): Promise<McpToolResult<typeof GetOrderOutput>> {
    const order = await this.orders.findVisibleTo(input.orderId, ctx.user.userId);
    if (!order) throw new McpToolError('订单不存在', { code: 'ORDER_NOT_FOUND' });
    return { structuredContent: order };
  }
}

2. 注册为所属业务模块的 provider

import { Module } from '@nestjs/common';
import { OrderService } from './order.service';
import { OrderMcpTools } from '@server/mcp/tools/order.tools';

@Module({
  providers: [OrderService, OrderMcpTools],
})
export class OrderModule {}

应用启动时 McpModule 自动发现已注册的工具类。已有业务模块无需额外在 app.module.ts 注册工具;新建业务模块仍需接入应用模块。启动日志会打印已注册的工具名。

3. 添加业务 Skill

创建 server/mcp/skills/order-status/SKILL.md:

---
name: order-status
description: 查询订单当前状态并向用户说明处理进度
---
先确认用户要查询的订单 ID,再调用 order_get。
根据工具返回的 status 和 amount 说明结果,不推测未返回的信息。
订单不存在或无权访问时告知用户,不重复尝试其他人的订单。

每个业务流程使用独立目录和固定文件名 SKILL.md。例如再添加 order-review/SKILL.md,就会发现第二份 Skill。它们提供给应用使用者的 Agent,与指导生成代码的 mcp-guide 不同。只有 Skill、没有工具或 UI 的应用也可连接 MCP。

4. 发现与调用

在已连接的标准 MCP 客户端中:

const { prompts } = await client.listPrompts();
// [{ name: 'order-status', description: '查询订单当前状态并向用户说明处理进度' }, ...]
const prompt = await client.getPrompt({ name: 'order-status' });
// prompt.messages[0].content.text 为去掉 YAML 前言的 Markdown 正文
const result = await client.callTool({
  name: 'order_get',
  arguments: { orderId: 'order_001' },
});

prompts/get 只返回使用说明,不会自动执行工具;后续由客户端或 Agent 按说明调用 tools/call。当前 Prompt 不声明参数,返回 description 和 messages: [{ role: "user", content: { type: "text", text: "Markdown 正文" } }]。Skill 不注册为 Resource,不使用 skill:// URI,也没有自定义 skills/list 方法。

以上方法均通过同一个 HTTP 端点发送 JSON-RPC 请求,例如 {"jsonrpc":"2.0","id":2,"method":"prompts/get","params":{"name":"order-status"}},没有 /mcp/prompts/get 等子路由。先完成 MCP 握手;业务调用的身份要求见下表。

约定

| 项目 | 约定 | |---|---| | 工具类位置 | server/mcp/tools/*.tools.ts | | 工具名 | prefix + 方法名(或 name 覆盖),需匹配 ^[A-Za-z0-9_.-]{1,128}$,全局唯一 | | 方法签名 | (input: Infer<typeof InputSchema>, ctx: McpContext) => McpToolResult<typeof OutputSchema> | | schema | zod(与 @modelcontextprotocol/sdk 一致),原始形状 { a: z.string() } 或 z.object({...}) 均可 | | 返回值 | 声明 outputSchema 时返回 { structuredContent }(SDK 自动补 content 文本);否则返回 { content: [...] } | | 业务错误 | 抛 McpToolError(message, { code?, data? }),原样返回给 Agent(data 会随结果返回,勿放敏感字段);其他异常记日志并返回通用失败提示 | | 身份 | 默认 requireUser: true:请求缺少用户身份时返回 MCP_USER_REQUIRED 错误,不执行业务代码 | | 权限 | 复用业务 Service 与数据库行级权限,工具层只负责把 ctx.user 传下去 |

Skill 文件与更新规则

| 项目 | 要求 | |---|---| | name | 建议与目录名相同(开发约定,运行时不强制);1–64 位小写字母、数字和连字符,不允许首尾连字符或连续 --;全局唯一 | | description | 非空字符串,最多 1024 个 UTF-16 代码单元;支持 YAML 引号和多行字符串 | | 文件 | 普通 UTF-8 文件;拒绝符号链接、路径越界、非法 UTF-8 和非普通文件 | | 数量与大小 | 最多 100 份,单文件最多 1 MiB,含前言的正文总量最多 8 MiB | | 错误处理 | 新目录必须有合法完整的 YAML 前言;重复键、别名引用等报错;不会静默返回部分列表 |

实际 Prompt 名称取 frontmatter 的 name,源码定位保留真实文件路径;仅修改 name 无需同步重命名目录或重启服务,下次请求即生效。

只发现 skills/ 下一级目录内的 SKILL.md,不加载附件;没有该文件的普通目录会被忽略。对外使用 skills 数组,每份 Skill 对应一个标准 Prompt。

每次 MCP 请求或平台目录查询都重新读取 Skill 文件,单次请求使用同一份快照;增改删在下一请求生效。工具定义来自本次应用启动的注册表,修改工具代码需要开发服务重载;生产修改需重新构建并发布。当前无主动变更通知,能力声明为 listChanged: false,客户端需主动重新获取列表或正文。

MCP Apps(可选)

当工具结果需要界面渲染(如订单详情卡片)时,声明一个 ui:// 资源并在工具上关联:

import { McpTools, McpTool, McpUiResource, readMcpUiTemplate } from '@lark-apaas/fullstack-nestjs-core';

// 在已注册的工具类中增加资源方法,并关联对应工具(以下省略业务实现)。
@McpTools({ prefix: 'order_' })
export class OrderMcpTools {
  @McpTool({ description: '…', inputSchema, outputSchema, ui: { resourceUri: 'ui://order/detail' } })
  async get(/* … */) { /* … */ }

  @McpUiResource({ uri: 'ui://order/detail', title: '订单详情' })
  detailView() {
    return readMcpUiTemplate('order-detail'); // 读取 dist/mcp-ui/order-detail.html
  }
}

界面源码放在 server/mcp/ui/order-detail/index.html(独立浏览器代码,使用 server/mcp/ui/tsconfig.json 检查;可用 React + @modelcontextprotocol/ext-apps 的 App / hooks),@lark-apaas/fullstack-vite-preset 和 @lark-apaas/coding-preset-vite-react 均会把每个入口打包为单文件 HTML:开发态输出 dist/mcp-ui/<entry>.html 并监听变更自动重建,生产构建直接输出 dist/dist/mcp-ui/<entry>.html。界面子构建只带 React 插件与 @shared 别名,不支持 styled-jsx 与 @server/*。目前仅 Vite 预设提供该构建,Rspack 预设不支持 MCP Apps。

模块配置

PlatformModule.forRoot({
  mcp: {
    serverName: 'my-app',            // 默认 SUDA_APP_ID,未设置时为 miaoda-app
    serverVersion: '1.0.0',          // 静态声明,不随应用修改或 SDK 升级自动变化
    requireUser: true,               // 默认:执行工具、获取 Prompt、读取资源需身份
    instructions: '…',               // initialize 响应中的说明
  },
});
// 关闭端点:PlatformModule.forRoot({ mcp: false })

端点与身份

这里列的是应用内部路径,设置 CLIENT_BASE_PATH 时加上该前缀。外部客户端使用平台提供的 MCP 连接地址与凭证,由网关转发到内部端点;不要把页面地址直接拼接内部路径作为公网连接地址。本地开发与沙箱开发使用相同协议和目录约定,不新增独立端口。

| 请求 | 响应 / 默认身份要求 | |---|---| | POST /__innerapi__/mcp:initialize、tools/list、resources/list、prompts/list | HTTP 200,标准 JSON-RPC 响应;SDK 不要求用户身份,列表需按已声明能力调用 | | 同端点 tools/call | 要求 ctx.user.userId;缺失返回 isError: true,错误码 MCP_USER_REQUIRED 在结果元数据中 | | 同端点 resources/read、prompts/get | 要求用户身份;缺失返回 JSON-RPC error | | 同端点 notifications/initialized | HTTP 202,空响应体 | | GET / DELETE MCP 端点 | HTTP 405,Allow: POST;不提供 SSE 流 | | POST 时工具、UI 资源和 Skill 均为空 | HTTP 404 | | POST 的 Accept 未同时包含 application/json 和 text/event-stream | HTTP 406;标准客户端会设置这两个值,即使服务端使用 JSON 响应 | | GET /__innerapi__/mcp/manifest | 平台目录接口,见下文;不需要 MCP 握手 |

tools/list、resources/list 仅在注册对应能力时可用,否则返回 JSON-RPC -32601 Method not found。客户端应根据 initialize 的 capabilities 调用;prompts/list 在服务可用时支持返回空数组。

平台负责凭证鉴权和可信身份注入;SDK 检查用户身份是否存在,并交给业务 Service 执行权限校验。工具类使用单例 provider,不承诺工具方法上的 Nest Guard / Pipe / Interceptor 自动执行,也不能依赖 HTTP Controller 上的权限装饰器。requireUser: false 可关闭 SDK 的身份存在性检查,不会关闭网关鉴权。

initialize.result.protocolVersion 是 MCP 协商版本,serverInfo.version 是模块配置的服务实现版本;下文的 version: 3 是平台清单结构版本。三者用途不同,均不能直接当作应用源码或 UI 内容更新标识。

导出

McpModule、McpTools、McpTool、McpUiResource、McpToolError、readMcpUiTemplate、 类型 McpContext / McpUser / McpToolOptions / McpToolResult / Infer / McpModuleOptions / McpCatalog / McpSkill / McpNamedSkill / McpSources / McpSourceLocation, 以及端点、清单和 Skill 常量。内部注册表与构建函数不作为包入口 API 导出。

应用修订与旧 UI 保护

应用修订通过 MCP 标准预留的 _meta 扩展传递,不改变 serverInfo.version、protocolVersion 或平台目录 version。键为 com.feishu.miaoda/appRevision,值是不透明字符串,只比较相等,不比较大小。此机制是妙搭扩展,普通 MCP 客户端不传该键仍可正常调用。

| 位置 | 含义 | |---|---| | 成功响应 result._meta["com.feishu.miaoda/appRevision"] | 当前请求使用的修订;包括 initialize、列表、工具、资源和 Prompt 响应 | | tools/call、resources/read、prompts/get 的 params._meta 同名键 | 调用方期望使用的修订,可选;提供时必须为 1–256 字符的字符串 |

宿主通过 tools/list(纯 Skill 应用可用 prompts/list)刷新修订,将应用/环境/用户、修订和资源 URI 纳入缓存键。旧 UI 发起请求时由 Host 附加原绑定值,不允许用查询到的新值替换它。示例:

const key = 'com.feishu.miaoda/appRevision';
const revision = (await client.listTools())._meta?.[key];
if (typeof revision === 'string') {
  const result = await client.callTool({
    name: 'order_get',
    arguments: { orderId: 'o_1' },
    _meta: { [key]: revision },
  });
}

不一致返回 HTTP 200 的 JSON-RPC error,不执行本次业务 handler:

{
  "jsonrpc": "2.0",
  "id": 2,
  "error": {
    "code": -32000,
    "message": "应用已更新,请重新调试;本次操作未执行",
    "data": {
      "code": "MCP_APP_REVISION_MISMATCH",
      "expectedRevision": "old",
      "actualRevision": "new"
    }
  }
}

Host 按 error.data.code 处理,保留旧结果、禁用旧 UI 并提示重新调试;不得自动重放可能产生副作用的调用。非法修订参数返回 -32602。此检查不是鉴权,原有身份校验仍然生效。

  • 开发态:发现已注册的工具、资源或 Skill 后,在 Nest 启动完成前,对实际 Node 入口所在的后端编译产物与依赖配置生成固定指纹,再结合已构建 UI 内容与 Skill 内容计算 SHA-256。相同产物和依赖重启不变;后端更新在新进程生效后变化,不因尚未生效的源码编辑提前变化。本地与沙箱规则一致。默认识别 dist/server + dist/shared 或扁平 dist 布局,排除客户端、MCP UI、source map 和临时文件;UI/Skill 仍按运行快照独立更新。此规则适用于编译完成后启动、依赖按锁文件安装的标准工程;不跟踪手工替换 node_modules 或运行期间自行热替换后端模块。非标准入口(如直接运行 TypeScript)请显式提供与运行代码对应的 appBuildId;无法识别时不返回修订,不伪造固定版本。
  • 发布态:SDK 在初始化时读取 veFaaS 注入的 _FAAS_FUNC_ID 与正整数 _FAAS_REVISION_NUMBER,结合 UI/Skill 内容形成修订。同一发布 revision 跨实例、重启保持一致,新后端发布(含依赖变更)更新;仅重新构建但未部署不会影响运行版本。无需模板写入构建 ID 文件。其他部署平台通过 PlatformModule.forRoot({ mcp: { appBuildId: 'immutable-release-id' } }) 显式提供不可变版本。
  • 版本缺失:缺少有效平台版本且未配置 appBuildId 时不返回修订,普通 MCP 仍可用;绑定调用返回 -32000、data.code=MCP_APP_REVISION_UNAVAILABLE,不允许伪造固定版本。
  • 按需初始化:无 MCP 能力时不扫描后端编译产物。纯 Skill 应用也在启动时固定后端指纹。开发进程启动后才新增首份 Skill,标准 Prompt 可直接使用,但须重启开发服务才能获得自动修订;未固定后端快照前不返回绑定版本。
  • 一致性边界:MCP 请求中的 readMcpUiTemplate() 复用该请求的 HTML 快照,避免修订与读取内容错配。仅涵盖固定 UI 产物及 Skill;动态业务数据不是代码修订。已经开始的请求可完成,保留开始时的修订;不撤销已发生的副作用,也不承诺后端/UI/Skill 在开发态整体原子切换或滚动发布期间全局最新版本校验。
  • 更新发现:当前不提供 SSE 主动通知;Host 在重新调试、恢复对话、交互前刷新列表,或结合平台变更事件刷新。URI 不变不代表内容没变,不能仅按 URI 缓存。

依据:MCP _meta 扩展规则。

平台定义查询

GET ${CLIENT_BASE_PATH}/__innerapi__/mcp/manifest 返回完整平台目录:

interface Catalog {
  version: number;       // 当前为 3,平台响应结构版本
  generatedAt: string;   // 本次响应生成时间,UTC
  endpoint: string;      // /__innerapi__/mcp,相对应用根
  tools: Tool[];
  resources: Resource[];
  prompts: Prompt[];
  skills: Array<{ name: string; description: string; path: string; content: string }>;
  sources: {
    tools: Record<string, { path: string; line?: number }>;
    resources: Record<string, { path: string; line?: number }>;
    prompts: Record<string, { path: string; line?: number }>;
  };
}

tools、resources、prompts 完整复用标准 MCP 列表定义;UI 关联仍为工具 _meta.ui.resourceUri。平台目录 v3 的 skills 保存 { name, description, path, content },content 是含 frontmatter 的完整源文件,供面板展示;标准 Prompt 获取正文时去掉前言。sources.tools 按工具名、sources.resources 按 UI URI、sources.prompts 按 Prompt 名提供源码定位,Skill 也可直接使用 skills[].path。未配置时数组为空,读取失败不能伪装为空。generatedAt 是响应时间,不代表源码版本;平台负责工程版本匹配。

面板无需 MCP 握手;这是平台自定义 HTTP 接口,其外层结构不是 MCP 标准方法的返回值。接口不读写 .spark/mcp/manifest.json,每次读取 Skill 正文,返回 Cache-Control: no-store。空应用仍返回完整结构和空值;模块关闭为404,未就绪503,定义生成或文件读取失败500(RFC9457)。入口访问权限由平台负责。

开发检查

应用启动时自动检查工具和资源定义,包括名称格式、重名及 UI 资源引用;定义错误会阻止启动。修改代码后,确认应用成功重启,再通过平台目录接口和标准 MCP 客户端验证当前能力。Skill 文件在读取时校验;类型检查、lint 与生产构建仍使用工程现有命令。

SDK 不提供校验 CLI,也不生成离线 manifest 文件;能力目录以运行中的接口为准。

构建与交付

生产运行时从 cwd/server/mcp 读取 Skills、从 cwd/dist/mcp-ui 读取 UI。NRF 统一以 dist 为发布根,以下路径均相对构建前的工程根目录:

| 交付根 / 运行 cwd | 启动命令 | Skill 目录 | UI 目录 | |---|---|---|---| | dist | node server/main.js | dist/server/mcp/skills | dist/dist/mcp-ui |

只复制 skills/<name>/SKILL.md,不复制附件。清理删除项时保留相邻工具编译产物。无需 UI 也交付 Skills;自定义构建须满足相同的运行目录约定。

| 构建预设 | Skill 交付 | MCP UI 单文件构建与交付 | |---|---|---| | @lark-apaas/fullstack-vite-preset | 支持 | 支持 | | @lark-apaas/coding-preset-vite-react | 支持 | 支持 | | @lark-apaas/fullstack-rspack-preset | 支持 | 不支持 |

Vite 开发态监听 UI / shared 变更并重建;生产发布前需完成构建,确保 Skill 文件和 UI 产物进入运行目录。

MCP UI 编译与依赖边界

Skills、Tools、UI 统一放在 server/mcp/,但 ui/ 是 iframe 内的浏览器程序。开通 UI 时,保留原配置并向 tsconfig.node.json 的 exclude 追加 server/mcp/ui;服务端不能 import UI 源码,只能通过 readMcpUiTemplate 读取构建产物。

主工程保留原 client/server 检查。Agent 从工程根执行 npx --no-install tsc --noEmit --project server/mcp/ui/tsconfig.json,再执行 npx --no-install eslint --config server/mcp/ui/eslint.config.cjs "server/mcp/ui/**/*.{ts,tsx,js,jsx}" --max-warnings 0;必须检查退出码并修复错误。无需辅助脚本或新增 npm 命令。

CLI 不注入 MCP UI 检查命令;主工程保留原有 client/server 检查,UI 由 Agent 按独立配置执行标准 tsc/eslint。

创建首个 UI 时一并生成以下两个文件,不要为没有 UI 的应用创建配置。

server/mcp/ui/tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "jsx": "react-jsx",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "types": [],
    "strict": true,
    "allowJs": true,
    "checkJs": true,
    "skipLibCheck": true,
    "noEmit": true,
    "baseUrl": "../../..",
    "paths": { "@shared/*": ["shared/*"] }
  },
  "include": ["./**/*"],
  "exclude": ["node_modules", "dist", "eslint.config.cjs"]
}

server/mcp/ui/eslint.config.cjs:

module.exports = require('@lark-apaas/fullstack-presets/lib/mcp/eslint-ui');

主应用 ESLint 的 UI 排除由平台 ESLint preset 提供,CLI 不改写应用的 ESLint 配置。存量 tsconfig.node.json 已显式声明 exclude 时,sync 只追加 server/mcp/ui;缺少 exclude 时保留原配置并提示人工处理,避免覆盖继承语义。

主工程 lint 成功不代表 UI 已检查;Agent 必须额外执行上述 UI 检查与构建。TS/ESLint 不检查 HTML 内联脚本,交互代码应放独立 TS/TSX 文件。静态检查不能替代真实宿主内的渲染验收。

两套 Vite 预设均校验实际依赖:只允许 UI 目录、shared 和浏览器适用的 npm 依赖;禁止直接或间接引用 client 业务代码、工具、服务端实现或 Node.js 模块。可复用的纯展示组件放 shared;它们也不得依赖应用 Provider、Router 或登录态。构建检查不能证明第三方组件在所有宿主中可用,仍需 iframe 验收。

子构建不加载主应用 Vite 配置、环境文件、public 目录或 PostCSS 配置。UI 数据交互使用 MCP Apps 宿主通信,不默认访问主应用的同源 API。ui://、工具的 _meta.ui.resourceUri、HTTP 端点及 HTML 输出路径不变。

日志

复用框架 Nest Logger:开发态写入本地服务日志,发布态通过既有 Observable 链路上报,模块为 mcp,source_type=platform,不新增指标或事件埋点。全栈应用无需额外配置;单独使用 McpModule 时需自行接入 Nest Logger 的日志后端。

每个到达 MCP 控制器的请求记录一次完成日志,包含 MCP 方法、工具名/资源 URI/Prompt 名、JSON-RPC 请求 ID、HTTP 状态和耗时。框架自动关联 trace、应用与用户上下文。HTTP 错误、JSON-RPC error、工具 isError: true 均记为失败,提前断开记为 aborted;平台目录查询也记录结果。

不记录原始请求参数、请求头、工具结果、UI HTML 或 Skill 正文;资源 URI 去除查询参数、fragment 和 userinfo。未预期异常记录服务端错误信息和堆栈,业务代码应避免在异常消息中包含凭证或敏感数据。日志接入不改变 MCP 响应协议。