
Gam Seller Mcp Node
io.github.juan-sibbo由 GitHub 账号 juan-sibbo 发布v0.11.1更新于 Oct 24, 2026
Governed MCP seller node: buyer agents discover inventory and register intents; no ad-server writes
概览
一个受管控的卖方广告库存 MCP 节点,让买方 AI 代理发现产品系列、查询可用性区间,并记录买方范围内的软性购买意向。
- 可以做什么
- 通过 stdio 暴露五个工具:well_known_capabilities 返回签名信任锚点;discover_products 列出已授权的产品系列及其固定标价;get_forecast 返回低/中/高可用性区间;check_availability 判断某时段能否满足请求的曝光量;create_intent/revoke_intent 记录或撤回买方范围内、带有效期的承诺。每个请求都经过重放检测、RS256 令牌校验、默认拒绝策略、速率限制、披露模式门禁和仅追加的哈希链审计账本。不存在对广告服务器的写入;唯一的写入是买方自己的软性承诺。
- 适用场景
- 当发布方希望让买方 AI 代理查看卖方库存可用性和固定价格,同时不暴露原始可用量、交易 ID 或精确底价,也不授予广告服务器写权限时使用。适合需要可管控、可审计交接产物的试点部署,而非直接访问 GAM API。
- 运行要求
- 以 npm 包形式在本地运行(npx gam-seller-mcp-node),使用 stdio 传输,需要 Node.js。可选环境变量 MCP_CONFIG_DIR 指向包含 deployment.json、catalog.json、entitlements.json 和 pricing.json 的目录;不设置时节点运行内置演示并在启动时打印演示买方令牌。买方接口始终需要 RS256 令牌。可选的实时 GAM 预测需要 gam.json 和具有读取角色的 GAM 服务账号;可选审计锚定使用 MCP_ANCHOR_SINK。
安装
在 SourceWeft 中
- 打开 控制台中的 Gam Seller Mcp Node,将其添加到工作区。
- 为需要使用其工具的对话启用该服务。
Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。
其他 MCP 客户端
参照 仓库 中的启动说明。
README
Governed MCP seller control-plane prototype for future GAM integration.
[npm version] [npm downloads] [CI] [License: MIT] [TypeScript] [MCP]
A Model Context Protocol server that exposes sell-side ad inventory to buyer-side AI agents: discovery, firm pricing, and a buyer-scoped soft commitment primitive. No writes to an ad server exist. Forecasts can come live from Google Ad Manager (opt-in, read-only ForecastService snapshot); the catalog is operator config, and without a GAM connection forecast data is synthetic.
What problem does this solve?
Sell-side ad inventory (availability, pricing, product structure) lives inside ad servers that hold commercially sensitive and sometimes personal data. Giving an AI buyer agent direct API access to GAM or a similar system creates three risks:
How it works
A buyer agent connects via MCP and gets five tools — three read-only, plus a buyer-scoped commitment primitive (create/revoke) that is the sole write surface:
Every call flows through the same pipeline before any domain logic runs:
Each request-path gate rejects on failure. One honest caveat to the diagram above:
client_request_id(the replay-guard deduplication key) is required by default on every authenticated surface since v0.9.0 — a request without it is rejected, so SEC-GATE-3 cannot be bypassed by omission. An operator can explicitly opt out for legacy clients withMCP_REQUIRE_IDEMPOTENCY_KEY=0, which reopens that bypass.
The rate-limit stage covers every authenticated tool — the read surfaces, create_intent,
and revoke_intent — so no authenticated surface bypasses it.
A corrupted or tampered on-disk ledger is detected on startup and the node refuses to serve (fail-closed on load, plus a chain-integrity verify before the first request) rather than resetting to an empty chain.
create_intent runs the same gates and adds one more before it records anything: the buyer's
price_ref must match the family's current firm price, or the request is rejected.
Quick start
Run a full pilot in one command.
scripts/pilot.shbrings the node up on your config with production guards on, mints a buyer token per entitled buyer, and prints how to drive a buyer agent through the whole loop (discover → forecast → commit → revoke) — seedocs/PILOT-QUICKSTART.md. The reference buyer agent lives atexamples/buyer-client-ts/agent.ts; hosting behind TLS is a filled-in-the-blanks recipe indeploy/.
Install in an MCP client (via npx)
Add the server to your MCP client (Claude Desktop, Claude Code, Cursor, …):
Or run it directly (stdio transport — the default for MCP clients):
Demo mode. With no config of your own, the node boots on a bundled
pilot-publisherexample (illustrative catalog, prices and forecasts) and says so on stderr — it starts instead of failing, so you can try the tools immediately. Because buyer surfaces always require a token (there is no anonymous path, even in demo), the node prints a ready-to-use demo buyer token on startup: copy it and pass it as thetokenargument todiscover_products/get_forecastto see the example families, prices and forecasts.For a real deployment, point
MCP_CONFIG_DIRat a directory holding your owndeployment.json,catalog.json,entitlements.jsonandpricing.json:
From source
Run the full buyer-agent walkthrough (scripted demo) — the five native tools driven over a real in-process MCP transport, ending in the governed refusals (fail-closed auth, Default-Deny, fail-closed pricing) and a verified audit chain:
With Docker
The node starts on 127.0.0.1:3900. The well-known document is at
/.well-known/seller-mcp-capabilities. Persistent volumes for keys and audit data are
pre-configured in docker-compose.yml.
Configure for your publisher
Four JSON files drive all publisher-specific behaviour — no code changes needed. Place them
in config/ (from-source) or in the directory named by MCP_CONFIG_DIR (npx/containerised):
Invalid config always fails closed: a malformed file stops the node rather than running
with a silently different access policy. Absent config (no config/ and no MCP_CONFIG_DIR)
drops to the bundled config/examples/pilot-publisher/
example — demo mode, announced on stderr — so the node is never a broken install, only ever a
real deployment or a clearly-labelled demo.
Taking a pilot onto real inventory (short of a live GAM connection) is all configuration —
see docs/PUBLISHER-DEPLOYMENT.md:
- Seed the forecast with the publisher's own availability, exported once from a GAM report,
via an optional
forecast.json(template:config/examples/pilot-publisher/forecast.sample.json). Buckets become realistic while every result stayssynthetic: true— pre-loaded is not a live read, so no live-GAM claim is made. - Connect GAM live with an optional
gam.json(template:config/examples/pilot-publisher/gam.sample.json) and a service account added to the GAM network with a read role that can run forecasts. The node asksForecastService.getAvailabilityForecastfor every configured family × period at boot and every 30 minutes, using prospective line items that are never saved — nothing in GAM is created, modified or reserved. Buyers are answered from that snapshot as Low/Mid/High buckets withsynthetic: false; raw availability never leaves the node. Precedence:gam.json>forecast.json> synthetic. - Close the handoff loop so a committed intent reaches the publisher's sales rails, via
MCP_INTENT_HANDOFF=file(a local JSONL drop an operator forwarder tails). The handoff makes no outbound call — forwarding is the operator's process, and a URL value is refused. A handoff record is a notification, never a GAM order or inventory hold. - Harden for the road:
MCP_REQUIRE_OPERATOR_CONFIG=1(refuse to boot on demo config),MCP_ANCHOR_SINK=tsa(anchor the audit trail to a third party).
Network egress — declared and bounded, not deny-all. Buyer request handling makes no outbound
network calls. The live GAM forecast (gam.json) calls Google's OAuth token endpoint and the Ad
Manager SOAP API for the configured network, at boot and on the 30-minute refresh cycle — never
inside a buyer request, so buyers cannot generate load on the publisher's GAM. Optional audit anchoring can generate operator-configured egress outside the buyer
request path (at boot and on the periodic anchor cycle): the TSA backend (MCP_ANCHOR_SINK=tsa)
submits the ledger head hash to the configured RFC 3161 authority; the S3 backend
(MCP_ANCHOR_SINK=s3) writes the anchor record to the configured Object Lock bucket; a custom
sink module (MCP_ANCHOR_SINK=<module>) runs operator-supplied code. The default local anchor
backend performs no external network call. Destinations come only from operator configuration —
no buyer input can choose one. This surface is pinned by
tests/egress-surface.test.ts: a new outbound capability fails CI
until it is declared on purpose.
Why not just use the GAM API directly?
Current status
Working prototype. The full request pipeline (auth → policy → rate-limit → domain → audit),
the buyer-scoped commitment primitive (create_intent / revoke_intent, with TTL expiry),
the audit ledger, GDPR data-subject-rights toolkit, Docker packaging, HTTP transport,
and a live interop probe (Python buyer agent simulation) are all implemented and tested.
The persistence layer is hardened for restarts (append-only, atomic writes, durable rotation
state, fail-closed load), and the head-hash anchor is append-only with selectable external WORM
backends (RFC 3161 timestamping / S3 Object Lock) — see Known limitations
for the residual (a live write-once destination is an operator infra act).
GAM connection — forecast only: the live adapter
(src/forecast/gam-source.ts) reads availability from GAM's
ForecastService when gam.json is present. Catalog families and their GAM targeting (ad units,
sizes, environment) are still mapped by hand in config, and prices remain static list prices.
Without gam.json the forecast is synthetic (or seeded) and says so (synthetic: true). See the
open issues for the roadmap.
Known limitations — dated status. Closed rows are kept on purpose: a limitations list that changes state over time is both a proof of honesty and a proof of progress.
Architecture
See docs/ARCHITECTURE.md for the module map and data-flow diagrams.
Key modules:
Security model
Default-Deny. Every request is denied unless an explicit entitlement says otherwise — there is no "allow by default" path in the code.
Two gates: who may call a tool, and what it may return. The policy layer works on surface
labels: each authenticated tool declares one allowed surface when it is registered, and exact
pricing, deal IDs, raw availability numbers, cross-buyer state, real inventory holds (soft-lock)
and any ad-server write are permanently on the denylist. That label check does not look at the
response, so it cannot on its own stop a tool under an allowed label from returning something it
shouldn't. The disclosure gate does: every authenticated tool must declare a strict response
schema (src/policy/disclosure.ts), and a response carrying any undeclared field is withheld and
replaced by a generic INTERNAL_ERROR (counted on mcp_disclosure_rejected_total). A new tool
cannot be registered without that schema, so adding a buyer-visible field means changing it in one
reviewable file. The one permitted write is a buyer's own commitment (create_intent /
revoke_intent), which required an explicit amendment to the surface allowlist and stays
buyer-scoped.
Opaque errors. A denied request, a failed authentication, and a revoked token all return the
same generic AUTH_FAILED code. Internal reasons never reach the buyer.
Audit-first. Every allow/deny is written to the ledger before the response is sent.
Buyer buyer_id values are pseudonymized (HMAC-SHA256) before entering the chain. Note that
buyer_id and request_id, while stored in each audit entry, are not included in the
hash-chain's canonical input (audit/event.ts:50); those fields are not covered by the
chain's tamper-evidence guarantee.
Privacy by construction. Responses carry only inventory-level data (product family, coarse bucket). User-level attributes don't exist in any response path.
See docs/DESIGN-PRINCIPLES.md for the full reasoning.
Regulatory posture
The AEPD (Spain's data protection authority) published guidelines on agentic AI systems in February 2026. The four recommendations most relevant to an ad-inventory node map directly to existing design decisions:
This alignment is declared machine-readably in the signed well-known document
(/.well-known/seller-mcp-capabilities) under privacy_posture.regulatory_alignment_declared:
["GDPR", "AEPD-orientaciones-IA-agentica-2026"]. A buyer agent or auditor can verify it
cryptographically without trusting this README.
The node does not make legal determinations — whether a given processing has a legitimate basis, whether consent is valid, whether a particular treatment is permitted. Those judgements belong to the controller (the broadcaster). The node provides the mechanisms; the controller applies the criteria. This boundary is what keeps the node's design stable regardless of how the EU Data Act negotiations resolve.
Machine-readable trust anchor
The /.well-known/seller-mcp-capabilities endpoint returns an RS256-signed JWT. A buyer agent
reads and verifies this document before the first authenticated request. The privacy_posture
block inside it is machine-readable and cryptographically bound to the node's keypair:
Not yet in the well-known document (properties that remain implicit):
- Whether the catalog and forecast data are synthetic or live (
data_source) - Whether head-hash anchoring uses a local file or cloud Object Lock (
anchor_store) - Whether the node is in demo mode or serving a real publisher config (
deployment_mode)
These properties would allow a buyer agent to programmatically distinguish a demo deployment from a production one, and a locally-anchored node from one with external tamper-evidence. They are not present in the current version.
Testing
The test suite includes:
- Unit tests for each module (policy, pricing, identity, audit, catalog, forecast, DSR)
- Integration tests over real in-memory MCP transports (
tests/server.test.ts) - HTTP transport tests over a real ephemeral-port HTTP server (
tests/http.test.ts) - End-to-end session tests simulating a full buyer-agent session (
tests/buyer-agent-session.test.ts) - External Python probe that exercises the HTTP transport without any shared Node.js code
CI runs on every push via GitHub Actions.
Data protection
Raw buyer_id values never enter the audit ledger — only an HMAC pseudonym. The
src/dsr/toolkit.ts implements export, restriction, and erasure of a
buyer's audit data (GDPR Art. 15/17/18/20). The node stores nothing about end users; the DSR
scope is exactly what it records — B2B buyer organization pseudonyms and their request events.
Distribution note. Operator commands ship in the npm package and the container image as the
gam-seller-admin bin (issue-token, revoke-token, dsr …; gam-seller-dsr remains as an alias
for the DSR commands), so no checkout is needed. They write the node's state and therefore run
only while the node is stopped: a running node owns its state directory (owner lease) and the
command is refused with exit code 3. The scripts/*.ts entries are dev wrappers over the same code.
Roadmap
See the open issues for the full roadmap. Highlights:
- GAM inventory mapping — derive family targeting from GAM ad units/placements instead of hand-written
gam.json - Buyer agent SDKs — Python and TypeScript client libraries for the MCP buyer flow
- OpenRTB 3.0 taxonomy — align
family_idscheme with IAB standards - Well-known observability properties — expose
data_source/anchor_store/deployment_modeso a buyer agent can distinguish demo from production programmatically
(The Prometheus /metrics endpoint is already shipped — loopback-only, opt-in.)
Contributing
See CONTRIBUTING.md. Issues tagged
good first issue
are a good starting point.
License
MIT — see LICENSE.
来源:README.md,提交 e6a8bce
工具
0版本历史
4- v0.11.1最新Sep 30, 2026
- v0.10.0Sep 29, 2026
- v0.9.0Sep 29, 2026
- v0.8.13Sep 16, 2026
评分
已满足适用检查项的 50%
该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。
- 近 30 天内可连通25 分
请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。
- 已声明认证方式10 分
请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。
- 已列出工具15 分
请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。
- 描述必需15 分
- 图标10 分
- 文档15 分
- 许可证10 分
