@lark-apaas/coding-steering 0.1.68 → 0.1.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "@lark-apaas/coding-steering",
3
- "version": "0.1.68",
3
+ "version": "0.1.69",
4
4
  "description": "Stack-specific steering content for miaoda-coding templates",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "steering"
8
8
  ],
9
+ "scripts": {
10
+ "lint:md": "markdownlint 'steering/**/*.md' --ignore 'steering/**/skills/**' --ignore 'steering/**/skills_common/**' --ignore 'steering/**/skills_local/**'"
11
+ },
9
12
  "devDependencies": {
10
13
  "markdownlint-cli": "^0.47.0"
11
14
  },
@@ -17,8 +20,5 @@
17
20
  "miaoda",
18
21
  "coding-steering"
19
22
  ],
20
- "license": "MIT",
21
- "scripts": {
22
- "lint:md": "markdownlint 'steering/**/*.md' --ignore 'steering/**/skills/**' --ignore 'steering/**/skills_common/**' --ignore 'steering/**/skills_local/**'"
23
- }
24
- }
23
+ "license": "MIT"
24
+ }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: mcp-guide
3
- description: "Use when 用户要把应用能力开放给外部 Agent / 工作伙伴 / MCP 客户端调用,新增或修改 MCP 工具,给工具结果加界面(MCP Apps / ui:// 资源),维护应用 Skill(server/mcp/skills/<name>/SKILL.md,SDK 注册为 MCP Prompt),要在开发态核对、调用、调试 MCP 工具,或要把本应用的 MCP 接到自己的 Agent 客户端上使用。触发词:MCP, MCP Server, MCP 工具, MCP Apps, ui://, @McpTools, @McpTool, @McpUiResource, McpToolError, readMcpUiTemplate, tools/list, tools/call, resources/read, prompts/list, prompts/get, mcp_inspect, 开放给 Agent, 工作伙伴调用, Agent 调用应用, 应用 Skill, server/mcp, server/mcp/ui, server/mcp/skills, 接入 MCP, 连接 MCP, 怎么用这个 MCP, MCP 客户端, Claude Code, Codex, mcp add, mcp login, config.toml, X-Mcp-Token, MCP 凭证, 可用范围, 连不上。不负责 HTTP 对外开放接口(走 openapi-guide)、自动化触发器(走 trigger-guide)。"
3
+ description: "Use when 用户要把应用能力开放给外部 Agent / 工作伙伴 / MCP 客户端调用,新增或修改 MCP 工具,给工具结果加界面(MCP Apps / ui:// 资源),维护应用 Skill(server/mcp/skills/<name>/SKILL.md,SDK 注册为 MCP Prompt),要在开发态核对、调用、调试 MCP 工具,或要把本应用的 MCP 接到自己的 Agent 客户端上使用。触发词:MCP, MCP Server, MCP 工具, MCP Apps, ui://, @McpTools, @McpTool, @McpUiResource, McpToolError, readMcpUiTemplate, tools/list, tools/call, resources/read, prompts/list, prompts/get, mcp_inspect, 开放给 Agent, 工作伙伴调用, Agent 调用应用, 应用 Skill, server/mcp, server/mcp/ui, server/mcp/skills, 接入 MCP, 连接 MCP, 怎么用这个 MCP, MCP 客户端, Claude Code, Codex, mcp add, mcp login, config.toml, MCP 凭证, 可用范围, 连不上。不负责 HTTP 对外开放接口(走 openapi-guide)、自动化触发器(走 trigger-guide)。"
4
4
  steering: true
5
5
  steering-topic: mcp_guide
6
6
  match-template-name: nestjs-react-fullstack
@@ -52,7 +52,7 @@ ls -d node_modules/@lark-apaas/nestjs-mcp node_modules/@lark-apaas/coding-preset
52
52
  2. schema 用 zod,提成常量放类上方;方法签名固定 `(input: Infer<typeof In>, ctx: McpContext)`,返回 `McpToolResult<typeof Out>`。签名写错在 `npm run type:check:server` 暴露。
53
53
  3. `description` 面向模型:用途、何时调用、参数来源、返回什么、失败怎样。每个 zod 字段 `.describe()`。消费方 Agent 只靠这些选工具、填参数。
54
54
  4. 四项 `annotations` 显式写出。只读工具必须 `readOnlyHint: true`:缺省会被当成可能写数据,调试时每次都要 `safe: true`,消费方也会额外确认。
