affset

io.github.affset由 GitHub 帳號 affset 發布v0.4.0更新於 Oct 24, 2026

Run affset from chat: stats, campaigns, zones, payouts, targeting, and team.

已驗證Streamable HTTP可網頁執行效率與工作流資料與分析商業與電商

概覽

AI 產生的概覽

讓助理在聊天中操作 affset 廣告伺服器:查看流量統計,管理廣告活動、區域、流量來源、付款、定向與團隊。

可以做什麼
  • 讀取統計與稽核資料:依維度分組的流量統計、轉換紀錄、租戶身分資訊
  • 瀏覽廣告活動、區域、流量來源與團隊成員,包括含定向與付款規則的完整活動紀錄
  • 產生投放網址:區域投放位址與依活動產生的追蹤連結
  • 建立與更新廣告活動、區域、流量來源與團隊成員,先試跑再確認套用
  • 變更活動狀態、付款規則、定向規則、子標籤,並封鎖表現不佳的區域
  • 以文件資源形式讀取 affset API 參考
適用情境
適合已在使用 affset 租戶的媒體採購與聯盟團隊,用來查看投放成效、調整活動、區域、付款或定向,並把追蹤連結交給流量網路,而不必開啟後台介面。
執行需求
可使用託管遠端端點並以 affset 電子郵件 OAuth 登入,或在 Node.js 22.13 以上版本於本機執行 npm 套件。自架需要 AFFSET_BASE_URL、AFFSET_API_KEY 與 AFFSET_NAMESPACE;AFFSET_READ_ONLY 與 AFFSET_REQUEST_TIMEOUT_MS 為選用。一個連線只服務一個租戶,且需要能連上 affset API 的網路。
安裝前請注意
自架需在 AFFSET_API_KEY 提供租戶 API 金鑰,建議使用專用、最小權限且會到期的金鑰。寫入類工具會建立、更新、封鎖與刪除活動、區域、付款規則與團隊成員,create_team_member 會一次性回傳新的 API 金鑰。試跑加確認只是模型層面的保護,並非安全界線;唯讀模式才是真正的限制。統計與轉換資料來自公開端點,可能夾帶注入文字。啟用活動可能觸及方案上限。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 affset,將其新增到工作區。
  2. 連接 所需的 憑證。它們會被私密儲存,絕不會在公開頁面上展示。
  3. 為需要使用其工具的對話啟用該服務。

Web executable,透過 Streamable HTTP。 遠端服務在工作區中設定後即可從網頁執行環境執行。

交給代理程式安裝

把這段提示詞貼上到 Claude Code、Codex、Cursor 等代理程式,或 SourceWeft 對話中。代理程式會讀取該伺服器的安裝指南,先向你展示發布者、端點和登入方式,經你同意後再把它新增到你的客戶端。金鑰不會寫進任何命令。

閱讀 https://sourceweft.com/mcp/io.github.affset%2Fmcp/install.md,並按其中說明把這個 MCP 伺服器接入我的客戶端。修改任何設定之前,先告訴我它的發布者、端點和登入方式,等我確認。不要把 API 金鑰寫進命令或我會提交的檔案;問我金鑰應從哪裡取得。

檢視代理程式所依據的安裝指南

其他 MCP 客戶端

把它新增到你客戶端的 mcpServers 設定中。 客戶端會在首次連接時完成 OAuth 登入。

{
  "mcpServers": {
    "affset": {
      "type": "http",
      "url": "https://mcp.affset.com/mcp"
    }
  }
}

README

affset MCP server

An MCP server that lets a media buyer run affset from a chat client — pull stats, manage campaigns/zones/team, payouts, targeting, sub labels, and cut underperforming zones in plain language, no dashboard.

The tools wrap the existing affset tenant API. Connect through the hosted endpoint (OAuth, no API key) or run this package locally (Bearer token + X-Namespace). One connection serves one tenant.

Fastest way to connect — the hosted endpoint. Add https://mcp.affset.com/mcp as a remote MCP server in Claude (web or desktop), Cursor, Claude Code, or any client that supports streamable HTTP with OAuth: paste the URL, sign in with your affset email, keep Read-only (the consent default) or grant full access. Every connection shows up on the dashboard Integrations page and can be revoked individually. Setup guide: affset.com/integrations.

The npm package below is the self-host path: same tool roster, runs on your machine with an API key you manage yourself. Stdio defaults to full access unless you set AFFSET_READ_ONLY=true.

Tools

