@frontmcp/edge

中文文档
1.12.2发布于 2026/11/15Apache-2.0TypeScriptESM + CJS构建来源已验证近 30 天下载 5080

在 Cloudflare Workers / V8 上运行一个 FrontMCP MCP 服务器 从平面配置中分离出来 — 没有装饰器, 没有构建步骤

中文使用文档
文档目录11 节
  1. 安装
  2. 使用量
  3. 持久对象中的会话
  4. 管理模式
  5. 套装
  6. API 密码
  7. createEdgeMcp(config)
  8. 文档
  9. 相关软件包
  10. 捐款
  11. 许可证

@ frontmcp/ edge (帮助)

从平面配置对象运行 Cloudflare Workers 上的 FrontMCP 服务器 。 工人的输入文件呼叫 createEdgeMcp() 用你给的配置 @FrontMcp 并出口其回报; wrangler 捆绑文件的原样,没有 frontmcp build 步骤和无 @FrontMcp 课。 比较 frontmcp build --target cloudflare,它添加了保存在持久对象和管理模式中的会话,可以拉出一个技能化的 OpenAPI 捆绑,从 Cron 触发器中刷新,并可以取出被推入的捆绑.

npm License

安装

npm install @frontmcp/edge @frontmcp/sdk
npm install -D wrangler

@frontmcp/sdk 是同级依赖. @frontmcp/plugin-skilled-openapi,管理模式使用,是一个依赖性,所以它与这个软件包一起被安装. 安装与节点.js 24 或稍后.

使用量

// src/worker.ts
import { createEdgeMcp } from '@frontmcp/edge';
import { app, tool, z } from '@frontmcp/sdk';

const searchTickets = tool({
  name: 'search_tickets',
  description: 'Search tickets by title',
  inputSchema: { query: z.string() },
})(async ({ query }) => ({ query, tickets: [{ id: 'T-1', title: 'Cannot log in' }] }));

export default createEdgeMcp({
  info: { name: 'help-desk', version: '1.0.0' },
  apps: [app({ id: 'help-desk', name: 'Help Desk', tools: [searchTickets] })],
  http: { entryPath: '/mcp' },
});
# wrangler.toml
name = "help-desk"
main = "src/worker.ts"
compatibility_date = "2024-09-23"
compatibility_flags = ["nodejs_compat"]
npx wrangler dev      # local
npx wrangler deploy   # ship

nodejs_compat 需要, 也一样 apps (您列出时可能是空的) tools (在最高一级). tool() 财务报告和审定财务报表 app() 定义没有装饰器的工具和应用程序; @Tool 财务报告和审定财务报表 @App 上课也行.

处理者做什么:

  • 它根据第一个请求构建服务器,而不是当模块装入, 因为一个工人不能做 I/O,然后再用它。 构建失败的答案 500 (配置错误)或 503 与 Retry-After,在延迟1秒至60秒后再次受审.
  • 它复制字符串绑定到 process.env 第一次请求时 [vars] ,而不是替换已经存在的值。 KV 命名空间等其他约束工具 this.workerEnv。 。 。 .
  • 服务于 MCP 时间 http.entryPath (单位:千美元)/ (默认)和 /healthz,以及 /readyz 与 health: { readyz: { enabled: true } }。 。 。 .
  • 背景任务取消 边缘; 服务器日志并启动.
  • 在德诺和邦,通过 fetch 他们自己的第二个论点,处理者从中读取客户端IP.

持久对象中的会话

无 sessions,"工人"不保留会话:2026-07-28之前,客户端对协议版本的每一个请求都会自行送达(2026-07-28不需要会话). 与 sessions: {}中,每个会话的请求都转到一个以会话 ID 命名的持久对象上,它保持了会话的服务器和请求之间的传输。 导出类并绑定:

// src/worker.ts
import { createEdgeMcp } from '@frontmcp/edge';
import { app, tool, z } from '@frontmcp/sdk';