55
- 5. 声明了 `outputSchema` 就返回匹配的 `structuredContent`,对不上时 `mcp_inspect` 报「输出与其声明的 outputSchema 不符」;列表工具入参带 `limit`(默认不超过 20)与 `cursor`;写操作的幂等由 Service 保证,不引入幂等键字段。
55
+ 5. 声明了 `outputSchema` 就返回匹配的 `structuredContent`,对不上时 `mcp_inspect` 报「输出与其声明的 outputSchema 不符」。`content` 默认不写,由 SDK 补 `structuredContent` 的 JSON 文本;自己写了 `content` 就关掉了这份兜底,只读文本的客户端只看得到你写的那几句——要摘要就在摘要后再追加一块完整 JSON,不要只写「找到 N 条」这类统计文案。列表工具入参带 `limit`(默认不超过 20)与 `cursor`;写操作的幂等由 Service 保证,不引入幂等键字段。
56
56
  6. 返回值按消费方够用来裁:`structuredContent` 只放它推理或界面渲染要用的字段。SDK 不截断,返回多少对方就吃多少,详见 `$SKILL_DIR/references/tool-authoring.md` 的「返回体积」。
57
57
  7. 业务失败抛 `McpToolError(message, { code })`,Agent 原样收到;`message` 连下一步一起写(「订单不存在,用 order_search 按客户名重查一个有效 ID」),只陈述现象会让对方原样重试或直接放弃。其他异常只落日志、返回通用提示,Agent 看不到原因。
58
58
  8. 身份只从 `ctx.user` 读;权限与行级过滤留在 Service。工具类作为业务模块 provider 注册,单例——请求态(当前用户、本次入参、游标)只放方法内的局部变量,写进类字段会在并发请求间串号。
@@ -68,7 +68,7 @@ ls -d node_modules/@lark-apaas/nestjs-mcp node_modules/@lark-apaas/coding-preset
68
68
  - 服务端 import `server/mcp/ui/` 里的源码,或界面 import `client/`、`server/` 里的业务代码、工具类、Node 模块——子构建的边界检查直接报错,服务端只经 `readMcpUiTemplate` 读构建产物。
69
69
  - 界面里存凭证、直连业务系统——沙箱 iframe 是独立 origin,用户登录态带不进来;数据走宿主转发的工具调用。
70
70
  - `McpToolError.data` 与 `structuredContent` 里放密钥、内部连接信息——两者原样进 Agent 上下文。
71
- - 自己实现 MCP 协议、JSON-RPC、`/mcp` 路由、凭证校验——SDK 与网关已做,重复实现会与网关映射冲突。
71
+ - 自己实现 MCP 协议、JSON-RPC、`/mcp` 路由、鉴权——SDK 与网关已做,重复实现会与网关映射冲突。
72
72
 
73
73
  ## 交付前核对
74
74
 
@@ -2,20 +2,17 @@
2
2
 
3
3
  适用于「应用已经有 MCP 工具,用户要在自己的 Agent(Claude Code / Codex / 其它 MCP 客户端)里调用它」。开发态自己验工具走 `debugging.md` 的 `mcp_inspect`,与本文无关。
4
4
 
5
- **产出是一条用户复制即可执行的命令,不是操作说明。**连接地址和凭证都由你查出来填好,不要让用户自己去界面上找。
5
+ **产出是一条用户复制即可执行的命令,不是操作说明。**连接地址由你查出来填好,不要让用户自己去界面上找。
6
6
 
7
- ## 取连接配置
7
+ ## 取连接地址
8
8
 
9
9
  ```bash
10
- lark-cli apps +mcp-get --app-id "$app_id" --include-secret
10
+ lark-cli apps +mcp-get --app-id "$app_id" --format pretty
11
11
  ```
12
12
 
13
- `data.config` 里是完整的 `mcpServers` 对象,含连接地址与 `X-Mcp-Token`。不加 `--include-secret` 拿到的是脱敏展示(`data.redacted=true`),不能用于连接。
13
+ stdout 只有一条连接地址(形如 `https://bytedance.feishu.link/mcp/app_17ej3z4w7zr`),原样填进下面的模板,不改写、不拼接。
14
14
 
15
- 命令语义、风险等级与权限要求以 `lark-apps-ops` 的 `references/lark-apps-mcp.md` 与各命令 `--help` 为准,本文不复述。两条只在这里强调:
16
-
17
- - **该命令会创建缺失的运行态凭证,风险是 `write`。**不要仅为「看看这应用开没开 MCP」而调用它。重复调用返回已有凭证,不轮换、不重置。
18
- - 拿不到命令(沙箱 CLI 版本不带该子命令)时如实告知用户,**不要自己拼连接地址兜底**——地址形如 `https://<租户域名>.<环境后缀>/mcp/app`,两段都不可推测。
15
+ 该命令同时会开启本应用的线上 MCP;重复调用返回同一结果。没开启过的应用不跑它就连不上,所以即使用户说自己知道地址,也先跑一次。
19
16
 