ToolWhat it does
whoamiShow the tenant this server is bound to: namespace, API base, derived dashboard URL, and (when readable) company / timezone / custom API domain. Read-only.
get_statsTraffic stats grouped by a dimension (date, campaign, zone, country, sub1–5, advertiser, publisher, …), optionally narrowed with advertiser_email/publisher_email filters (one user, any group_by). Returns clicks, conversions, CR, payout, media cost and ROI as a table. paid_only defaults to true (same as the dashboard) so CR excludes informative conversions. Sub columns use the tenant's sub labels when configured. Per-user groupings and filters are owner/manager plus the matching side's manager role.
list_campaignsList campaigns (status / name filter, pagination).
get_campaignOne campaign's full record — every field (untruncated offer URL, exact schedule, budgets/pacing, silent flag, payout goal type) plus its targeting rules and payout rules, in one call.
list_zonesList traffic-source zones (status / name filter, pagination, linked source).
list_teamList team members (email, role, manager). Never returns API tokens.
create_team_memberInvite a team member (owner, manager, publisher, advertiser, publisher_manager, advertiser_manager). A scoped manager key can only create its own managed role, self-assigned. Returns the new API key once — list_team never shows it again. Dry-run by default; confirm: true to apply.
create_campaignCreate a campaign from an advertiser email, offer URL, geo, payout and name. Defaults: CPA / rate 0, paused, global payout rule, and a ready tracking link (linked source template when configured; otherwise source_click_id={clickid} + sub placeholders). Dry-run by default; confirm: true to apply.
set_campaign_statusRun or pause a campaign (action: "run" | "pause"). Dry-run by default; confirm: true to apply. Running can hit the plan's active-campaign limit.
update_campaignPartial update (name, offer URL, status, rate, budgets, dates, …). Dry-run by default; confirm: true to apply. Prefer set_campaign_status for run/pause.
create_zoneCreate a traffic-source zone (name + optional postback/site/traffic-back URLs, optional traffic_source_id link). Always created active. Dry-run by default; confirm: true to apply.
update_zonePartial update (name, status, URLs, traffic_source_id). Dry-run by default; confirm: true to apply. Pass null to clear a URL or unlink the source.
list_traffic_sourcesList traffic sources — the networks bought from, each with the tracking/postback templates its linked zones use. API token shown only as set/none.
create_traffic_sourceCreate a traffic source, optionally from a network preset (exoclick, trafficstars, propellerads, adsterra, richads) that copies verified templates into an editable row. Dry-run by default; confirm: true to apply.
update_traffic_sourcePartial update (name, templates, api_token, status). Linked zones pick the new tracking template up immediately. Dry-run by default; confirm: true to apply.
list_source_bidsThe network campaigns of one traffic source with their current bids, read live from the network account (ExoClick, TrafficStars, RichAds — needs the source's API token): status, pricing model, bid in USD, last change that entered Affset's mutation stage. Read-only.
set_source_bidSet one network campaign's bid in USD using its existing pricing model (for RichAds: CPM for pops, CPC for push/display). The dry run returns expected_current_bid; pass that value back with confirm: true to bind the write to the bid reviewed, so a concurrent change is refused rather than overwritten. Records the attempt in the source's bid history and reports applied only when the network echoes the new value. Raising a bid above 5× needs allow_large_increase: true. Dry-run by default.
get_zone_urlThe /serve URL to paste into a network's campaign settings — rotates across the zone's active campaigns. A zone linked to a traffic source renders that source's tracking template; otherwise prefilled sub convention + optional cost macro. Warns when no active campaigns are visible.
get_tracking_linkThe /track/click link for an existing campaign + zone — straight to one active campaign, with no rotation or targeting checks. Renders a linked source's template like get_zone_url. Re-derives what create_campaign echoed on create.
cut_zonesBlacklist underperforming zones on a campaign by threshold (CR / spend / ROI). Dry-run by default; confirm: true to apply.
list_payout_rulesList a campaign's global + per-zone payout rules and its payout_goal_type.
set_payout_ruleUpsert a global or zone-specific payout. Dry-run by default; confirm: true to apply.
delete_payout_ruleDelete a global or zone-specific payout rule. Dry-run by default; confirm: true to apply.
set_payout_goalSet or clear payout_goal_type (goal-based conversions). Dry-run by default; confirm: true to apply.
list_targeting_typesCatalog of targeting rule types, flagging the seeded ones /serve never evaluates.
list_targeting_rulesList a campaign's targeting rules, flagging any that have no effect.
set_targeting_ruleUpsert one targeting rule (safe merge), normalised to what /serve matches. Dry-run by default; confirm: true to apply.
remove_targeting_ruleRemove one targeting rule by id or type+method. Dry-run by default; confirm: true to apply.
list_sub_labelsList tenant display names for sub1–sub5.
set_sub_labelsSet or clear sub labels (partial; null clears). Dry-run by default; confirm: true to apply.
list_conversionsList conversion audit records (payout, spend, pixel type, payload, postback). paid_only filters server-side; other optional filters are client-side on the current page.

Which URL do I give the network?

get_zone_url (/serve/{zone})get_tracking_link (/track/click/{campaign}/{zone})
Picks the campaignaffset, from the zone's rotationyou, one fixed campaign
Needs an active campaignyes — otherwise traffic back / unsoldyes — otherwise 404
Needs an active zoneyesyes
Geo & targeting rulesenforcednot enforced
cost= lands onthe impression rowthe click row

Use one or the other for a given traffic stream — never both with cost=, or the media cost is counted twice.

Both use the tenant's custom API domain when one is set, since the URL gets pasted into the network verbatim. Macros ({clickid}, [CLICK_ID], ${SUBID}) are inserted without percent-encoding — the source expands them before the request reaches affset.

cut_zones only ever adds zones to a campaign's blacklist, and does a read-merge-write so existing targeting rules are never touched.

create_campaign needs a traffic-source zone for the tracking link: pass zone_id, or let it auto-pick when the namespace has exactly one active zone. Campaigns are created paused; activate them before sending traffic through either URL. Both URL types also require an active zone. Geo whitelist is enforced in /serve only — the direct tracking link is not geo-gated, but it still requires an active, currently serviceable campaign.

Documentation resources

Beyond the tools, the server exposes the affset API reference as MCP resources, so an assistant can answer "how does conversion tracking work?" or "what does /serve accept?" from the docs themselves — not just from the tool schemas.

Resource URITypeContent
affset://docs/api-referencetext/markdownThe full API reference — endpoints, auth, roles, examples.
affset://docs/api-reference.jsonapplication/jsonThe same reference as structured data, for programmatic use.

They're the exact content published at affset.com/docs, generated from one source, and fetched at read time from AFFSET_DOCS_URL ({origin}/api-reference.md and {origin}/api-reference.json) — so they always reflect the currently published docs, not a copy pinned to this package. The fetch sends no credentials (the docs are public and live on a different origin than the tenant API). HTML SPA fallbacks, redirects, invalid JSON, and oversized bodies are rejected. Both resources are always available, including under AFFSET_READ_ONLY.

Configuration

Self-host (stdio) only — hosted connections do not use these variables. All config comes from the environment (never hard-coded):

VariableDescription
AFFSET_BASE_URLOrigin of the affset API, e.g. https://api.affset.com (no path/query/credentials). Must be https unless the host is localhost/127.0.0.1/::1 — plain http would send the API key in cleartext.
AFFSET_API_KEYTenant API key. Its namespace must match AFFSET_NAMESPACE.
AFFSET_NAMESPACETenant namespace (lowercase letters, numbers, hyphens; 3–63 chars — same rules as signup).
AFFSET_READ_ONLYOptional, default false. Set to true/1 to register only the read-only tools (whoami, get_stats, get_campaign, every list_*, get_zone_url, get_tracking_link) — every create/update/delete/cut tool is unavailable, not just gated behind confirm. See Security for why this matters.
AFFSET_REQUEST_TIMEOUT_MSOptional, default 30000. Per-request HTTP timeout in milliseconds (1000–300000).
AFFSET_DOCS_URLOptional, default https://affset.com. Origin the API-reference documentation resources are fetched from (origin only, no path). Fetched anonymously — no API key is sent here.

See .env.example.

Install

Hosted (fastest — no install)

Add the remote server in your MCP client and approve access in the browser. OAuth is discovered from the endpoint — do not paste an API key, and do not add an Authorization header.

  • Claude (web or desktop) — Customize → Connectors → + → Add custom connector → https://mcp.affset.com/mcp.
  • Cursor — Settings → MCP → Add server, transport "streamable HTTP", same URL.
  • Claude Code — claude mcp add --transport http affset https://mcp.affset.com/mcp then authenticate with /mcp.

You sign in with your affset email (magic link). Read-only is selected on the consent screen unless you switch to Full access. The connection gets its own scoped credential — your API key is never involved — and appears on the dashboard's Integrations page, where it can be revoked at any time. Full guide: affset.com/integrations.

The self-host paths below run the same tool roster over stdio and require Node.js 22.13 or newer.

From npm (recommended for self-hosting)

No clone, no build — your MCP client runs it with npx. For Claude Desktop (claude_desktop_config.json):

json
{  "mcpServers": {    "affset": {      "command": "npx",      "args": ["-y", "@affset/mcp"],      "env": {        "AFFSET_BASE_URL": "https://api.affset.com",        "AFFSET_API_KEY": "sk_live_...",        "AFFSET_NAMESPACE": "your-namespace"      }    }  }}

For Claude Code:

bash
claude mcp add affset \  -e AFFSET_BASE_URL=https://api.affset.com \  -e AFFSET_API_KEY=sk_live_... \  -e AFFSET_NAMESPACE=your-namespace \  -- npx -y @affset/mcp

Same env flags with -- npx -y github:affset/mcp if you install from GitHub instead of the npm registry (see below).

Add -e AFFSET_READ_ONLY=true for a stats/reporting-only instance (see Security).

From GitHub directly (no npm publish required)

npx can install straight from the git repo instead of the npm registry — useful if you'd rather not publish, or just want to track main without a release step:

json
{  "mcpServers": {    "affset": {      "command": "npx",      "args": ["-y", "github:affset/mcp"],      "env": {        "AFFSET_BASE_URL": "https://api.affset.com",        "AFFSET_API_KEY": "sk_live_...",        "AFFSET_NAMESPACE": "your-namespace"      }    }  }}

A push to main makes that commit available to this unpinned install path — no npm publish is required. On resolution, npm fetches the repository and runs the prepare script to build dist/ before starting the binary. npm may reuse its cache on later starts; an already running MCP process is not updated until it is restarted and npx resolves the dependency again.

For reproducible deployments, pin a reviewed ref instead of floating on main: github:affset/mcp#<commit-sha> or github:affset/mcp#<tag>. Restart the MCP process deliberately when you want it to resolve and run a newer revision.

From source

bash
git clone https://github.com/affset/mcp.git affset-mcpcd affset-mcpnpm install        # builds via the prepare script

Then point your MCP client at the built entry file — swap the npx command above for "command": "node", "args": ["/absolute/path/to/affset-mcp/dist/index.js"].

Usage examples

list paused campaigns → list_campaigns(status: "paused")

show me everything about campaign 42 → get_campaign(campaign_id: 42)

show zones → list_zones()

who's on the team? → list_team()

add [email protected] as a publisher → create_team_member(email: "[email protected]", role: "publisher") (dry-run) → confirm

stats for today by sub1 → get_stats(group_by: "sub1")

stats by advertiser → get_stats(group_by: "advertiser_email")

stats for one publisher, grouped by zone → get_stats(group_by: "zone_id", publisher_email: "[email protected]")

stats including informative conversions → get_stats(paid_only: false)

set up RichAds end to end → create_traffic_source(name: "RichAds", preset: "richads") (dry-run) → confirm → create_zone(name: "RichAds push", traffic_source_id: "…") (dry-run) → confirm → get_zone_url()

create a campaign for offer X, advertiser [email protected], geo BR, payout $2 → create_campaign(user_email: "[email protected]", offer_url: "https://offer.example/lp?s={click_id}", geo: ["BR"], payout: 2) (dry-run) → confirm

what URL do I paste into RichAds? → get_zone_url() — a zone linked to a traffic source gets the source's template filled in

give me the link for campaign 42 again → get_tracking_link(campaign_id: 42)

run campaign 42 → set_campaign_status(campaign_id: 42, action: "run") (dry-run) → confirm

pause campaign 42 → set_campaign_status(campaign_id: 42, action: "pause") (dry-run) → confirm

set zone postback → update_zone(zone_id, postback_url: "…") (dry-run) → confirm

cut zones with CR < 0.2% and spend > $5 → cut_zones(campaign_id, cr_max: 0.002, spend_min: 5) (dry-run) → confirm

show payouts for campaign 42 → list_payout_rules(campaign_id: 42)

set zone payout to $3 → set_payout_rule(campaign_id: 42, payout: 3, zone_id: "…") (dry-run) → confirm

only pay on deposit conversions → set_payout_goal(campaign_id: 42, goal_type: "deposit") (dry-run) → confirm

what targeting types exist? → list_targeting_types()

whitelist BR+MX on campaign 42 → set_targeting_rule(campaign_id: 42, type: "geo", method: "whitelist", rule: "BR,MX") (dry-run) → confirm

name sub1 Zone, sub2 Creative → set_sub_labels(sub1: "Zone", sub2: "Creative") (dry-run) → confirm

show recent conversions → list_conversions()

hide informative conversions (goal-type misses) → list_conversions(paid_only: true)

find $0 payouts (no rule / still on this page) → list_conversions(zero_payout: true)

lookup by source click id → list_conversions(source_click_id: "abc123")

Notes & limits

  • get_stats groups by one dimension per call. Drill-down is a sequence of calls, each narrowing with campaign_ids / zone_ids / sub1..sub5 / conversion_type / advertiser_email / publisher_email / paid_only filters. The two email filters select one user's campaigns or zones without changing group_by; access is limited to owner/manager or the corresponding scoped manager role. Filtering by conversion_type returns conversion rows only (impressions, clicks and media cost are zero). paid_only defaults to true (the API default is false; this matches the dashboard) so the conversions count and CR drop informative rows recorded with postback_skipped=non_goal_type — pixel type missed the campaign's payout_goal_type. Silent conversions still count; this is not a payout>0 filter. Set false for the raw count. Only recent (unfolded) events are filtered; conversions already in daily archives stay included.
  • spend means media_cost (your traffic cost). ROI / spend thresholds need cost data imported for the slice.
  • List endpoints have no server-side name search — name_contains filters the current page client-side.
  • Date-range presets, YYYY-MM-DD bounds and rendered timestamps all resolve in the tenant timezone (read once from /api/tenant), so a window lines up with the date buckets group_by=date returns instead of straddling two of them. Explicit timestamps must include Z or a UTC offset.
  • All mutations (creates, updates, cuts, deletes) stay on dry-run → confirm: true. Creates are additive once confirmed and echo what was written.
  • Activating a campaign or creating a zone can return 402 plan limit — the error surfaces dimension / current / limit.
  • Payout resolution at conversion: zone-specific → global → $0. Goal type gates spend/payout by pixel type= match; non-matching events still record at $0. Payouts go down to $0.00001, so payout amounts print at up to five decimals.
  • Changing a payout is delete + create — the API has no update and the (campaign, zone) pair is unique. set_payout_rule restores the previous payout if the create fails, and says so loudly in the one case where it cannot.
  • Targeting is enforced on /serve only — not on direct tracking links. set_targeting_rule / remove_targeting_rule merge safely; other rules are kept.
  • Targeting values are matched exactly and case-sensitively at serve time (geo from CF-IPCountry, os/browser from the user agent, device type from a fixed set). set_targeting_rule normalises what it can (br,mx → BR,MX, android → Android) and rejects what could never match — an unmatched whitelist silently stops delivery.
  • capping, weekdays and hours are seeded but never evaluated by /serve. set_targeting_rule refuses to write them (they would read as working targeting while the campaign kept buying); list_targeting_types flags them. Use unique_users (visits/hours) for frequency capping.
  • list_conversions is the conversion audit trail (not aggregated stats). The API has no campaign/zone/date filters; paid_only is the one server-side filter (true drops rows recorded with postback_skipped=non_goal_type — pixel type missed the campaign's payout_goal_type; silent conversions and other skip reasons still come back — this is not a payout>0 filter). The other optional filters apply to the current page only. Rows do not include campaign_id/zone_id. Publisher-side roles do not see spend and advertiser-side roles do not see payout, so zero_payout needs a role that can; paid_only does not (it keys on postback_skipped, not payout).
  • create_team_member creates the API key directly (like the dashboard's "Add Team Member") — it does not send an invite email. Hand the returned key to the person yourself. Revoking/removing a team member is not yet a tool; use the dashboard's Team page.
  • Out of scope: deleting campaigns/zones/conversions, billing, creative management.
  • Tenant signup is deliberately not a tool. POST /api/public/create-instance is Origin-gated and fails closed, which is what keeps signup browser-only; a server-side caller would have to spoof an allowlisted Origin to get past it. The endpoint also withholds the API key when email delivery is configured (it sends a magic link instead), and this server binds one namespace from the environment at startup — so it could not use a tenant it just created. Sign up in the dashboard, then point a server instance at the new namespace.

Using as a library

Since 0.2.0 the package doubles as a runtime-agnostic library: everything the stdio server registers (tools, docs resources, read-only stripping) is exposed as one helper that runs on any fetch-capable runtime — Node ≥22.13 or Cloudflare Workers. The hosted affset MCP gateway (mcp.affset.com) consumes exactly this surface, so the remote roster can never drift from stdio.

ts
import { registerAffsetTools, type Config } from "@affset/mcp/core";
const config: Config = {  baseUrl: "https://api.affset.com",  docsBaseUrl: "https://affset.com",  apiKey: perRequestKey, // e.g. an OAuth grant's backing credential  namespace: tenantNamespace,  requestTimeoutMs: 30_000,  readOnly: scope === "read", // never registers tools without readOnlyHint: true};
registerAffsetTools(server, config); // server: your own McpServer instance

registerAffsetTools accepts your McpServer structurally, so your own @modelcontextprotocol/sdk install works — no need to match this package's copy. Env-var loading (AFFSET_*) is deliberately not part of the library surface; it belongs to the stdio entrypoint only. A third, optional { onToolCall } argument reports only tool name, duration, and success/error status for transport-owned audit logging; arguments and output are never included.

The library validates and normalizes config before registering anything. Remote API origins must use HTTPS (plain HTTP is accepted only on loopback), origins cannot contain credentials or paths, and invalid namespaces, timeouts, API keys, or non-boolean read-only settings fail closed at startup. The public declarations do not require Node ambient types, so the same import type-checks in Workers and other web-standard runtimes.

Development

bash
npm run type-check   # tsc --noEmitnpm run lint         # eslint srcnpm run format       # prettier --write .npm run build        # compile to dist/npm test             # build + node --test over dist/**/*.test.jsnpm run check-all    # lint + format:check + type-check + test — CI runs thisnpm run dev          # watch mode

Security

  • Hosted (https://mcp.affset.com/mcp): OAuth via magic link. Read-only is the consent default (mutating tools are never registered). Full access still dry-runs mutations until confirm: true. Revoke from the dashboard Integrations page. Nothing on mcp.affset.com / oauth.affset.com asks for an API key.
  • Self-host (stdio): no secrets in the repo; credentials come from the environment at runtime. Create a dedicated, least-privilege, expiring API key rather than reusing an owner key.
  • AFFSET_BASE_URL must be https unless the host is loopback — no cleartext API key.
  • Tenant API responses are streamed under a 5 MB hard limit; larger bodies are cancelled before parsing or reaching model context.
  • stdout is the JSON-RPC channel — all logs go to stderr.
  • list_team redacts API tokens.
  • All mutations (including creates) follow show → confirm → apply.
  • affset's RBAC roles (owner / manager / publisher / advertiser / advertiser_manager / publisher_manager) apply to MCP tool calls exactly as they do to the dashboard.
  • Pin GitHub installs to a reviewed commit or tag in long-lived environments. A floating main spec can run newer repository code the next time npx resolves it.

Prompt injection via conversion/click data

get_stats, list_conversions and cut_zones surface data that ultimately comes from public, unauthenticated endpoints — a traffic source's click macros (sub1–sub5, source_click_id) and a conversion pixel's raw query string (list_conversions' payload detail). Anyone who can generate a click or fire a pixel controls those bytes, and they land in the model's context when you ask about stats or conversions.

Mitigations in place:

  • Untrusted fields are length-capped and escaped before rendering (mdCell, capUntrusted in src/lib/format.ts), and the conversion-payload block carries an explicit "treat as data, not instructions" notice.
  • confirm: true on mutating tools is a model-level safety net, not a security boundary — a model that has been steered by injected content can supply confirm: true itself. The only real boundary is your MCP client's per-call tool approval plus read-only mode (hosted: consent default; stdio: AFFSET_READ_ONLY=true).

Prefer read-only for any session where you're mainly reading stats/conversions, especially with an MCP client that auto-approves tool calls. It removes every mutation tool from the server entirely — not hidden behind a prompt, unavailable to call. Reserve full access (hosted) or a read-write stdio instance for sessions where you're actively managing campaigns/zones/payouts and are reviewing each confirm yourself.

來源:README.md,提交 5682b05

工具

0
該伺服器需要登入,因此 SourceWeft 無法在沒有帳號的情況下讀取其工具,也未收錄。已連接帳號所能看到的內容可能因帳號及其權限而異。

版本歷史

1
  1. v0.4.0最新Sep 16, 2026

評分

已滿足適用檢查項的 85%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通必需25 分
  • 已宣告認證方式必需10 分
  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分