const searchTickets = tool({
  name: 'search_tickets',
  description: 'Search tickets by title',
  inputSchema: { query: z.string() },
})(async ({ query }) => ({ query, tickets: [] }));

const mcp = createEdgeMcp({
  info: { name: 'help-desk', version: '1.0.0' },
  apps: [app({ id: 'help-desk', name: 'Help Desk', tools: [searchTickets] })],
  http: { entryPath: '/mcp' },
  sessions: {}, // the Durable Object binding defaults to FRONTMCP_SESSIONS; set { binding } to change it
});

export default mcp;
export const FrontMcpSession = mcp.SessionDurableObject;
# wrangler.toml
[[durable_objects.bindings]]
name = "FRONTMCP_SESSIONS"
class_name = "FrontMcpSession"

[[migrations]]
tag = "v1"
new_classes = ["FrontMcpSession"]

如果捆绑不在的话 wrangler.toml,工人不开庭就满足请求.

管理模式

managed 拉一个签名 熟练的 OpenAPI 捆绑( OpenAPI specs 编译为 MCP 技能和工具) 从捆绑的服务器, 并服务于您的应用程序旁边, 因此服务器的能力不进行调用而改变 。 一个工人在请求之间没有定时器,所以捆绑由 Cron 触发器刷新,该触发器称返回 scheduled() 处理器 :

// src/worker.ts
import { env } from 'cloudflare:workers';

import { createEdgeMcp, kvBundleCacheFromEnv } from '@frontmcp/edge';

export default createEdgeMcp({
  info: { name: 'billing', version: '1.0.0' },
  apps: [],
  managed: {
    endpoint: 'https://bundles.example.com/v1/bundles/billing',
    authToken: env.BUNDLE_PULL_TOKEN,
    expectedAudience: 'billing-prod',
    jwksUrl: 'https://bundles.example.com/.well-known/jwks.json',
    expectedIssuer: 'https://bundles.example.com',
    trustedKeys: [{ keyId: 'billing-2026-11', alg: 'EdDSA', publicKeyPem: env.BUNDLE_SIGNING_KEY }],
    cache: kvBundleCacheFromEnv('BUNDLE_CACHE'),
  },
});
# wrangler.toml
[[kv_namespaces]]
binding = "BUNDLE_CACHE"
id = "<your KV namespace id>"

[triggers]
crons = ["*/5 * * * *"]

BUNDLE_PULL_TOKEN 是个秘密( a)npx wrangler secret put BUNDLE_PULL_TOKEN) (中文(简体) ). 模块装入时会评价配置, 所以从中读取密钥 env 那个 cloudflare:workers 出口,不是来自请求.

BUNDLE_SIGNING_KEY 是公共密钥的 PEM 服务器符号捆绑,以及 keyId 是其捆绑名称的密钥 ID。 这不是秘密: 一个 [vars] 输入 wrangler.toml 我会做的。 钥匙在 jwksUrl 只验证拉动符,而不是捆绑,所以没有 trustedKeys 每个捆绑都被拒绝 unknown signing keyId 工人们,是没有技能的。 至 1.11.2 这一例子尚存 trustedKeys 出来.

外地说明
endpoint为签名捆绑服务的 HTTPS URL
authToken为此服务器发布的捆绑服务器的令牌
expectedAudience, (中文(简体) ), jwksUrl, (中文(简体) ), expectedIssuer捆绑服务器的代号是如何验证的
trustedKeys公共密钥捆绑的签名有: { keyId, alg: 'RS256' | 'EdDSA', publicKeyPem }
cache保留上次应用的捆绑 : kvBundleCacheFromEnv('BINDING'),或者你自己的商店
enableWebhook还要带捆子去 POST /__skilled_openapi/push页:1
requirePushBodyDigest拒绝一个无助推号 body_sha256 索赔,见下文。 默认 false
pushReplay哪里推出标志 jtis 保留: { binding } (默认) FRONTMCP_PUSH_REPLAY页:1
allowRollback如下文所示,用比所服务年龄大的拉出或推出捆绑。 默认 false
requireSignature, (中文(简体) ), dev, (中文(简体) ), credentials, (中文(简体) ), outbound被传入了技能型OpenAPI插件. dev 跳过签名检查: 生产中永远不要使用它
confirm作为插件在用户请求前询问用户的动作 confirm; 见下文
pollIntervalMs, (中文(简体) ), bundleCacheDir忽略了没有计时器或文件系统的工人