20
17
  ## 服务名你来起
21
18
 
@@ -29,38 +26,34 @@ lark-cli apps +mcp-get --app-id "$app_id" --include-secret
29
26
 
30
27
  ## 落到客户端
31
28
 
32
- 从 `data.config` 的 `mcpServers` 条目里取出连接地址与请求头,连同上面定好的名字填进下面对应模板,整条给用户。
29
+ 把连接地址与上面定好的名字填进对应模板,整条给用户。
33
30
 
34
31
  **Claude Code**
35
32
 
36
33
  ```bash
37
- claude mcp add --transport http <名字> <连接地址> --header "X-Mcp-Token: <凭证>"
34
+ claude mcp add --transport http <名字> <连接地址>
38
35
  claude mcp login <名字>
39
36
  ```
40
37
 
41
38
  **Codex**
42
39
 
43
40
  ```bash
44
- codex mcp add <名字> --url "<连接地址>?x-mcp-token=<凭证>"
41
+ codex mcp add <名字> --url "<连接地址>"
45
42
  codex mcp login <名字>
46
43
  ```
47
44
 
48
- `codex mcp add` 没有传请求头的参数,凭证走 query 参数。不要用 `--bearer-token-env-var`,它会走静态 token 分支把 OAuth 跳过去。
49
-
50
45
  ## 为什么是两条命令
51
46
 
52
- `X-Mcp-Token` 认「连的是哪个应用」,第二条命令办的是「谁在调用」——浏览器里完成一次飞书授权,客户端拿到用户令牌并自行续期。两者缺一都连不上,也互相替代不了。应用凭证不是用户身份,查出凭证不等于用户已获授权。
47
+ 第一条只登记连接地址;第二条在浏览器里完成一次飞书授权,客户端拿到用户令牌并自行续期。工具以授权用户的身份执行,不 `login` 就是 401。
53
48
 
54
49
  `login` 只服务 HTTP 传输。用户若报「only supported for streamable HTTP servers」,是配置成了本地 stdio 形态,按上面的模板改成 `url` 形式。
55
50
 
56
51
  ## 两件命令解决不了的事
57
52
 
58
- 凭证能提前建,但要真正调通还需要:
59
-
60
53
  | 条件 | 不满足时的表现 |
61
54
  |---|---|
62
- | 应用已发布且线上 MCP 已启用 | 连接失败;创建凭证不要求发布,所以拿到凭证不代表能连 |
63
- | 调用人在应用可用范围内 | 403,与凭证是否有效无关 |
55
+ | 应用已发布 | 连接失败 |
56
+ | 调用人在应用可用范围内 | 403,与授权是否成功无关 |
64
57
 
65
58
  这两条你查不到也改不了,连不上时按下表对号,属于这两类就直接告诉用户去处理,不要反复重试。
66
59
 
@@ -70,13 +63,7 @@ codex mcp login <名字>
70
63
  |---|---|---|
71
64
  | 401,reason `lark_oauth_client_mismatch` | 授权用的 client_id 与服务端元数据不一致 | 清掉客户端授权缓存重新 `login` |
72
65
  | 401,reason `lark_oauth_conf_unavailable` | 平台侧配置问题 | 告知用户找平台同学,不要让他反复重试 |
73
- | 401,无 reason | 凭证复制不全,或配的是预览态凭证 | 重新 `+mcp-get --include-secret` 取一次,整段替换 |
66
+ | 401,无 reason | 没执行 `login`,或授权已失效 | 重新 `login` |
74
67
  | 403 `user is not in the visible scope of this app` | 不在可用范围,或授权账号不属于应用租户 | 前者找负责人放开范围;后者换应用租户内的账号授权 |
75
- | 404 | 应用没启用 MCP;或零工具零资源无应用 Skill(SDK 此时也返回 404) | 后者是正常状态不是故障,先确认本应用确实注册了工具 |
68
+ | 404 | 连接地址没原样使用 `+mcp-get` 的输出;或零工具零资源无应用 Skill(SDK 此时也返回 404) | 重跑 `+mcp-get` 整段替换地址;后者是正常状态不是故障,确认本应用确实注册了工具 |
76
69
  | POST 被拒,CSRF 相关 | 旧模板应用对 POST 有 CSRF 双提交校验 | 平台侧已知未收口项,告知用户,不在客户端侧拼 cookie 绕 |
