asm-mcp
@asm/asm-mcp 提供业务 MCP 服务与适配器。AsmDataProvider 是宿主的数据边界,InMemoryAsmDataProvider 是不访问外部 API 的确定性模拟实现。
在 Durable 中挂载
Section titled “在 Durable 中挂载”import { createRegistry } from "@earendil-works/pi-durable";import { AsmMcpServer, createAsmMcpExtension, generateAsmMultitenantSeed, InMemoryAsmDataProvider, isRoutineUnattended,} from "@asm/asm-mcp";import { createExtraToolsExtension } from "@asm/extra-tools";
const seed = generateAsmMultitenantSeed();const server = new AsmMcpServer({ provider: new InMemoryAsmDataProvider(seed),});const registry = createRegistry();registry.install(createAsmMcpExtension(server));registry.install(createExtraToolsExtension({ multiTenant: true, skills: seed.skills, isUnattended: isRoutineUnattended,}));宿主用该 Registry 打开 Harness,并为每次执行传递已认证的租户 Context。最小直接调用见开始组装。
工具与元数据
Section titled “工具与元数据”MCP 服务公开八个工具,Durable 桥接名称加 asm__ 前缀:
| 工具 | 关键参数 |
|---|---|
pos_query_report |
period、dateOrMonth、可选 endDate |
knowledge_search |
query、可选 topK(1–20) |
competitor_search |
mode: "nearby" | "details",可选 queryOrName、radiusMeters;details 要求名称 |
macro_stats_query |
category: "market_cpi" | "labor_wages"、可选 months |
law_regulation_search |
query、可选 keywords |
web_search |
query、可选 extractUrls(最多两个) |
report_get_facts |
period、targetDate、可选 sectionIds |
report_review |
draft、period、targetDate |
period 为 daily、weekly 或 monthly。工具 schema 拒绝额外参数,租户身份不放在业务参数内。
调用元数据是 { tenantContext, execution }。前者要求 tenantId、scopeId 和非空 roles;后者要求 runId、callId、mode: "direct" | "codemode",可带 profile。在 MCP 传输中它位于 tools/call 的 _meta,由可信宿主注入;收到未认证客户端提供的同名字段不能直接转发。
| 接入方式 | API 与宿主责任 |
|---|---|
| MCP transport | await server.connect(transport) 返回异步关闭函数;宿主实现传输认证 |
| Durable | createAsmMcpExtension(serverOrClient) 或 createTenantScopedMcpTools(serverOrClient) |
| 独立 QuickJS | createAsmCodemodeTools(client, { runId }, context);独立沙箱不提供 Durable 恢复 |
| pi CLI | createAsmCliExtension({ client, resolveIngress, ... }) 返回可加载的扩展函数 |
本地服务的 Durable 桥接把事实缓存、审核记录和网页预算存入会话所属的 AsmRunDoc。远程 MCP 服务的持久状态仍需服务端提供 AsmStateStore;默认 MemoryAsmStateStore 只在进程内保存。
CLI 的 resolveIngress(flags, signal?) 必须校验认证授权,不能直接信任 --tenant、--scope 或 --roles。需要 PostgreSQL 历史时配置 pool、provider 和会话锁;恢复时校验原会话。公共入口 resolveAndInjectTenantContext(input, provider, context?) 处理身份和范围检查,bootstrapTenantCliSession(input, pool, provider, context?) 还验证持久会话归属。
接入报告流程
Section titled “接入报告流程”接续前面的 registry 和 server,增加原生 Durable 报告扩展:
import { InlineCodemodeSandbox, loadQuickJSWasm } from "@earendil-works/pi-codemode";import { createReportPipelineExtension, ReportPipelineTask,} from "@asm/asm-mcp";
const sandbox = new InlineCodemodeSandbox({ wasm: loadQuickJSWasm() });registry.install(createReportPipelineExtension({ sandbox, registry: server.sections,}));// RoutineRunner.run 的请求中使用:// task: ReportPipelineTask,// input: { period: "daily", targetDate: seed.targetDate }ReportPipelineTask 直接启动报告工具,不通过模型规划步骤。宿主配置会话模型或传入 writer,并在所有使用者关闭后关闭 sandbox。
流程先采集事实,再写一次初稿并执行代码校验。初稿未通过时只修订一次;再次失败则输出确定性事实模板,状态为 degraded_facts_only。事实和已完成草稿都在推进前提交,恢复时复用;未提交的模型调用可能再次执行。
内置章节为 sales_summary、channel_breakdown、customer_traffic、issues_and_actions。可通过 ReportSectionRegistry.register(plugin) 扩展。插件的 fetch、compute、severity 在 QuickJS 中执行,函数不得捕获宿主变量或调用宿主 import;template 在宿主渲染事实。
财务查询拒绝只读员工角色;报告入口需要报告权限和适当执行模式。readOnly: true 会在报告查询或模型调用前被拒绝,冻结租户不能开始报告工作。只读归档访问仍由认证入口控制。
web_search 每次运行最多调用两次。需要抓取页面的 Provider 使用 fetchPublicText() 或遵守其固定解析地址和重定向复查约定。不同并发会话和报告应使用不同的 sessionKey。