一旦套接字应用并保存在其中, Cron 运行结束 cache,同样当它是第一个事件时,一个孤立者会得到:然后它开始捆绑同步,它的第一拉就是跑的拉. 失败签名或其他检查的捆绑没有被应用和保存,所以无法到达捆绑服务器的冷启动服务于Worker应用的最后一个捆绑.

Bundles只向前走:一个被拉出或被推入的捆绑比隔离服务要长的捆绑没有被应用和保存,所以一个有效签名的旧捆绑无法将工人卷回. 按 semver 比较出两个 semver 版本;同一种版本(重新签名),或两种版本都不同时 semver 比较出 generatedAt。要回滚,请在较新的版本下发布先前的内容,或设定 allowRollback: true。一个启动的隔离还没有完成捆绑,所以缓存捆绑就代表了它:当缓存捆绑的拉力失败、被拒绝时,或者当它比缓存捆绑老(没有)时,隔离为缓存捆绑服务 allowRollback),所以一个被打乱了的CDN或回滚出源无法取回新的隔离. 服务缓存的捆绑 永远不会写回 cache高达1.11.0个新隔离器服务于它所拔出的东西,当捆绑被拒绝时,它没有任何服务,并且将缓存输入器重置在离线起始处.

Cron 运行失败( E)scheduled() cloudflare将运行记录为例外, 并算作失败引用)

  • 未应用它提取的捆绑: 其签名或其他检查失败, 或比所服务的时间长 。 错误名称 : [frontmcp/edge] Cron refresh: bundle [email protected] was not applied: digest mismatch (...)。当运行开始隔离时,它说 was not applied, so the cached bundle serves: older than the cached bundle [email protected] (...)。 。 。 .
  • 牵引失败数字 : [frontmcp/edge] Cron refresh: the bundle pull failed: pull responded 503。当运行开始隔离时,它说 the bundle pull failed, so the cached bundle serves。在它的代号上失败的拉出支票: the bundle pull failed (token check: expired): ...,作为 lastSync.tokenFailure 显示在 /readyz。 。 。 .

多达1.11.0次,据报告取得了成功。 运行,它的拉 应用捆,或拉 服务,成功.

/readyz 说一样,一次 health: { readyz: { enabled: true } } 你们应当打开它。 虽然上一次同步被拒绝或失败,但其 skilled-openapi 检查是 degraded,与 details: { state: 'stale', bundleId, bundleVersion, lastSync }:捆绑已送达,拒绝或失败与其理由和时间. 工人保持准备,因为它服务 最后一个好捆。 每一个隔离的答案 自己的捆绑和同步。 细节出现在生产之外,或与 health.includeDetails: true。 。 。 .

每个操作呼叫先检查API的宿主: 在工人, node:dns 通过DNS解决HTTPS,所以主机必须公开解决.