77
-
78
- ## 凭证纪律
79
-
80
- - `--include-secret` 的输出只用于拼那条给用户的命令。**不写进项目任何文件、不进日志、不复述进汇报与摘要、不提交仓库。**
81
- - 保持客户端默认的 local scope;不要建议 `--scope project`——那会把凭证写进随代码提交的配置文件。
82
- - 用户自己贴出过凭证(聊天、issue、文档)时提醒他重置,旧值立即失效。
@@ -24,7 +24,7 @@
24
24
  1. 改工具 / 资源 / 界面。
25
25
  2. 等 dev server 重启(`nest --watch` 会自动重启;界面改动由 `[mcp-ui]` 插件自动重建)。定义有问题会让应用起不来并打印「MCP 定义校验失败」,修完再往下走。
26
26
  3. `list_tools` 核对清单与源码一致(`visibility: ['app']` 的工具也在清单里,属正常)。
27
- 4. `call` 逐个只读工具跑一次真实入参。写工具只在能自己清掉痕迹(有对应删除 / 撤销工具,或业务上可撤回)时才真调并随手清理;否则只验证入参校验与 `McpToolError` 分支,汇报里写明「写入链路未真实验证」。
27
+ 4. `call` 逐个只读工具跑一次真实入参。除了 `structuredContent`,单独看「文本内容」那段是否带全了业务数据:只读文本的客户端只看得到这段,只有「找到 N 条」之类的统计文案就是 `content` 写坏了(见 `tool-authoring.md`「方法签名与返回值」)。写工具只在能自己清掉痕迹(有对应删除 / 撤销工具,或业务上可撤回)时才真调并随手清理;否则只验证入参校验与 `McpToolError` 分支,汇报里写明「写入链路未真实验证」。
28
28
  5. 带界面的工具 `read_resource`。
29
29
  6. 每个 `McpToolError` 分支构造一次触发(不存在的 ID、非法状态),确认 `isError: true` 且 `code` 与 `description` 预告一致。
30
30
  7. 更新 `server/mcp/skills/<name>/SKILL.md`,再 curl 目录接口(frontmatter 不合法这里会 500):`skills[]` 有该 `name`、`content` 是最新正文,`prompts[]` 的 `description` 一致。
@@ -43,6 +43,7 @@
43
43
  | `isError` + 业务 `code` | 工具按设计抛了 `McpToolError` | 对照 `description` 检查是否预告了该 code;入参是否真的该触发 |
44
44
  | `isError` + 通用失败提示 | 方法抛了非 `McpToolError` 的异常 | 读结果尾部 Trace-ID 日志定位;该异常若是可预期的业务失败,改抛 `McpToolError` |
45
45
  | zod 校验错误 | 入参不符 inputSchema | 对照 `list_tools` 里的 schema 改 `arguments`;schema 本身不合理就改源码 |
46
+ | 用户在自己的 Agent / 客户端里调工具,只看到「找到 N 条」,没有明细 | 手写的 `content` 只放了摘要,关掉了 SDK 自动补的 JSON 文本;对方客户端只读 `content`。`mcp_inspect` 同时展示 `structuredContent`,所以自测看着正常 | 删掉 `content` 让 SDK 补,或在摘要块后追加 `JSON.stringify(structuredContent)`;不要把字段逐个拼成中文文案 |
46
47
  | 「工具已执行,但输出与其声明的 outputSchema 不符」 | `structuredContent` 与 `outputSchema` 对不上 | 工具已跑通:改返回值或改 schema,不重试同一调用 |
47
48
  | `timeout` | 单个子请求 60s 无动静,或整次超 600s | 收窄入参 / 分页 / 拆成「发起 + 查状态」两个工具;加进度通知无效(JSON 模式发不出去) |
48
49
  | `connect_failed` / `http_error` / `unauthorized` | 网关或预览环境问题;`http_error` 500 先对下面 frontmatter 那行,应用自己也会 500 | 看 statusCode;不是应用代码问题时告知用户,不改代码绕 |
@@ -187,7 +187,7 @@ await app.connect();
187
187
  - 外部域名写进 `@McpUiResource` 的 `csp`:`connectDomains`(fetch / WebSocket)、`resourceDomains`(图片、脚本、样式)、`frameDomains`(内嵌 iframe)、`baseUriDomains`;默认全禁,漏写表现为资源静默加载失败。
188
188
  - 业务返回值用 `textContent` 或 JSX 文本渲染,不拼 `innerHTML`。
