关于 gam-seller-mcp-node
gam-seller-mcp-node 是一个受治理的 MCP 服务原型,让买方 Agent 发现广告库存、固定价格和可用量,同时限制数据暴露。它使用买方范围内的软意向而非广告服务器写入,并以哈希链审计账本记录决策。
主要功能
- 01公开获准查看的产品系列与价格
- 02返回分档的可用量预测
- 03创建和撤销买方范围内的意向
- 04验证工具请求身份并限流
- 05在审计账本中记录决策
- 06可选读取 GAM 预测数据
适用场景
- 01让买方 Agent 发现符合权限的广告库存
- 02查看可用量和固定标价
- 03记录买方软意向以供后续处理
- 04审计获准和拒绝的请求
MCP 相关
它通过 MCP 工具向买方 Agent 提供卖方能力发现、产品发现、预测以及买方范围内意向的创建和撤销。唯一写入面是软意向,不会创建或修改广告服务器订单。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
用于未来 GAM 集成的受治理 MCP 卖方控制平面原型。
一个 Model Context Protocol 服务,向买方 AI Agent 提供卖方广告库存信息:发现、确定价格和一种买方限定范围的软承诺机制。它不会向广告服务端写入任何数据。预测数据可以实时来自 Google Ad Manager(可选,只读的 ForecastService 快照);目录由运营方配置,未连接 GAM 时,预测数据为模拟数据。
这个项目解决什么问题?
卖方广告库存(可用量、定价、产品结构)存放在广告服务端中,其中包含商业敏感数据,有时也包含个人数据。让 AI 买方 Agent 直接访问 GAM 或类似系统的 API 会带来三种风险:
| 风险 | 不使用本项目 | 使用本项目 |
|---|---|---|
| 数据暴露过度 | Agent 可以读取原始可售库存、交易 ID 和确切底价 | 只能访问粗略分档和预先声明的产品系列 |
| 意外写入 | Agent SDK 可以创建订单、修改广告订单项 | 不会向广告服务端写入数据;唯一的写操作是买方自己的软承诺,永远不会转化为 GAM 订单或库存占用 |
| 缺乏问责 | API 调用会被记录,但无法审计 | 哈希链审计账本;每项允许或拒绝的操作都会被记录 |
工作原理
买方 Agent 通过 MCP 连接后会获得五个工具——其中三个只读,另有一个买方限定范围的 承诺机制(创建/撤销),这是唯一的写入入口:
Buyer agent
│
├── well_known_capabilities ← Signed trust anchor. Check this first.
│ Returns: RS256-signed capability document, node identity, privacy posture.
│
├── discover_products ← What can I buy here, and at what firm price?
│ Returns: product families the buyer is entitled to see (e.g. "Pre-Roll Video"),
│ each with its formats, channel and sites, and its firm list price
│ when the publisher has configured them.
│ Never returns: deal IDs, internal IDs, raw inventory, exact per-impression pricing.
│
├── get_forecast ← How available is this family next quarter?
│ Returns: Low / Mid / High availability bucket.
│ Never returns: exact impression counts, CPM curves, floor prices.
│
├── check_availability ← Can you deliver 2.8M impressions of this family in October?
│ Returns: available / partial (up to ~2.7M, ~1.3M viewable) / unavailable, as of the
│ last forecast — and, when it does not fit, where it would ("November: 3.1M").
│ Volumes come from the forecast rounded down to 2 significant figures
│ (configurable by the publisher).
│ Never returns: other buyers' bookings, floor prices, audience data.
│
├── create_intent ← Commit to a product at its current firm price (with TTL).
│ Records a firm, time-boxed buying intent — rejected if the price is stale or
│ mismatched. NOT a GAM order and NOT an inventory hold; it is the handoff artifact
│ the classic sales rails pick up. Buyer-scoped: you can only ever commit as yourself.
│
└── revoke_intent ← Withdraw one of your own active intents by id.
每次调用在运行任何领域逻辑之前,都会经过同一条处理流水线:
Buyer request
│
▼
[SEC-GATE-3] Replay detection — deduplicate client_request_id
│
▼
[Auth] RS256 token validation → identity confirmed or AUTH_FAILED
│
▼
[Policy] Surface denylist → entitlement check → scope check (Default-Deny)
│
▼
[Rate limit] N=1 / T=30s per buyer_id
│
▼
[Domain] Catalog / ForecastEngine — synthetic, seeded or live GAM forecast snapshot
│
▼
[Disclosure] Response checked against the tool's strict schema — undeclared field → withheld
│
▼
[Audit] Append-only hash-chained ledger, buyer pseudonymized (HMAC)
│
▼
Response to buyer
请求路径中的每个关卡都会在失败时拒绝请求。上图有一点需要如实说明:
client_request_id(重放防护的去重键)自 v0.9.0 起,默认要求在每个经过身份验证的接口上提供—— 不带此键的请求会被拒绝,因此无法通过省略它来绕过 SEC-GATE-3。运营方可以通过MCP_REQUIRE_IDEMPOTENCY_KEY=0显式选择退出,以兼容旧版客户端;这会重新开放该绕过途径。
限流阶段覆盖所有经过身份验证的工具——只读接口、create_intent
和 revoke_intent——因此没有任何经过身份验证的接口可以绕过限流。
磁盘上的账本若已损坏或遭篡改,系统会在启动时检测到,并拒绝提供服务 (加载时失败即关闭,并在首个请求前验证链完整性),而不是将账本重置为空链。
create_intent 会运行相同的关卡,并在记录任何内容前增加一道关卡:买方的
price_ref 必须与该产品系列当前的确定价格匹配,否则请求会被拒绝。
快速开始
用一条命令运行完整试点。
scripts/pilot.sh会根据你的配置启动节点并启用生产防护措施, 为每位有权限的买方创建一个买方令牌,并说明如何让买方 Agent 完成整个流程(发现 → 预测 → 承诺 → 撤销)——详见docs/PILOT-QUICKSTART.md。参考买方 Agent 位于examples/buyer-client-ts/agent.ts;在 TLS 后托管的完整配置步骤见deploy/。
在 MCP 客户端中安装(通过 npx)
将服务添加到你的 MCP 客户端(Claude Desktop、Claude Code、Cursor 等):
{
"mcpServers": {
"gam-seller": {
"command": "npx",
"args": ["-y", "gam-seller-mcp-node"]
}
}
}
或直接运行(stdio 传输——MCP 客户端的默认方式):
npx -y gam-seller-mcp-node
演示模式。 如果没有自己的配置,节点会基于捆绑的
pilot-publisher示例启动(包含示意性的目录、价格和预测),并在 stderr 中说明这一点——它会启动而不是报错,因此你可以立即试用这些工具。 由于买家端始终需要令牌(即使在演示模式下也不支持匿名访问),节点会在启动时打印一个可直接使用的演示买家令牌:复制该令牌,并将其作为token参数传给discover_products/get_forecast,即可查看示例产品系列、价格和预测。对于实际部署,请将
MCP_CONFIG_DIR指向包含你自己的deployment.json、catalog.json、entitlements.json和pricing.json的目录:MCP_CONFIG_DIR=/etc/gam-seller/config npx -y gam-seller-mcp-node
从源代码构建
git clone https://github.com/juan-sibbo/gam-seller-mcp-node.git
cd gam-seller-mcp-node
npm install
npm run build
npm run start:http # HTTP transport on 127.0.0.1:3900
运行完整的买家 Agent 演练(脚本化演示)——通过真正的进程内 MCP 传输驱动五个原生工具,最终展示受治理的拒绝(失败时关闭的身份验证、默认拒绝、失败时关闭的定价),并验证审计链:
npm run demo # or: npx tsx demo/run-demo.ts
使用 Docker
docker compose up
节点会在 127.0.0.1:3900 上启动。众所周知的文档位于
/.well-known/seller-mcp-capabilities。密钥和审计数据的持久化卷已在
docker-compose.yml 中预先配置。
为发布商进行配置
四个 JSON 文件决定所有针对发布商的行为,无需更改代码。将它们放在 config/(从源代码运行时)或 MCP_CONFIG_DIR 指定的目录中(npx/容器化部署时):
deployment.json # DSR contact, controller model, data retention window
catalog.json # product families + per-buyer access grants
entitlements.json # which buyers are entitled to which MCP surfaces
pricing.json # firm list prices per family (fail-closed on expiry)
forecast.json # OPTIONAL — seed availability buckets from real numbers (still synthetic-labeled)
gam.json # OPTIONAL — live GAM forecast (network, service-account key path, family → targeting)
配置无效时始终会安全失败:文件格式错误会导致节点停止,而不会在访问策略悄然改变的情况下继续运行。配置缺失(没有 config/,也没有 MCP_CONFIG_DIR)时,会回退到随附的 config/examples/pilot-publisher/ 示例——进入演示模式,并在 stderr 上提示。因此节点不会处于损坏的安装状态,只会运行真实部署或明确标注的演示。
将试点部署切换为使用真实库存(无需连接线上 GAM)只需配置即可——请参阅 docs/PUBLISHER-DEPLOYMENT.md:
- 预置预测数据:将发布商自己的可售情况从 GAM 报告导出一次,通过可选的
forecast.json提供(模板:config/examples/pilot-publisher/forecast.sample.json)。 这样可使分桶结果更贴近实际,同时所有结果仍保持synthetic: true——预加载数据不是实时读取,因此不会声称数据来自线上 GAM。 - 连接线上 GAM:使用可选的
gam.json(模板:config/examples/pilot-publisher/gam.sample.json),并在 GAM 网络中添加具有可运行预测的读取权限的服务账号。节点会在启动时以及每 30 分钟,针对每个已配置的广告系列类型 × 周期,调用ForecastService.getAvailabilityForecast,使用从不保存的预览订单项——不会在 GAM 中创建、修改或预留任何内容。系统会根据该快照,以 Low/Mid/High 分桶的形式回答买方请求,并设置synthetic: false;原始可售情况不会离开节点。优先级:gam.json>forecast.json> synthetic。 - 闭合交接流程:通过
MCP_INTENT_HANDOFF=file,将已提交的意向传递到发布商的销售流程中(本地 JSONL 文件,由操作员的转发程序监听)。交接过程不会进行出站调用——转发由操作员负责,并且系统会拒绝 URL 值。交接记录只是通知,不是 GAM 订单,也不会占用库存。 - 加强生产环境安全性:设置
MCP_REQUIRE_OPERATOR_CONFIG=1(拒绝在演示配置下启动),设置MCP_ANCHOR_SINK=tsa(将审计轨迹锚定到第三方)。
网络出口——已声明并受限,而非一律禁止。 处理买方请求时不会进行任何出站网络调用。线上 GAM 预测(gam.json)会在启动时以及每 30 分钟的刷新周期内,针对已配置的网络调用 Google OAuth 令牌端点和 Ad Manager SOAP API——这些调用绝不会发生在买方请求期间,因此买方无法给发布商的 GAM 造成负载。可选的审计锚定可在买方请求路径之外(启动时及定期锚定周期内)产生由操作员配置的网络出口:TSA 后端(MCP_ANCHOR_SINK=tsa)会将账本头部哈希提交给已配置的 RFC 3161 权威机构;S3 后端(MCP_ANCHOR_SINK=s3)会将锚定记录写入已配置的 Object Lock 存储桶;自定义接收器模块(MCP_ANCHOR_SINK=<module>)会运行操作员提供的代码。默认的本地锚定后端不会进行外部网络调用。目标地址仅来自操作员配置,买方输入无法选择目标。该网络出口范围由 tests/egress-surface.test.ts 固定:新增出站功能必须经过明确声明,否则 CI 将失败。
为什么不直接使用 GAM API?
| 方式 | 数据暴露 | 可写性 | 可审计性 | 对 AI Agent 的友好程度 |
|---|---|---|---|---|
| 原始 GAM API | 账户中的所有数据 | 完整的增删改查 | 仅日志记录 | 较差(SOAP/REST,不支持 MCP) |
| OpenRTB 出价请求 | 用户级数据、底价 | 仅出价 | 无 | 较差 |
| 此服务 | 来自实时预测的家庭组 + 可用性检查 | 仅买家自己的软性承诺(不写入 GAM) | 哈希链式账本 | 原生支持 MCP |
当前状态
工作原型。完整请求流水线(身份验证 → 策略 → 速率限制 → 领域逻辑 → 审计)、
买方范围的承诺原语(create_intent / revoke_intent,带 TTL 到期机制)、
审计账本、GDPR 数据主体权利工具包、Docker 打包、HTTP 传输,
以及实时互操作探测(Python 买方 Agent 模拟)均已实现并通过测试。
持久化层已加固,可应对重启(仅追加、原子写入、持久化轮换状态、加载失败时安全关闭),
且头哈希锚点采用仅追加方式,并支持可选的外部 WORM
后端(RFC 3161 时间戳 / S3 Object Lock)——有关剩余事项(启用实时一次写入目标属于运维基础设施操作),请参阅已知限制。
GAM 连接 — 仅用于预测:实时适配器
(src/forecast/gam-source.ts)会在存在 gam.json 时从 GAM 的
ForecastService 读取可用量。目录系列及其 GAM 定向配置(广告单元、
尺寸、环境)仍需在配置中手动映射,价格仍为静态标价。
没有 gam.json 时,预测数据为合成数据(或使用种子数据),并会标明这一点(synthetic: true)。请参阅
未解决问题了解路线图。
已知限制 — 带日期的状态。特意保留已关闭的条目:一份状态随时间变化的限制清单,既能证明坦诚,也能证明进展。
| 限制 | 定位 | 状态 | 关闭依据 |
|---|---|---|---|
归因信息(buyer_id / request_id)按条目存储,但位于链式篡改证据哈希之外 | audit/event.ts | 设计决策,并非缺陷 — 可追溯性与可擦除性之间的权衡(ADR-4) | — |
头哈希锚点每次写入都会重写整个文件(writeFileSync)——不是仅追加,也没有外部 WORM | audit/anchor.ts | ✅ 已关闭 2026-08-23 — 采用仅追加 JSONL 和可注入的 AnchorSink;通过 MCP_ANCHOR_SINK 可选择 tsa(RFC 3161)和 s3(S3 Object Lock)后端 | #92 #94 #95 |
| 外部 WORM 锚定要求运维人员指定一个实时一次写入目标(TSA URL 或已锁定的存储桶)——本节点提供后端,不提供目标 | audit/anchor-tsa.ts、audit/anchor-s3.ts | 未解决 — 部署边界(基础设施操作) | — |
client_request_id(重放防护)为可选项;省略它会绕过 SEC-GATE-3 | src/server.ts | 在 v0.9.0 中已关闭 — 默认要求所有经过身份验证的接口都提供该参数(失败时安全关闭);MCP_REQUIRE_IDEMPOTENCY_KEY=0 是运维人员明确选择的退出选项 | #82 |
| 传输过程中不使用 TLS(预期由反向代理终止 TLS) | — | 未解决 — 部署边界 | — |
revoke_intent 未经过速率限制阶段 | src/server.ts | ✅ 已关闭 2026-08-18 — 现在与所有经过身份验证的接口一样,受速率限制门控 | #80 |
npm 包未包含 GDPR DSR CLI(scripts/dsr.ts,……) | package.json files | ✅ 已关闭 2026-08 — 作为 gam-seller-dsr bin 发布 | #78 |
| 账本加载时采用失败开放策略——文件损坏会重置为空链 | audit/ledger.ts | ✅ 已关闭 2026-08-07 | #65 |
| 启动时未在提供服务前验证链完整性 | src/server.ts | ✅ 已关闭 2026-08-07 | #65 |
架构
模块图和数据流图请参阅 docs/ARCHITECTURE.md。
主要模块:
| 模块 | 作用 |
|---|---|
src/server.ts | MCP 工具定义 + 请求管道 |
src/policy/ | 默认拒绝引擎、授权存储、界面白名单/黑名单、逐工具披露模式 |
src/identity/ | RS256 密钥管理、令牌签发/验证、撤销黑名单 |
src/audit/ | 哈希链账本、HMAC 假名化、仅追加的头哈希锚定,支持可选的 WORM 后端(anchor-tsa.ts、anchor-s3.ts) |
src/pricing/ | 固定目录价存储,考虑有效期(价格过期时默认拒绝) |
src/forecast/ | 分桶引擎 + 数据源:合成、种子数据、实时 GAM 快照 |
src/gam/ | 服务账号 OAuth + 精简的 Ad Manager SOAP 客户端(只读) |
src/dsr/ | GDPR 第 15/17/18/20 条数据主体权利工具包(也以 gam-seller-dsr 二进制文件形式发布) |
src/catalog/ | 产品系列存储、逐买家访问授权 |
安全模型
默认拒绝。 除非有明确的授权,否则每个请求都会被拒绝——代码中不存在“默认允许”路径。
两道关卡:谁可以调用工具,以及工具可以返回什么。 策略层依据界面标签工作:每个经过身份验证的工具在注册时都会声明一个允许的界面;具体价格、交易 ID、原始可用量、跨买家状态、真实库存预留(软锁定)以及任何广告服务器写入操作都永久列入黑名单。标签检查不会查看响应,因此单靠它无法阻止使用允许标签的工具返回不应返回的内容。披露关卡则可以做到这一点:每个经过身份验证的工具都必须声明严格的响应模式(src/policy/disclosure.ts);任何包含未声明字段的响应都会被扣留,并替换为通用的 INTERNAL_ERROR(计入 mcp_disclosure_rejected_total)。注册新工具时必须提供该模式,因此添加买家可见字段意味着要在一个便于审查的文件中进行修改。唯一允许的写入是买家自己的承诺(create_intent / revoke_intent),这需要对界面白名单作出明确修改,并且仍限定于该买家。
不透明错误。 请求被拒绝、身份验证失败和令牌被撤销时,都会返回相同的通用 AUTH_FAILED 代码。内部原因绝不会传达给买家。
审计优先。 每项允许/拒绝决定都会在发送响应前写入账本。买家的 buyer_id 值会先经过假名化(HMAC-SHA256),再写入链中。请注意,buyer_id 和 request_id 虽然存储在每条审计记录中,但不会纳入哈希链的规范输入(audit/event.ts:50);防篡改哈希链保证不涵盖这些字段。
通过设计保护隐私。 响应仅包含库存级数据(产品系列、粗粒度分桶)。任何响应路径中都不存在用户级属性。
完整说明请参阅 docs/DESIGN-PRINCIPLES.md。
监管立场
AEPD(西班牙数据保护机构)于 2026 年 2 月发布了关于 Agentic AI 系统的指南。其中与广告库存节点最相关的四项建议,均可直接对应到现有设计决策:
| AEPD 建议 | 本节点 |
|---|---|
| 通过设计和默认设置实现保护 | 默认拒绝:除非明确授权允许访问,否则所有接口均拒绝访问 |
| 记录并存档 Agent 的操作 | 仅追加的哈希链式审计账本;每次允许或拒绝操作都会在发送响应前记录 |
| 控制向第三方传输的内容,并确保其可追溯 | 面向买方的披露允许列表(SEC-GATE-*):响应中永久禁止包含精确价格、交易 ID 和原始可用量。网络出口允许列表:唯一的出站连接是运营方选择启用的审计锚定,以及运营方选择启用的 GAM 预测刷新(见上文 网络出口),并由 CI 固定 |
| 按照目的和保留规则管理 Agent 记忆 | DSR 工具包(第 15/17/18/20 条);对审计账本强制执行可配置的保留期限 |
这一对齐情况以机器可读方式声明在经过签名的 well-known 文档
(/.well-known/seller-mcp-capabilities)中的 privacy_posture.regulatory_alignment_declared 字段里:
["GDPR", "AEPD-orientaciones-IA-agentica-2026"]。买方 Agent 或审计人员无需信任此 README,即可通过密码学方式验证这一声明。
本节点不作出法律认定——例如某项处理是否具有合法依据、同意是否有效,或特定处理是否被允许。这些判断应由控制者(广播商)作出。本节点提供机制;控制者应用判断标准。正是这一边界,使节点设计不受欧盟《数据法案》谈判结果的影响,保持稳定。
机器可读的信任锚点
/.well-known/seller-mcp-capabilities 端点会返回一个 RS256 签名的 JWT。买方 Agent 会在首次进行身份验证请求前读取并验证此文档。文档中的 privacy_posture 区块为机器可读,并通过密码学方式与节点密钥对绑定:
| 属性 | 当前值 | 含义 |
|---|---|---|
end_user_personal_data | "none" | 所有响应路径均不包含终端用户个人数据 |
audience_segmentation | "not_offered_v1" | 不提供任何受众定向接口 |
tc_string_consumption | "none" | 节点不使用 TC 字符串(服务器到服务器,PATH A) |
device_storage_access | "none" | 不访问设备存储(不适用 ePrivacy) |
jurisdiction | ["ES", "EU"] | 声明的运营司法管辖区 |
regulatory_alignment_declared | ["GDPR", "AEPD-orientaciones-IA-agentica-2026"] | 声明的合规对齐情况 |
dsr_contact | 来自 deployment.json | 数据主体请求的联系方式 |
controller_model | 来自 deployment.json | 发布商声明的控制者角色 |
audit_retention | 来自 deployment.json | 热存储/归档保留期限,以天/月为单位 |
尚未包含在 well-known 文档中的内容(仍为隐式的属性):
- 目录和预测数据是合成数据还是实时数据(
data_source) - 头哈希锚定使用本地文件还是云 Object Lock(
anchor_store) - 节点处于演示模式,还是正在使用真实发布商配置提供服务(
deployment_mode)
这些属性可帮助买方 Agent 以编程方式区分演示部署和生产部署,以及本地锚定节点和具有外部防篡改证据的节点。当前版本中尚未提供这些属性。
测试
npm test # full suite (vitest)
python3 sandbox/buyer-agent-probe.py # external Python interop probe (no shared code with server)
测试套件包括:
- 单元测试,覆盖每个模块(policy、pricing、identity、audit、catalog、forecast、DSR)
- 集成测试,使用真实的内存 MCP 传输(
tests/server.test.ts) - HTTP 传输测试,使用真实的临时端口 HTTP 服务(
tests/http.test.ts) - 端到端会话测试,模拟完整的买方 Agent 会话(
tests/buyer-agent-session.test.ts) - 外部 Python 探测程序,在不使用任何共享 Node.js 代码的情况下测试 HTTP 传输
CI 会通过 GitHub Actions 在每次推送时运行。
数据保护
原始 buyer_id 值绝不会进入审计账本,账本中只保存 HMAC 假名。
src/dsr/toolkit.ts 实现了买方审计数据的导出、限制处理和擦除(GDPR 第 15/17/18/20 条)。节点不会存储任何关于终端用户的信息;DSR 的范围恰好就是它记录的内容——B2B 买方组织的假名及其请求事件。
分发说明。 运维命令随 npm 包和容器镜像一同发布,提供 gam-seller-admin 可执行程序(issue-token、revoke-token、dsr …;gam-seller-dsr 仍可作为 DSR 命令的别名),因此无需检出代码。这些命令会写入节点状态,因此只能在节点停止时运行:运行中的节点会独占其状态目录(所有者租约),此时命令会以退出码 3 被拒绝。scripts/*.ts 条目是对同一代码的开发包装程序。
路线图
完整路线图请参阅未解决的问题。重点包括:
- GAM 库存映射——从 GAM 广告单元/版位推导系列定向,而不是手动编写
gam.json - 买方 Agent SDK——用于 MCP 买方流程的 Python 和 TypeScript 客户端库
- OpenRTB 3.0 分类体系——使
family_id方案与 IAB 标准保持一致 - 通用可观测性属性——公开
data_source/anchor_store/deployment_mode,让买方 Agent 能以编程方式区分演示环境和生产环境
(Prometheus /metrics 端点已发布——仅限环回地址,且需选择启用。)
贡献
请参阅 CONTRIBUTING.md。标记为
good first issue
的问题是不错的入门选择。
许可证
MIT——请参阅 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。