confirm 通过 HTTP 方法、 标记或操作ID , 将用户在请求前询问的动作命名为 Skilled OpenAPI 插件 confirm 选项 是否为: confirm: { methods: ['DELETE'] }。插件检查它,所以一个无效的值拒绝启动(500 CONFIG_INVALID,则日志将字段命名)。 问题是MCP诱导,服务器只用 elicitation: { enabled: true }在一个拒绝启动的云旗工人身上(Elicitation requires distributed storage when running on Edge runtime因此,有一个动作 confirm 名字被拒绝, the user's confirmation is needed for voidInvoice, and this server can't ask for it: elicitation is disabled; nothing was sent,或与 whenUnsupported: 'allow' 运行不问. load_skill 标记为 asksForConfirmation: true,审计日志记录了确认结果。 截至1.11.0管理模式无 confirm,所以每个动作 跑不问.

套装

与 enableWebhook: true,捆绑服务器可以推出一个新的捆绑,而不是等待下一次 Cron 运行:

POST /__skilled_openapi/push
Authorization: Bearer <JWT>
Content-Type: application/json

{ "schemaVersion": 1, "bundleId": "billing", "version": "4.2.0", ..., "integrity": { ... } }
  • 标志 检查为 authToken (由密钥签名) jwksUrl, (中文(简体) ), iss 等于 expectedIssuer, (中文(简体) ), aud 包括 expectedAudience页:1 它必须携带 jti 包含8个或8个以上没有被推入的字符,以及 iat 5分钟内,所以很久前就发行了的拉活符,不能推.
  • 标志 body_sha256 索赔 将它绑在身体上: 基数64url SHA-256,没有被粘贴,是请求体的确切字节(不是捆绑) integrity.digest) (中文(简体) ). 工人将它收到的字节搁置起来;任何其他机构都回答 401,日志上写着 push refused (body_mismatch)拒绝的代币 jti 保持未使用,所以它发出的推力仍然适用。 没有这个权利要求,在5分钟内捕获到的标志物可以携带为这名工人签名的捆绑服务器的任何其他捆绑. 一个没有它的信使被接受,每个隔离处有一个警告, 所以捆绑的服务器还没有设置它仍然工作; requirePushBodyDigest: true 拒绝. pushBodyDigest(body) 财务报告和审定财务报表 PUSH_BODY_DIGEST_CLAIM 从 @frontmcp/adapters/skills 计算发件人的索赔.
  • 躯体 作为JSON的捆绑, 被检查为被拉的捆绑是: 它的图谋, 然后它的签名反对 trustedKeys (除非 requireSignature: false) (中文(简体) ).
  • 适用,除非它比被送达的捆绑年长,在第一次拉出之后,在任何捆绑已经排队之后,所以一个推和克龙跑永远不能一次应用,然后保持在 cache。 。 。 .
状态体质
200{ "applied": true, "bundleId", "bundleVersion" }
400{ "error": "invalid_bundle" }: 身体不是捆绑的
401{ "error": "invalid_token" },与 WWW-Authenticate: Bearer error="invalid_token"
409{ "error": "push_replayed" }: 已使用此符号的推力
409{ "error": "bundle_outdated" }: 捆绑比被送达的年长, 留下来
422{ "error": "bundle_not_applied" }: 签名或捆绑支票失败; 已送达捆绑保留
503{ "error": "temporarily_unavailable" }: JWKS 无法读取,或者 jti 无法记录

一个被拒绝的代号也一样 401 任何支票都拒绝了 只有日志命名它: [saas-source] push refused (wrong_issuer): ...,或 bundle push not checked (jwks_unavailable): ... (单位:千美元) 503。 。 。 .

终点是可接通的流出, skilled-openapi:bundle-push,在与服务器其他HTTP流相同的IP过滤器和速率限制后.

这个 jtis被保存在一个持久对象中,每个工人的隔离和复制都到达,因此回放到另一个隔离的推力也被拒绝. (KV不能做到这一点:它最终是一致的,所以两个隔离点都可以读取a jti 未使用。 ) 导出类并绑定:

// src/worker.ts
const mcp = createEdgeMcp({
  // ...
  managed: { /* ... */ enableWebhook: true }, // the binding defaults to FRONTMCP_PUSH_REPLAY; set pushReplay: { binding } to change it
});

export default mcp;
export const FrontMcpPushReplay = mcp.PushReplayDurableObject;
# wrangler.toml
[[durable_objects.bindings]]
name = "FRONTMCP_PUSH_REPLAY"
class_name = "FrontMcpPushReplay"

