关于 Less Annoying CRM (remote, OAuth)
这是部署在 Cloudflare Workers 上的 Less Annoying CRM 非官方远程 MCP 服务,供支持 OAuth 的 MCP 客户端使用。它通过 OAuth 2.1 授权访问,并提供可选只读权限及与本地版本相同的 10 个工具。
主要功能
- 01OAuth 2.1 浏览器授权
- 02Streamable HTTP 远程连接
- 03读取联系人、备注和任务
- 04查看销售管道及项目
- 05创建记录并更新项目状态
- 06按授权范围限制读写
适用场景
- 01在 claude.ai 中连接 CRM
- 02通过浏览器授权远程 MCP 客户端
- 03授予只读权限以查询 CRM
- 04部署到自己的 Cloudflare 账户
MCP 相关
远程 MCP 服务通过 Streamable HTTP 提供 10 个 Less Annoying CRM 工具,并使用 OAuth 2.1 和授权范围控制访问。
配置
远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。
https://lacrm-mcp.northkeellabs.com/mcpclaude mcp add --transport http com-northkeellabs-lacrm-mcp-remote https://lacrm-mcp.northkeellabs.com/mcp{"mcpServers":{"com-northkeellabs-lacrm-mcp-remote":{"type":"http","url":"https://lacrm-mcp.northkeellabs.com/mcp"}}}在 Cursor 中打开{"servers":{"com-northkeellabs-lacrm-mcp-remote":{"type":"http","url":"https://lacrm-mcp.northkeellabs.com/mcp"}}}在 VS Code 中打开{"mcpServers":{"com-northkeellabs-lacrm-mcp-remote":{"type":"http","url":"https://lacrm-mcp.northkeellabs.com/mcp"}}}{"mcpServers":{"com-northkeellabs-lacrm-mcp-remote":{"serverUrl":"https://lacrm-mcp.northkeellabs.com/mcp"}}}命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。
工具
详细介绍
lacrm-mcp-remote
为 Less Annoying CRM(LACRM)提供的远程 MCP 服务,托管于 Cloudflare Workers,并支持 OAuth 2.1 登录。它提供的 10 个工具与本地 lacrm-mcp 服务相同(由 Northkeel Labs 开发,也使用 Python 和 stdio),但通过 Streamable HTTP 提供,因此可用作 claude.ai 自定义连接器,也可用于任何支持带 OAuth 的远程服务的 MCP 客户端。
独立且非官方项目。与 Less Annoying CRM 无关联,未经其认可或支持。
托管端点
https://lacrm-mcp.northkeellabs.com/mcp
Northkeel Labs 在该 URL 上运行此代码。将其粘贴到任何支持带 OAuth 的远程服务的 MCP 客户端中(参见连接客户端),登录,然后在授权页面粘贴你的 LACRM API 密钥。Worker 仅存储加密后的 OAuth 授权凭据;不会保留你的密钥或 CRM 数据副本。免费提供,尽力维护,不提供 SLA,每个连接每分钟最多 60 次工具调用。若要改为在你自己的 Cloudflare 账户上运行,请参见自行部署。
为什么采用远程服务和 OAuth
本地 stdio 服务要求每位用户安装 Python、将 API 密钥存储在配置文件中,并让进程持续运行在自己的计算机上。远程服务只需一个 URL。用户将其粘贴到 claude.ai、Claude Desktop 或 Cursor 中,在浏览器中登录一次,客户端便会获得一个短期有效、可撤销的令牌。API 密钥不会暴露给客户端、配置文件或模型上下文。
LACRM 本身不支持 OAuth,只提供 API 密钥。此服务通过“自带密钥,获取 OAuth”的模式连接两者:Worker 是符合标准的 OAuth 2.1 授权服务器(发现元数据、动态客户端注册、Client ID Metadata Documents、PKCE S256、刷新令牌轮换),用户只需在授权页面粘贴一次 LACRM 密钥。系统会通过一次只读的 LACRM 调用验证密钥,之后密钥便只存在于加密后的 OAuth 授权凭据中。
架构
flowchart LR
subgraph Client["MCP client (claude.ai, Claude Desktop, Cursor, Inspector)"]
C[OAuth + Streamable HTTP]
end
subgraph Worker["Cloudflare Worker: lacrm-mcp-remote"]
OP["OAuthProvider<br/>discovery, register, token"]
AU["Consent page at authorize<br/>key + access level"]
MCP["MCP endpoint<br/>rate limit, scope gate, 10 tools"]
RL[("RateLimiter<br/>SQLite Durable Object<br/>one per grant")]
end
KV[("KV: OAUTH_KV<br/>clients, grants, token hashes<br/>props encrypted")]
LACRM["LACRM API v2<br/>api.lessannoyingcrm.com"]
C -- discovery, DCR/CIMD, token --> OP
C -- browser sign-in --> AU
C -- Bearer token --> MCP
OP <--> KV
AU -- GetUser (validate key) --> LACRM
AU -- completeAuthorization --> OP
MCP --> RL
MCP -- key from decrypted grant --> LACRM
代码索引:src/index.ts(OAuthProvider 接线)、src/authorize.ts(授权页面)、src/mcp.ts(受保护端点)、src/tools.ts(10 个工具)、src/lacrm.ts(API 客户端、重试、错误映射)、src/security.ts(截断、不可信数据包装、脱敏)、src/ratelimit.ts(Durable Object)。
基于 @cloudflare/workers-oauth-provider 1.2.4、agents 0.33.0(createMcpHandler)和 MCP TypeScript SDK v2(@modelcontextprotocol/server 2.4.0)构建。处理程序是无状态的:每个请求都会为对应的授权凭据创建一个全新的 MCP 服务。它通过同一个 /mcp 端点同时支持 2025 年推出的 Streamable HTTP 客户端和 2026 年的无状态协议。没有 /sse 端点。McpAgent 自 agents 0.33.0 起已弃用,因此这里改用 createMcpHandler;唯一的 Durable Object 是按授权凭据隔离的速率限制器。
授权流程,一步步说明
- 客户端不带令牌调用
POST /mcp,收到401,响应中包含WWW-Authenticate: Bearer resource_metadata=".../.well-known/oauth-protected-resource/mcp", scope="lacrm.read"。 - 客户端读取受保护资源元数据(RFC 9728),然后读取位于
/.well-known/oauth-authorization-server的授权服务器元数据(RFC 8414)。 - 客户端通过 Client ID Metadata Document URL 表明身份,或在
/oauth/register注册(RFC 7591 动态客户端注册)。 - 客户端在浏览器中打开
/authorize,并使用 PKCE(仅支持 S256)、state和resource。服务器会拒绝未知客户端以及任何未注册的重定向 URI,并在本地显示错误,而不会重定向。 - 同意页面会显示发起请求的客户端信息(名称、CIMD 客户端的已验证域名、重定向主机;本地应用会显示警告),并要求输入 LACRM API 密钥和访问级别:只读(
lacrm.read)或读写(lacrm.read lacrm.write)。 - 提交时(仅限 POST;CSRF 句柄绑定到
__Host-Cookie,且只能使用一次,有效期为 10 分钟),服务器会使用该密钥调用 LACRMGetUser。如果密钥无效,则显示错误,且不会存储任何内容。 - 服务器完成授权。密钥、LACRM 用户 ID、已授予的作用域和授权 ID 会作为授权属性存储,并使用由令牌包装的密钥材料加密。随后,浏览器会携带授权码重定向到客户端。
- 客户端在
/oauth/token使用授权码和 PKCE 验证器换取访问令牌(有效期 1 小时)和轮换刷新令牌。 - 每次
/mcp调用都会按授权进行速率限制(默认每分钟 60 次)并检查作用域:只读授权在tools/list中只能看到 6 个工具,而且任何写入工具调用都会被拒绝,无论该工具是否列出。
工具
| 工具 | 访问权限 | LACRM 函数 | 功能 |
|---|---|---|---|
search_contacts | 读取 | GetContacts | 按文本搜索联系人和公司;每条记录返回摘要,支持分页 |
get_contact | 读取 | GetContact | 获取单个联系人或公司的完整记录 |
list_contact_notes | 读取 | GetNotesAttachedToContact | 获取联系人的备注,支持分页 |
list_tasks | 读取 | GetTasks | 获取指定日期范围内的任务(最长 366 天),可选联系人筛选条件 |
list_pipelines | 读取 | GetPipelines | 获取管道及其状态 ID |
list_pipeline_items | 读取 | GetPipelineItems | 获取单个管道中的项目,可选状态筛选条件 |
create_contact | 写入 | CreateContact | 新建联系人或公司;所有者默认为密钥对应的用户 |
add_note | 写入 | CreateNote | 为联系人添加纯文本备注 |
create_task | 写入 | CreateTask | 新建任务,可选关联联系人 |
update_pipeline_item_status | 写入 | EditPipelineItem | 将项目移至另一状态;不会运行自动化流程 |
没有删除工具。限制与 Python 版本一致:每页最多返回 50 条结果,并提供 has_more_results;文本字段最多截断至 2,000 个字符;列表最多包含 50 个项目;每个包含 CRM 记录的响应都会带有 data_notice,标明其中的内容是不可信数据,而非指令。在请求发送到 LACRM 之前,会使用 zod 验证输入(ID 必须是数字字符串,枚举值、ISO 日期、电子邮件格式和长度限制均会进行检查)。
部署自己的实例
你需要一个 Cloudflare 账户(免费套餐即可:一个 Worker、一个 KV 命名空间、由 SQLite 支持的 Durable Objects)以及 Node 22+。
git clone <this repo> lacrm-mcp-remote && cd lacrm-mcp-remote
npm install
npx wrangler login
npx wrangler kv namespace create OAUTH_KV
将打印出的命名空间 ID 粘贴到 wrangler.jsonc 中的 kv_namespaces[0].id(仓库中的 ID 属于托管实例,无法在你的账户中使用)。仓库配置会通过我们的自定义域名提供 Worker,因此也要更改地址:
- 使用自己的域名(该区域必须位于同一个 Cloudflare 账户中):将
routes[0].pattern设置为你的主机名,例如mcp.example.com,并将vars.MCP_RESOURCE设置为https://mcp.example.com/mcp。 - 或使用 workers.dev:删除
routes条目,设置"workers_dev": true,并将vars.MCP_RESOURCE设置为https://lacrm-mcp-remote.<your-workers-subdomain>.workers.dev/mcp(你的子域名显示在 Cloudflare 控制面板的 Workers and Pages 下)。
令牌与此精确 URL 绑定。然后:
npm test # unit and integration tests in the Workers runtime
npx wrangler deploy
无需服务器端密钥:每位用户的 LACRM 密钥都会通过授权页面提交,并且仅存储在其加密授权信息中。可选设置是 wrangler.jsonc 中的普通变量(RATE_LIMIT_PER_MINUTE,默认值为 60)。如果之后添加自己的密钥,请在生产环境中使用 npx wrangler secret put NAME,在本地使用 .dev.vars(参见 .dev.vars.example;.dev.vars 已被 gitignore 忽略)。
本地开发:将 .dev.vars.example 复制为 .dev.vars(其中会将 MCP_RESOURCE 设置为 http://localhost:8787/mcp,覆盖生产环境中的值),然后运行 npx wrangler dev,即可通过 http://localhost:8787 提供所有内容。
连接客户端
使用托管端点 https://lacrm-mcp.northkeellabs.com/mcp 或你自己的部署 URL。
-
claude.ai:依次前往 Settings > Connectors > Add custom connector。粘贴 URL,然后点击 Connect。浏览器标签页会打开授权页面;粘贴你的 LACRM 密钥(LACRM:Settings > Programmer API),并选择访问级别。
-
Claude Desktop:依次前往 Settings > Connectors > Add custom connector,URL 和操作流程与 claude.ai 相同。
-
Cursor:将配置添加到
~/.cursor/mcp.json并启用服务;Cursor 会打开浏览器进行登录。{ "mcpServers": { "lacrm": { "url": "https://lacrm-mcp.northkeellabs.com/mcp" } } } -
MCP Inspector:运行
npx @modelcontextprotocol/inspector,选择传输方式 "Streamable HTTP",输入 URL,并完成 OAuth 登录流程。针对本地开发时,请使用http://localhost:8787/mcp。
如需撤销访问权限,请在 LACRM 中删除或轮换 API 密钥。使用该密钥创建的所有授权信息都会失效,无论令牌由哪个客户端持有。
安全
摘要(详情和威胁模型请参阅 SECURITY.md):
- LACRM 密钥绝不会作为工具参数接收、返回给客户端或写入日志。LACRM 返回的错误文本会经过脱敏处理后才会出现在工具结果中。
- 授权属性(其中保存着密钥)由 OAuth 提供方加密;访问令牌、刷新令牌和授权码仅以哈希形式存储。
- 授权页面:仅接受 POST;CSRF 句柄只能使用一次,并与浏览器绑定;所有由客户端提供的文本都会进行 HTML 转义;使用
Content-Security-Policy: default-src 'none'; script-src 'none'; frame-ancestors 'none',并仅包含一个带 nonce 的内联样式;不使用 JavaScript 和第三方资源;设置X-Frame-Options: DENY和no-store。 - 严格匹配重定向 URI;未知客户端和未注册的重定向 URI 会显示为本地错误,绝不会触发重定向。
- 每次调用都会强制执行作用域限制;每项授权都有速率限制;写入操作在遇到 5xx 错误或超时时绝不会自动重试。
- CRM 内容会限制长度并标记为不可信,以降低提示词注入风险。无法彻底消除风险;写入操作时应有人类介入,进行摘要时优先使用只读授权。
测试
测试结果(2026-11-10):npm test 通过 50 项测试(通过 @cloudflare/vitest-plugin 使用 Workers 运行时;通过 fetch 模拟 LACRM);tsc --noEmit 检查通过;wrangler deploy --dry-run 执行成功;端到端实测(scripts/e2e-live.mjs 针对 wrangler dev 和一个包含演示数据的真实 LACRM 账户)20/20 个步骤通过;针对生产部署 https://lacrm-mcp.northkeellabs.com 进行的同一测试也为 20/20。已脱敏的输出:本地、生产环境。2026-11-10 还将其作为 claude.ai 自定义连接器进行了测试(只读授权)。
测试涵盖每个工具的请求映射和输出格式、截断、分页、不可信数据包装、错误映射和脱敏、重试策略、权限范围控制(只读授权既无法查看也无法调用写入工具,包括批量调用)、授权页面(错误密钥、缺少或伪造的 CSRF 令牌、未知重定向 URI、未知客户端、HTML 转义、拒绝授权、一次性句柄)、速率限制,以及确保密钥不会出现在任何响应或日志行中。
如需自行运行实时测试:先启动 npx wrangler dev,然后运行 LACRM_API_KEY=... node scripts/e2e-live.mjs。该测试会执行一次写入操作:在名为 “Maya Demo” 的联系人上添加一条备注。
免责声明
这是一个独立的非官方项目,与 Less Annoying CRM 无关,未获其认可或支持。“Less Annoying CRM” 是其所有者的商标。使用风险自负;连接生产 CRM 账户前,请先检查代码。采用 MIT 许可证,详见 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。