189
189
  - 每次调用重新注入数据;界面不假设自己能跨调用保留状态。
190
- - 宿主不支持 MCP Apps 时只显示 `content` 文本,所以带界面的工具也要返回可读的文本摘要。
190
+ - 宿主不支持 MCP Apps 时只显示 `content` 文本,所以带界面的工具同样默认不写 `content`、由 SDK 补 JSON;要加摘要就在后面追加完整 JSON 块(见 `tool-authoring.md`「方法签名与返回值」),只给摘要会让这类宿主看不到数据。
191
191
  - 一个工具配一个专注的界面。一个界面同时伺候多个工具的结果,每次渲染都要先判断数据从哪来,模型也更难预期结果长什么样。
192
192
 
193
193
  ## 调试
@@ -78,7 +78,7 @@ export class OrderMcpTools {
78
78
  items: page.items.map((o) => ({ id: o.id, customer: o.customer, amount: o.amount, status: o.status })),
79
79
  nextCursor: page.nextCursor,
80
80
  },
81
- content: [{ type: 'text', text: `找到 ${page.items.length} 条订单` }],
81
+ // 不写 content:SDK 自动补一份 structuredContent 的 JSON 文本,只读文本的客户端也拿得到明细
82
82
  };
83
83
  }
84
84
 
@@ -140,7 +140,22 @@ export class OrderModule {}
140
140
  ## 方法签名与返回值
141
141
 
142
142
  - 固定 `(input, ctx)`。无入参的工具 `input` 为 `Record<string, never>`,仍要写第二个参数 `ctx`。
143
- - 声明了 `outputSchema`:返回 `{ structuredContent }`,类型由 schema 推导;`content` 可选,SDK 会自动补一份 JSON 文本给不支持结构化输出的客户端。加一句业务摘要的 `content` 让文本客户端也能读。
143
+ - 声明了 `outputSchema`:返回 `{ structuredContent }`,类型由 schema 推导;**默认不写 `content`**。SDK 只在 `content` 缺省时自动补一块 `JSON.stringify(structuredContent)` 文本。
144
+ - 不支持结构化输出的客户端只读 `content` 文本:只渲染文本的宿主、把工具结果原样喂给模型的 Agent 都看不到 `structuredContent`。
145
+ - 自己写了 `content`,SDK 就不再补。只写「找到 2 条待办」这类统计文案,这些客户端就只看到这一句,标题、ID、状态全丢——用户会反馈「调用了但没有实际内容」。
146
+ - 确实要一句摘要时,摘要块后面再追加一块完整数据,不要把字段逐个拼进中文文案(拼漏、与 schema 走样):
147
+
148
+ ```typescript
149
+ const data = { items, nextCursor };
150
+ return {
151
+ structuredContent: data,
152
+ content: [
153
+ { type: 'text', text: `找到 ${total} 条订单,本页 ${items.length} 条` },
154
+ { type: 'text', text: JSON.stringify(data) },
155
+ ],
156
+ };
157
+ ```
158
+
144
159
  - 未声明 `outputSchema`:返回 `{ content: [{ type: 'text', text }] }`;`content` 还支持 `image` / `audio` / `resource_link` 块。
145
160
  - `isError: true` 由 SDK 在捕获 `McpToolError` 时设置,不需要手动返回。
146
161
 
@@ -159,7 +174,7 @@ return {
159
174
  hint: '命中 847 条,只返回前 20 条;加 status 过滤或换更具体的关键字',
160
175
  items: page.items,
161
176
  },
162
- content: [{ type: 'text', text: '命中 847 条,返回前 20 条' }],
177
+ // 不写 content:SDK 补的 JSON 文本里 truncated / hint 排在最前,只读文本的客户端也能看到
163
178
  };
164
179
  ```
165
180
 
@@ -196,7 +211,7 @@ return {
196
211
  2. 建 `server/mcp/tools/<域>.tools.ts`,至少一个只读工具复用现有 Service。
197
212
  3. 把工具类加进所属业务模块的 `providers`。
198
213
  4. 写 `server/mcp/skills/<name>/SKILL.md`(下节)。
199
- 5. 跑 `lark-cli apps +mcp-key-create --app-id "$app_id"` 备好运行态凭证。
214
+ 5. 跑 `lark-cli apps +mcp-get --app-id "$app_id"` 开启线上 MCP。不跑这一步,应用发布后外部客户端也连不上。
200
215
  6. 进 `debugging.md` 的调试回路。
201
216
 
202
217
  ## 应用 Skill:`server/mcp/skills/<name>/SKILL.md`