[[migrations]]
tag = "v1" # or the next tag, when wrangler.toml already has migrations
new_sqlite_classes = ["FrontMcpPushReplay"]
  • 每个观众一个对象 留着 jtis, 以 expectedAudience每一个 jti 被保存到它的标志 iat 5分钟后就投了.
  • 没有束缚,每个推答 503 temporarily_unavailable,日志则指定了缺少的绑定值。 拉和克龙运行不受影响. 最高为1.11.0 jtis被保存在每个隔离的记忆中, 隔离不共享.
  • 在德诺或邦设置 pushReplay 到函数 env 返回一个商店, { claim(key, ttlMs) }: 它解决 true 当它记录 key, (中文(简体) ), false 何时 key 已经记录了,当它无法辨别时,就扔出。 掷出时拒绝推入.

API 密码

createEdgeMcp(config)

config 这是什么 @FrontMcp 取( 取)info, (中文(简体) ), apps, (中文(简体) ), tools, (中文(简体) ), http, (中文(简体) ), auth附加:

外地类型说明
sessions{ binding? }将每个会话保留在一个持久对象中,以 binding (默认) FRONTMCP_SESSIONS(中文(简体) )
managedManagedEdgeOptions如上, 拖取一个熟练的 OpenAPI 捆绑
skillIndex{ binding?, ttlSeconds? },或函数 env保留 KV 命名空间中的技能搜索索引( 默认绑定) FRONTMCP_SKILL_INDEX),所以一个寒冷的开始读取它而不是重建它

服务器从不听 : createEdgeMcp() 套装 serve: false。不构建服务器就可以找到配置错误,例如 approval 或者说 featureFlag 元数据不执行插件,在评估模块时抛出,所以 Worker 无法部署 .

它返回:

成员说明
fetch(request, env?, ctx?)工人的要求处理器
SessionDurableObject永久对象类 sessions。仅在使用时导出它
PushReplayDurableObject永久对象类 managed.enableWebhook,如上所示
scheduled(event?, env?, ctx?)与 managed 仅: Cron 触发器处理器, 它拉出捆绑, 不应用时则扔出

其他出口: kvBundleCacheFromEnv, (中文(简体) ), createKvBundleCache, (中文(简体) ), kvSkillIndexCacheFromEnv, (中文(简体) ), createKvSkillIndexCache, (中文(简体) ), buildManagedOpenApiPluginOptions,则 EdgeMcp, (中文(简体) ), EdgeMcpConfig, (中文(简体) ), ManagedEdgeOptions, (中文(简体) ), EdgePushReplayStore 财务报告和审定财务报表 EdgePushReplayStoreFactory 类型.

文档

捐款

资料来源: libs/edge欢迎提出问题和拉动请求;见 捐款额。 。 。 .

许可证

阿帕奇2.0 (英语). 见 自由主义。 。 。 .

版本与发布记录

@frontmcp/edge 共发布 68 个版本,首次发布于 2026/06/21;过去 12 个月发布了 68 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
1.12.2latest2026/11/151 天前
1.12.12026/11/132 天前
1.12.02026/11/114 天前
1.12.0-rc.1rc2026/11/114 天前
1.11.32026/11/106 天前
1.11.22026/11/096 天前

关于 @frontmcp/edge 的常见问题

@frontmcp/edge 是什么?

在 Cloudflare Workers / V8 上运行一个 FrontMCP MCP 服务器 从平面配置中分离出来 — 没有装饰器, 没有构建步骤

如何安装 @frontmcp/edge?

在项目目录运行 npm install @frontmcp/edge,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:@frontmcp/sdk(1.12.2)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@frontmcp/edge 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@frontmcp/edge 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@frontmcp/edge 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=24.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@frontmcp/edge 可以商用吗?

@frontmcp/edge 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@frontmcp/edge 的下载量有多少?

根据 npm 官方下载统计,@frontmcp/edge 在 2026/10/16 – 2026/11/14 这 30 天内被下载 5,080 次,日均约 169 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@frontmcp/edge 还在维护吗?

最新版本 1.12.2 发布于 2026/11/15(1 天前);至今共发布 68 个版本,其中过去 12 个月发布了 68 个;首次发布于 2026/06/21。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。