使用说明
Ontraport MCP服务器
一个无状态的Node.js服务,实现了 模型上下文协议(MCP) 将Ontraport CRM功能作为结构化工具公开,可供任何兼容MCP的AI代理使用。
服务器是 翻译层。它接收来自AI代理的工具调用,根据Ontraport API凭据对其进行身份验证,将其转换为适当的Ontraport REST API调用,清除响应,并返回结构化结果。它不包含自己的业务逻辑。
目录
建筑
┌─────────────────┐ ┌──────────────────────────┐ ┌──────────────────┐
│ │ │ Ontraport MCP Server │ │ │
│ AI Agent │──────▶│ │──────▶│ Ontraport API │
│ (any MCP │ POST │ 1. Validate auth │ HTTP │ │
│ compatible) │ /v1 │ 2. Route JSON-RPC │ │ api.ontraport │
│ │◀──────│ 3. Translate to REST │◀──────│ .com/1/... │
│ │ │ 4. Clean response │ │ │
└─────────────────┘ └──────────────────────────┘ └──────────────────┘
关键属性:
- 基于官方MCP SDK构建 --用途
@modelcontextprotocol/sdk和StreamableHTTPServerTransport用于协议兼容的传输和会话管理。 - 无状态 --没有数据库,没有持久会话存储,没有缓存。整个州都住在昂特拉波特。内存会话仅跟踪MCP传输生命周期。
- 完整MCP端点 —
POST /v1对于请求,GET /v1对于SSE通知流,DELETE /v1用于会话终止。加GET /v1/health用于监测。 - 每会话隔离 --每个代理连接都有自己的
Server+Transport这对搭档只查看了自己的凭据。无跨会话状态泄漏。 - 动态清单 --工具列表在每次生成时都是新的
tools/list通过查询Ontraport帐户的对象元数据来调用。 - 凭证传递 --服务器转发
Api-Key和Api-Appid头直接发送到Ontraport API。它从不存储、缓存或记录凭据。
入门指南
先决条件
- Node.js 18+(使用原生
fetch)
安装并运行
npm install
npm start
默认情况下,服务器在端口3000上启动。设置 PORT 环境变量来改变它。
健康检查
curl http://localhost:3000/v1/health
# {"status":"ok"}
此端点不需要身份验证,用于正常运行时间监控。
快速测试--初始化
curl -X POST http://localhost:3000/v1 \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Api-Key: YOUR_API_KEY" \
-H "Api-Appid: YOUR_APP_ID" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","clientInfo":{"name":"test","version":"1.0"},"capabilities":{}}}'
答复:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2024-11-05",
"serverInfo": { "name": "ontraport-mcp-server", "version": "1.0.0" },
"capabilities": { "tools": {} }
}
}
认证
每一个请求 POST /v1 必须包含两个HTTP标头:
| 标题 | 类型 | 必填 | 描述 |
|---|---|---|---|
Api-Key |
string | Yes | Ontraport帐户的API密钥,作用域为所需权限。 |
Api-Appid |
string | 是 | Ontraport帐户的唯一应用程序ID |
如果任一标头丢失,服务器将在进行任何下游调用之前用HTTP 401进行响应。
服务器在每次调用时都会将这些凭据直接传递给Ontraport API。权限范围由Ontraport现有的API密钥范围系统强制执行-如果密钥缺少所需的范围,则Ontraport API会返回错误,MCP服务器会干净地显示它。
运作原理
请求的生命周期
每个请求都遵循相同的路径:
Agent sends JSON-RPC request to POST /v1
↓
Express validates Api-Key + Api-Appid headers (401 if missing)
↓
Looks up or creates a session (Server + StreamableHTTPServerTransport pair)
↓
SDK handles JSON-RPC framing, protocol negotiation, and method routing:
• initialize → returns server info + capabilities (handled by SDK)
• tools/list → our handler builds dynamic manifest via /objects/meta
• tools/call → our handler dispatches to tool implementation
↓
Tool handler:
1. Validates input parameters
2. Constructs Ontraport API request
3. Executes via OntraportClient with provided credentials
4. Cleans response (strips noise fields)
5. Returns structured result
SDK的 StreamableHTTPServerTransport 处理完整的MCP流式HTTP规范,包括会话管理(Mcp-Session-Id header),服务器通知的SSE流(GET /v1),以及会话终止(DELETE /v1).
动态清单生成
并非所有Ontraport帐户都有相同的对象。客户可以创建自定义对象,每个对象都有自己的API端点和唯一的对象类型ID。因此,工具清单不能是静态的。
当代理人来电时 tools/list,服务器:
- 呼叫
GET /objects/meta在Ontraport API上使用代理的凭据 - 标识自定义对象(不在内置对象类型ID列表中的任何对象)
- 为每个自定义对象生成4个工具定义(
get_,create_,update_,search_) - 将48个静态工具与动态生成的自定义对象工具合并
- 强制执行100个工具清单上限(请参见 舱单尺寸保护)
- 返回完整的工具列表
清单是 从未缓存.每一个 tools/list 通话引发新的 /objects/meta 请求,以便清单始终反映帐户的当前状态。
如果 /objects/meta 调用失败(凭据错误、Ontraport中断),服务器返回MCP错误——它从不返回部分清单。
工具执行
当代理人来电时 tools/call,服务器:
- 在静态处理程序注册表中查找工具名称
- 如果未找到,则检查名称是否与自定义对象模式匹配(
get_X,create_X,update_X,search_X) - 对于自定义对象工具,调用
/objects/meta动态解析对象的类型ID——这意味着tools/call独立于任何先前的作品tools/list呼叫 - 执行处理程序,该处理程序将工具参数转换为适当的Ontraport REST API调用
- 清除响应并以MCP内容格式返回
这个设计完全 无状态 --没有全局可变状态,两者之间没有依赖关系 tools/list 和 tools/call,并且来自不同帐户的并发请求是安全的。
响应清洁
在将Ontraport API响应返回给代理之前,服务器将删除添加无值噪声的字段:
system_sourcesource_locationimport_idbindexip_addy(已弃用)ip_addy_displaycontact_cat(已弃用)updateSequence(已弃用)updateCampaign(已弃用)account_id(代理已经知道它连接到哪个帐户)
对嵌套对象和数组进行递归清理。
项目结构
src/
├── index.js # Express app: auth, session management, HTTP routing
├── errors.js # Structured error builders (400–500 taxonomy)
├── mcp/
│ └── server.js # MCP Server factory: creates per-session Server instances
│ # with tools/list and tools/call handlers registered
├── manifest/
│ ├── static-tools.js # All 42 static tool definitions (single source of truth)
│ ├── custom-object-tools.js # Generates 4 tools per custom object
│ ├── builder.js # Merges static + dynamic tools, enforces 100-tool cap
│ └── builder-constants.js # Built-in object type IDs (shared constant)
├── ontraport/
│ ├── client.js # HTTP client for the Ontraport REST API
│ └── response-cleaner.js # Strips noise fields from API responses
└── tools/
├── registry.js # Tool name → handler dispatch (static + dynamic resolution)
├── contacts.js # 7 contact tools
├── tags.js # 6 tag tools
├── sequences.js # 4 sequence/campaign tools
├── tasks.js # 5 task tools
├── notes.js # 2 note tools
├── messages.js # 2 message tools
├── purchases.js # 5 purchase/order read tools
├── transactions.js # 13 transaction write tools
├── products.js # 2 product tools
├── custom-objects.js # Generic CRUD handlers for custom objects
└── metadata.js # 2 metadata/discovery tools
test/
├── health.test.js # Health endpoint
├── auth.test.js # Authentication validation (SDK transport)
├── mcp-server.test.js # MCP Server factory
├── error-handling.test.js # Error taxonomy
├── response-cleaner.test.js # Response cleaning
├── manifest.test.js # Manifest generation, cap, truncation
└── tools/
├── contacts.test.js
├── tags.test.js
├── sequences.test.js
├── tasks.test.js
├── notes.test.js
├── messages.test.js
├── purchases.test.js
├── transactions.test.js
├── products.test.js
├── custom-objects.test.js
└── metadata.test.js
设计原则
- 单一真相来源 --所有工具定义(名称、描述、输入模式)都存在于
static-tools.js。清单、输入验证和处理程序调度都来自这个文件。 - 一种模式 -每个工具处理程序都遵循相同的形状:验证输入,构造Ontraport API调用,执行它,返回结果。错误处理由客户端和传输层处理。
- 没有重复就没有抽象 --每个工具模块都是一组导出的异步函数。没有基类,没有中间件链,除了Express和MCP SDK之外没有框架。
工具参考
联系人(7个工具)
| 工具 | 描述 | API端点 |
|---|---|---|
get_contact |
通过ID或电子邮件检索单个联系人。 | GET /Contact 或 GET /object/getByEmail |
search_contacts |
按条件、标签或自由文本搜索联系人。分页(最多50个)。 | GET /Contacts |
create_contact |
创建新联系人。允许重复的电子邮件。 | POST /Contacts |
merge_or_create_contact |
通过电子邮件查找并更新,如果找不到,则创建。使用代理最安全。 | POST /Contacts/saveorupdate |
update_contact |
按ID更新现有联系人的字段 | PUT /Contacts |
delete_contact |
永久删除联系人。无法撤消。 | DELETE /Contact |
get_contact_count |
获取匹配的联系人总数,而不返回记录。 | GET /Contacts/getInfo |
标签(6工具)
| 工具 | 描述 | API端点 |
|---|---|---|
add_tag_by_name |
按名称应用标签。自动创建不存在的标签。 | PUT /objects/tagByName |
remove_tag_by_name |
按名称删除标签。默默地忽略不存在的标签。 | DELETE /objects/tagByName |
add_tag_by_id |
按ID应用标签 | PUT /objects/tag |
remove_tag_by_id |
按ID删除标签 | DELETE /objects/tag |
get_contacts_by_tag |
获取具有特定标签的联系人(按姓名或ID)。 | GET /objects/tag |
list_tags |
列出帐户中的所有标签。 | GET /Tags |
序列和活动(4个工具)
| 工具 | 描述 | API端点 |
|---|---|---|
subscribe_to_sequence |
在序列或活动中注册联系人。 | PUT /objects/subscribe |
unsubscribe_from_sequence |
从序列或活动中删除联系人。 | DELETE /objects/subscribe |
pause_rules_and_sequences |
暂停联系人的所有自动化操作。 | POST /objects/pause |
unpause_rules_and_sequences |
恢复暂停的自动化。 | POST /objects/unpause |
任务(5个工具)
| 工具 | 描述 | API端点 |
|---|---|---|
assign_task |
从消息模板创建和分配任务。 | POST /task/assign |
complete_task |
将任务标记为已完成,并带有可选结果。 | POST /task/complete |
cancel_task |
取消任务(状态2,不再可操作)。 | POST /task/cancel |
update_task |
更新受让人、到期日或状态。 | PUT /Tasks |
get_tasks |
使用可选筛选检索任务 | GET /Tasks |
注释(2个工具)
| 工具 | 描述 | API端点 |
|---|---|---|
create_note |
创建一个附加到联系人记录的注释。 | POST /objects (对象ID=12) |
get_notes |
检索笔记,可选择按联系人筛选。 | GET /objects (对象ID=12) |
消息(2个工具)
| 工具 | 描述 | API端点 |
|---|---|---|
get_messages |
列出可用的电子邮件、短信和任务消息。 | GET /Messages |
get_message |
获取一条内容完整的消息。 | GET /Message |
采购和订单(5个工具)
| 工具 | 描述 | API端点 |
|---|---|---|
get_purchases |
检索购买记录。 | GET /Purchases |
get_purchase_logs |
检索购买生命周期日志。 | GET /PurchaseHistoryLogs |
get_transactions |
检索交易/发票记录。 | GET /Transactions |
get_orders |
检索订单记录(订阅、付款计划)。 | GET /Orders |
get_open_orders |
检索活动订阅记录。 | GET /OpenOrders |
交易记录写入(13个工具)
这些工具可以修改财务记录。代理在执行之前应与用户确认。
| 工具 | 描述 | API端点 |
|---|---|---|
process_transaction |
将联系人的卡记入档案。需要提供产品和已保存的卡ID。不接受新卡详细信息。 | POST /transaction/processManual |
log_transaction |
不收费记录交易(线下销售、现金、支票)。 | POST /transaction/processManual (chargeNow=0) |
get_order |
检索交易的订单详细信息(订阅计划、下一次收费日期)。 | GET /transaction/order |
update_order |
更新订单的下一个收费日期、金额或其他字段。 | PUT /transaction/order |
cancel_subscription |
通过删除订单记录来取消活动订阅。停止所有未来的收费。 | DELETE /order |
delete_order |
删除单个订单记录(订阅或付款计划时间表)。 | DELETE /order |
convert_to_collections |
将逾期交易升级到收款状态。 | PUT /transaction/convertToCollections |
refund_transaction |
通过原始支付网关退款。无法撤消。 | PUT /transaction/refund |
void_transaction |
撤销未结算的交易。 | PUT /transaction/void |
write_off_transaction |
核销未付交易(坏账)。 | PUT /transaction/writeOff |
mark_transaction_paid |
标记为手动支付(支票、现金、电汇等)。 | PUT /transaction/markPaid |
rerun_transaction |
重试被拒绝/失败的事务。 | POST /transaction/rerun |
resend_invoice |
重新发送发票电子邮件。 | POST /transaction/resendInvoice |
产品(2个工具)
| 工具 | 描述 | API端点 |
|---|---|---|
list_products |
列出帐户中的所有产品。 | GET /Products |
get_product |
按ID获取单个产品 | GET /Product |
自定义对象(每个对象4个工具)
这些工具是 动态生成 对于通过以下方式在帐户中发现的每个自定义对象 /objects/meta。工具名称包括对象的名称和 object_type_id 自动注射。
| 模式 | 描述 | API端点 |
|---|---|---|
get_[ObjectName] |
按ID检索记录 | GET /object |
create_[ObjectName] |
创建新记录。 owner 是必需的。 |
POST /objects |
update_[ObjectName] |
更新现有记录上的字段。 | PUT /objects |
search_[ObjectName] |
按条件、文本或标签搜索。分页。 | GET /objects |
元数据和发现(2个工具)
| 工具 | 描述 | API端点 |
|---|---|---|
get_object_meta |
获取任何对象类型的字段定义。 | GET /objects/meta |
get_collection_count |
使用可选筛选获取任何对象的记录计数 | GET /objects/getInfo |
输入参考
条件参数
这 condition 参数用于所有搜索和列表工具。它是一个JSON编码的字符串,包含一组条件对象。
单一条件:
[{"field":{"field":"lastname"},"op":"=","value":{"value":"Smith"}}]
支持的操作员:
| 操作员 | 描述 |
|---|---|
= |
等于。适用于文本、数字和时间戳字段。 |
> |
大于。数字和时间戳字段。 |
< |
小于。数字和时间戳字段。 |
>= |
大于或等于 |
<= |
小于或等于 |
IN |
值在列表中。值格式: {"list":[{"value":1},{"value":2}]} |
CONTAINS |
用于列表类型字段。检查是否分配了选项。 |
DOES NOT CONTAIN |
用于列表类型字段。检查是否未分配选项。 |
复合条件:
插入 "AND" 或 "OR" 条件对象之间的字符串:
[{"field":{"field":"lastname"},"op":"=","value":{"value":"Smith"}}, "AND", {"field":{"field":"city"},"op":"=","value":{"value":"Austin"}}]
分页
所有列表/搜索工具都支持分页:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
start |
integer | 0 | 从零开始的偏移量。 |
range |
integer | 50 | 要返回的记录数。最多50。 |
sort |
string | -- | 要排序的字段名 |
sortDir |
string | -- | "asc" 或 "desc"。必须与 sort. |
时间戳
所有日期/时间字段都使用 Unix时间戳 (自1970年1月1日00:00:00 UTC以来的秒数)。在将人类可读的日期传递给任何工具之前,代理必须将其转换为Unix时间戳。
列出选择字段
列表选择类型的自定义字段需要用 */* 分隔符:
"f1500": "*/*1*/*2*/*"
使用 get_object_meta 以发现列表字段的选项ID。
错误处理
服务器将Ontraport API错误转换为结构化响应,为代理提供足够的信息来决定是否重试、修复输入或向用户报告。
| HTTP代码 | 类别 | 代理指南 |
|---|---|---|
| 200 | 成功 | 解析 data 字段从响应。 |
| 400 | 错误请求 | 不要重试。修复格式错误的输入参数。 |
| 401 | 未经授权 | 凭据无效或丢失。在没有新凭据的情况下不要重试。 |
| 403 | 禁止 | API密钥缺少所需的范围。向用户报告。 |
| 404 | 找不到 | 对象ID不存在。请在重试之前验证ID。 |
| 422 | 无法处理 | 字段验证失败(例如,电子邮件格式无效)。请修复并重试。 |
| 429 | 速率受限 | 后退并重试。检查 Retry-After 头球限额:180需求/分钟/账户。 |
| 500 | 服务器错误 | 使用指数回退重试(最多3次尝试)。 |
错误响应格式:
{
"code": 400,
"error": "bad_request",
"message": "id is required for update_contact."
}
舱单尺寸保护
如果一个帐户有许多自定义对象,则清单可能会超过LLM上下文窗口中的容量。服务器强制执行的最大值为 100工具:
- 始终包括所有48个静态工具
- 自定义对象按最近修改的内容排序
- 自定义对象工具以4个为一组(一次一个完整对象),直到达到限制
- 如果被截断,a
_manifest_note添加了一个工具,告诉代理存在其他对象,可以通过以下方式发现get_object_meta
速率限制
服务器不添加自己的速率限制。它继承了Ontraport的极限 每个账户每分钟180个API请求 透明。当超过限制时,服务器返回HTTP 429 Retry-After 来自Ontraport的标头,以便代理可以适当地回退。
安全
- 凭证传递 —
Api-Key和Api-Appid在每次呼叫时转发到Ontraport,从不存储。 - 会话凭据绑定 --会话通过SHA-256哈希绑定到凭据。返回的请求必须呈现相同的内容
Api-Key/Api-Appid这创建了会话。防止跨帐户的会话劫持。 - 在所有端点上进行身份验证 —
Api-Key和Api-Appid每个请求(POST、GET(SSE)和DELETE)都会验证标头,而不仅仅是会话创建。 - 每会话隔离 --每个代理连接都有自己的
Server实例的作用域为其凭据。会话之间没有共享的可变状态。 - 会话TTL --被放弃的会话在30分钟不活动后会自动清理。背景扫描每5分钟运行一次,以防止内存泄漏。
- 不记录凭据 --仅错误处理程序日志
err.message,永远不要在堆栈跟踪中包含可能包含凭据的完整错误对象。 - 没有毅力 --服务器没有数据库,没有文件存储。内存会话仅跟踪MCP传输生命周期。
- 范围执行 -权限由Ontraport现有的API密钥范围系统强制执行。如果键缺少作用域,API调用将失败,服务器将显示错误。
测试
npm test
测试套件包括 17个套件中的106个测试:
| 套房 | 保险范围 |
|---|---|
health.test.js |
健康端点返回200,无需身份验证 |
auth.test.js |
缺少标题→ 401,有效的身份验证+SDK初始化→ 200 |
mcp-server.test.js |
服务器工厂创建隔离的每个凭据实例 |
error-handling.test.js |
所有错误类型,转换为JSON格式,来自HttpStatus映射 |
response-cleaner.test.js |
剥离所有10个噪声场,递归清理,空处理 |
manifest.test.js |
静态工具、自定义对象、内置ID排除、100工具上限、对象边界截断、无内部字段泄漏、故障传播 |
tools/contacts.test.js |
所有7个联系工具——正确的端点、参数、验证 |
tools/tags.test.js |
所有6个标签工具——正确的有效载荷、对象ID注入 |
tools/sequences.test.js |
所有4个序列工具——sub_type默认值,正确的方法 |
tools/tasks.test.js |
所有5个任务工具——正确的车身结构 |
tools/notes.test.js |
两个笔记工具——objectID=12注入 |
tools/messages.test.js |
所有2个消息工具——正确的端点 |
tools/purchases.test.js |
所有5个采购/订单工具——正确的端点 |
tools/transactions.test.js |
所有13个事务写入工具——正确的方法和路径 |
tools/products.test.js |
两种产品工具——正确的端点 |
tools/custom-objects.test.js |
所有4个自定义对象操作——对象ID注入、验证 |
tools/metadata.test.js |
两种元数据工具——参数传递 |
配置
| 环境变量 | 默认值 | 描述 |
|---|---|---|
PORT |
3000 |
服务器监听的端口 |
什么被排除在v1之外
以下内容被有意排除在外,可能会被考虑用于未来的版本:
| 特性 | 原因 |
|---|---|
| 活动生成器 | 复杂性高,存在帐户配置错误的风险 |
| 表单 | 对代理用例的需求低 |
| Webhooks | 基于推送的模式,独立架构 |
| 散装作业 | 需要对药剂爆炸半径进行安全审查 |
| 日历事件 | 保持清单专注于CRM和商务 |
| 收件箱/对话 | 需要消息传递基础架构上下文 |
| 信用卡管理 | 安全敏感,需要确认流程 |
| 流程手册(新交易) | 财务风险、复杂报价/产品构建 |
对象类型ID引用
| 对象 | 类型ID |
|---|---|
| 联系人 | 0 |
| 任务 | 1 |
| 组 | 3 |
| 序列 | 5 |
| 规则 | 6 |
| 消息 | 7 |
| 注释 | 12 |
| 标签 | 14 |
| 产品 | 16 |
| 购买 | 17 |
| 购买历史记录日志 | 30 |
| 未结订单 | 44 |
| 交易 | 46 |
| 订单 | 52 |
| 标签订阅者 | 138 |
| 活动/自动化 | 140 |
自定义对象具有动态分配的ID,并通过以下方式发现 /objects/meta.
