Connect your agent
Amnetic is agent-native. Your coding agent — Claude Code, Cursor, or any
MCP-capable client — connects to the marketplace over the
Model Context Protocol and gets the 13 core
MCP tools (the buyer loop enter_market / purchase, the ownership_* and
balance / list_images helpers, and the six seller tools) — plus the three
buyer-posting request tools where that surface is enabled. This page is the
deep per-client connection reference; the tools themselves — every parameter and
return shape — are single-sourced in the
MCP tools reference.
When your agent hits a knowledge or data gap, enter_market clones its
conversation context into a forgetful inner agent that runs inside our
walled exchange, evaluates real seller data, and returns a buy recommendation —
each suggested listing enriched with its public title, description, and price.
Your agent decides, then purchase buys exactly those listings and returns
ownership metadata. ownership_download retrieves the owned bytes, and the two
ownership_* tools let your agent re-access anything it already bought, in any
later session; balance checks your credit and
list_images lists the inner-agent images you can run. Everything the inner
agent saw is destroyed when the session ends; the only thing that crosses the
wall is the recommendation. You pay only for what you buy.
The fastest path is Claude Code — mint an amn_ API key in the
portal, then register the server with one command:
claude mcp add --transport http amnetic \
https://market.amnetic.ai/mcp \
--header "Authorization: Bearer amn_YOURKEY"
After it finishes, Claude Code already has the marketplace tools — skip to The loop below. The rest of this page documents the endpoint and the per-client registration for Cursor, Claude Desktop, the claude.ai browser connector, and hand-rolled streamable-HTTP consumers. For the full list of tools and their shapes, see the MCP tools reference.
The endpoint
The MCP server lives on the same host, port (443), and TLS certificate as
the REST API — there is no special port to remember. It speaks two MCP
transports, so every client can use its native one:
- Streamable HTTP at
/mcp— the recommended transport for every client (Claude Code, Cursor, the claude.ai browser connector, ChatGPT, and other current MCP clients). - HTTP+SSE at
/sse— the original transport, kept only as a legacy fallback for older SDK-only clients (andmeno-research); new setups use/mcp.
| Environment | Host | Streamable HTTP (recommended) | HTTP+SSE (legacy fallback) |
|---|---|---|---|
| Production | https://market.amnetic.ai |
…/mcp |
…/sse |
| Staging (design-partner access) | https://market.staging.amnetic.ai |
…/mcp |
…/sse |
| Local dev | http://localhost:8081 |
…/mcp |
…/sse |
Pick the row for your environment and the recommended
/mcpcolumn — e.g.https://market.amnetic.ai/mcp. The older:8443port still answers for clients configured before the move, but the port-less URL is canonical.
Reading responses: Streamable HTTP tool calls can stream multiple frames
If you use a standard MCP client library (Claude Code, Cursor, Claude
Desktop, the official MCP SDKs for Python/TypeScript/Go,
mcp-remote, the claude.ai browser connector), you don't need to think about
this — your client handles it for you. This section is only for integrators
who hand-roll their own Streamable HTTP consumer against /mcp.
The /mcp endpoint uses the standard MCP Streamable HTTP wire format (the
server is built on the official
modelcontextprotocol/go-sdk),
so a single tool call's response can arrive as multiple SSE frames on one
streamed HTTP response, not just a single JSON body:
- Each frame is a standard SSE
event: messageblock whosedata:line is one JSON-RPC 2.0 message. - Intermediate frames are progress notifications (
method: "notifications/progress", noid). A long-running tool likeenter_market— which spins up an inner agent — emits these while it works. - The terminal frame is the JSON-RPC response: the object whose
idmatches your requestidand that carriesresult(orerror). This is the frame with the actual payload.
A correct consumer reads the stream to completion and uses the frame whose
id matches the request (equivalently: the frame carrying result/error).
A naive consumer that stops after the first data: frame silently gets a
progress notification — incomplete or empty data, with no error raised. This
is the one and only trap, and it only bites hand-rolled parsers.
Do not use this one-shot POST pattern against legacy /sse. The HTTP+SSE
transport is stateful: a raw client must first open the SSE GET, read the
server's endpoint event, POST initialize, POST
notifications/initialized, and only then send tools/list or tools/call to
that session endpoint. If tools/call is the first message on a fresh /sse
session, the server correctly rejects it as a call during session
initialization. Use an MCP SDK for /sse unless you implement that full
handshake.
Minimal correct consumer (Python, stdlib only — pick the matching JSON-RPC response, don't stop at frame one):
import json, urllib.request
def call_tool(endpoint, api_key, name, arguments, req_id=1):
body = json.dumps({
"jsonrpc": "2.0", "id": req_id,
"method": "tools/call",
"params": {"name": name, "arguments": arguments},
}).encode()
req = urllib.request.Request(endpoint, data=body, method="POST", headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
# Accept BOTH: the server may answer with a bare JSON body or an SSE stream.
"Accept": "application/json, text/event-stream",
})
resp = urllib.request.urlopen(req, timeout=180)
raw = resp.read().decode()
# Bare-JSON answer (no streaming): use it directly.
if raw.lstrip().startswith("{"):
return json.loads(raw)
# SSE answer: scan every `data:` frame and return the JSON-RPC RESPONSE —
# the frame whose id matches our request (it carries result/error).
# Intermediate frames are progress notifications; do NOT stop at the first.
response = None
for line in raw.splitlines():
if not line.startswith("data:"):
continue
try:
msg = json.loads(line[len("data:"):].strip())
except json.JSONDecodeError:
continue
if isinstance(msg, dict) and msg.get("id") == req_id and (
"result" in msg or "error" in msg
):
response = msg
if response is None:
raise RuntimeError("stream closed without a matching JSON-RPC response")
return response
The rule of thumb: read until the stream closes, then pick the JSON-RPC message whose
idmatches your request. SendAccept: application/json, text/event-streamso you handle both a single JSON body and a multi-frame SSE stream. If you reach for an MCP client SDK instead of hand-parsing, all of this is handled for you.
Which surface are you connecting from?
| You use… | How it authenticates | Section |
|---|---|---|
| claude.ai (web browser) | OAuth — log in with your Amnetic account | claude.ai (browser) |
| Claude Desktop (Mac/Windows app) | Your amn_… API key, via a small bridge |
Claude Desktop |
| Claude Code (CLI / IDE) | Your amn_… API key as a header |
Claude Code |
| Cursor / other MCP client | Your amn_… API key as a header |
Cursor / generic MCP JSON |
Authenticate
MCP clients authenticate by sending your account API key as a standard bearer token on the transport:
Authorization: Bearer amn_YOURKEY
The server verifies the key once, when the connection is established. The key
never travels as a tool argument — it stays out of your agent's model
context, out of MCP client logs, and out of tool-call transcripts. A missing or
invalid bearer fails the connection with 401 before any tool runs.
To mint a key, see How to get an API key below.
Claude Code
Register the server with claude mcp add and the --header flag (re-run it with
a different URL to target another environment):
# Production
claude mcp add --transport http amnetic \
https://market.amnetic.ai/mcp \
--header "Authorization: Bearer amn_YOURKEY"
# Staging
claude mcp add --transport http amnetic \
https://market.staging.amnetic.ai/mcp \
--header "Authorization: Bearer amn_YOURKEY"
# Local dev
claude mcp add --transport http amnetic \
http://localhost:8081/mcp \
--header "Authorization: Bearer amn_YOURKEY"
Your agent now has the marketplace tools available — see the MCP tools reference for the full set.
Legacy SSE fallback. If you run an older client that predates streamable HTTP, the
/ssetransport still answers: swap--transport httpfor--transport sseand/mcpfor/sse(e.g.claude mcp add --transport sse amnetic https://market.amnetic.ai/sse --header "Authorization: Bearer amn_YOURKEY"). Prefer/mcpfor anything new —/sseis deprecated upstream and slated for removal.
amnetic CLI (optional)
The amnetic solution commands are a scriptable CLI wrapper around the same MCP
buyer tools. They use Streamable HTTP by default: pass the MCP base URL with
--mcp, and the CLI calls <base>/mcp for solution request,
solution purchase, solution ownership-list, and
solution ownership-download.
amnetic solution --mcp https://market.amnetic.ai request \
--api-key amn_YOURKEY \
--goal "Find a dataset of US retail foot traffic under $50"
amnetic solution --mcp https://market.amnetic.ai purchase \
--api-key amn_YOURKEY \
--listing-id LISTING_UUID
amnetic solution --mcp https://market.amnetic.ai purchase \
--api-key amn_YOURKEY \
--listing-id LISTING_UUID \
--offer-id OFFER_UUID \
--expect-token QUOTE_TOKEN \
--renew-of GRANT_UUID
If you have old scripts that pass https://market.amnetic.ai/sse, the CLI
normalizes that legacy URL to https://market.amnetic.ai/mcp. The API key is
still sent only as Authorization: Bearer on the MCP transport; it is never a
tool argument.
Cursor / generic MCP JSON
Add the server to .cursor/mcp.json (project) or ~/.cursor/mcp.json (global) —
the same url + headers shape works for any client that reads MCP JSON config:
{
"mcpServers": {
"amnetic": {
"url": "https://market.amnetic.ai/mcp",
"headers": { "Authorization": "Bearer amn_YOURKEY" }
}
}
}
Cursor supports environment interpolation in this file — use
"Authorization": "Bearer ${env:AMNETIC_KEY}" to keep the key out of a committed
config.
Claude Desktop
The Claude Desktop app (Mac/Windows) reaches a header-authenticated remote MCP
server through the mcp-remote
bridge. Open your Claude Desktop config —
~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or
%APPDATA%\Claude\claude_desktop_config.json (Windows) — and add Amnetic under
mcpServers, then fully quit and reopen Claude Desktop:
{
"mcpServers": {
"amnetic": {
"command": "npx",
"args": [
"-y", "mcp-remote",
"https://market.amnetic.ai/mcp",
"--header", "Authorization:${AMNETIC_AUTH}"
],
"env": { "AMNETIC_AUTH": "Bearer amn_YOURKEY" }
}
}
}
Why the
envindirection: Claude Desktop on some platforms splitsargson spaces, which would mangleAuthorization: Bearer amn_…. Passing the header value through an environment variable (Authorization:${AMNETIC_AUTH}, no space) sidesteps that.mcp-remoteneeds Node.js (npx) on your PATH.
Once it reconnects, the Amnetic tools are available in Claude Desktop —
including inside Research. Ask Claude to research something and it can call
enter_market, show you the recommended listings and prices, and (with
your go-ahead) purchase the ones you want, then use ownership_download to
cite their contents.
claude.ai (web browser) — custom connector
In the browser app you add Amnetic as a custom connector and authenticate by logging in with your Amnetic account (OAuth) — no API key to paste, no config file. This is the path that lets Amnetic show up in claude.ai's Research.
- In claude.ai go to Settings → Connectors → Add custom connector.
- Paste the Streamable HTTP URL for your environment:
- Production:
https://market.amnetic.ai/mcp - Staging:
https://market.staging.amnetic.ai/mcp
- Production:
- Click Add. Claude auto-registers with the server and opens the Amnetic sign-in page — nothing to paste. Log in with the same Amnetic account you use for the marketplace and approve access. (If a Claude build asks for an OAuth client ID under Advanced settings, get it from your portal operator; leave the secret blank.)
- Back in claude.ai, enable the Amnetic connector for your chat (and for
Research). The tools —
enter_market,purchase,ownership_list,ownership_download,balance,list_images— are now available.
There's no separate key step: the connector is bound to the account you logged in as, and identity is derived from that login (never from a request field). Your account still needs credit to consult and buy — see Credits.
Funding & accounts. The browser connector signs you into an existing Amnetic account; if you don't have one yet, sign up first (the marketplace is invite-gated). Consults and purchases draw on that account's balance exactly like every other surface.
How to get an API key
API keys (the amn_… keys) are scoped to your account; identity is derived from
the key, never from a request body. Mint one in the portal
API-key panel — sign in with your Amnetic account and create a key. The
plaintext is shown exactly once — store it now. The server keeps only a
hash and the 8-character prefix. There is no MCP tool that mints keys.
Advanced integrators who can't use the portal can mint the same key over REST (it requires a Cognito ID token from your account sign-in):
curl -sS -X POST https://market.amnetic.ai/api/v1/accounts/api-keys \
-H "Authorization: Bearer <cognito-id-token>" \
-H 'Content-Type: application/json' \
-d '{"name":"my-agent"}'
# → { "id": "...", "prefix": "amn_xxxx", "name": "my-agent", "plaintext": "amn_…" }
Revoke a key over REST with DELETE /api/v1/accounts/api-keys/{id}, or manage
keys in the portal. See Authentication.
The claude.ai browser connector needs no key — it authenticates with Cognito OAuth when you add the connector and log in. The API key is only for header-authenticated clients (Claude Code, Cursor, Claude Desktop).
The loop: enter_market → purchase
Once connected, enter_market and purchase form the whole buyer loop;
the ownership_* pair covers re-access afterwards.
1. enter_market
Hand your agent's working context into the wall. You pass the conversation
messages (the standard chat-message stream every model already produces); the
inner forgetful agent clones that context, runs against the real catalog, and
returns a recommendation — each suggested listing enriched with its public
catalog metadata, no free-form inner-agent text.
Input:
{
"messages": [
{ "role": "system", "content": "You are helping a retail analytics team." },
{ "role": "user", "content": "Find a dataset of US retail foot-traffic, weekly granularity, under $50. At least 200 locations, covers 2025-2026. Public census data is too coarse." }
],
"llm_model": "claude-opus-4-6",
"image_id": "sha256:…",
"required_rights": {
"use": "commercial",
"training": "internal"
}
}
Only messages is required, and it must include at least one user message.
llm_model (optional) picks the LLM the inner agent runs on and must exactly
match a supported Claude model ID such as claude-opus-4-6 (the default),
claude-sonnet-4-6, claude-opus-4-7, or claude-haiku-4-5; omit it for
the platform default. image_id (optional) picks the inner-agent image — call
list_images to see what's available, or omit it for the default image.
required_rights is optional and accepts minimums for use,
redistribution_scope, derived_display, training, training_serve,
training_weights, term, and attribution. It is enum-only; omitted
dimensions mean “don't care.” Omitted, null, and {} preserve the legacy
behavior. While licensing remains production-dark, the live enter_market
schema omits this argument entirely—inspect tools/list instead of sending it
optimistically.
Dark pre-ratification preview. The
licenseblock in the example below is provisional pre-purchase quote metadata available only in non-production environments whereLICENSE_TERMS_ENABLEDis on. It is absent in production pending counsel ratification and is not operative license text, a grant, or acceptance.
Output:
{
"decision": "buy",
"recommended_listings": [
{
"listing_id": "9dad1234-5678-90ab-cdef-1234567890ab",
"document_type": "data",
"title": "US Retail Foot-Traffic — Weekly, 500 locations",
"description": "Store-level weekly visit counts for 500 US retail locations, 2025–2026.",
"category": "retail",
"price_cents": 4500,
"currency": "usd",
"seller_name": "Mobility Metrics Lab",
"data_format": "text/plain",
"data_size_bytes": 18432,
"tags": ["retail", "foot-traffic", "weekly"],
"status": "active",
"created_at": "2026-06-01T14:23:11Z",
"updated_at": "2026-07-10T09:41:02Z",
"license": {
"schema_version": "lts/1",
"standard": {
"terms": { "use": "commercial", "redistribution_scope": "entity", "derived_display": "none", "training": "none", "training_serve": "none", "training_weights": "none", "term": "perpetual", "exclusivity": "none", "attribution": "not_required" },
"price_cents": 4500,
"token": "dv1:…"
},
"offers": [
{
"offer_id": "8fcb1234-5678-90ab-cdef-1234567890ab",
"offer_key": "training",
"terms": { "use": "commercial", "redistribution_scope": "entity", "derived_display": "none", "training": "internal", "training_serve": "none", "training_weights": "none", "term": "perpetual", "exclusivity": "none", "attribution": "not_required" },
"price_cents": 12500,
"currency": "usd",
"token": "8fcb1234-5678-90ab-cdef-1234567890ab"
}
]
},
"rights_fit": {
"band": "fit",
"best_offer": {
"offer_id": "8fcb1234-5678-90ab-cdef-1234567890ab",
"offer_key": "training",
"terms": { "use": "commercial", "redistribution_scope": "entity", "derived_display": "none", "training": "internal", "training_serve": "none", "training_weights": "none", "term": "perpetual", "exclusivity": "none", "attribution": "not_required" },
"price_cents": 12500,
"currency": "usd",
"token": "8fcb1234-5678-90ab-cdef-1234567890ab"
}
}
}
],
"recommended_total_cents": 12500
}
Each recommended listing carries its public catalog metadata — title,
description, category, price, seller display name, tags, format, size, status,
and timestamps — looked up platform-side, so the recommendation is decidable on
its face. In a flag-on non-production environment, it also carries the
provisional license quote ladder: the standard terms/price/token and every
active composed tier's id, key, enum-only terms, price, currency, and token.
The separate exact-text read surface is not part of this phase, which is why
this preview remains unavailable in production; do not treat the enum metadata
as operative terms or purchase from it alone. After reviewing the operative
terms through an available exact-text surface, use those selectors in a later
explicit purchase. The recommendation does not buy anything, grant rights, or
record acceptance. With the non-empty required_rights shown above, the
platform compares each visible ladder mechanically and adds rights_fit.
band is one of fit, partial, no_fit, or unknown; only fit has a
best_offer. A composed summary is copied from that listing's quote ladder; a
standard summary normalizes its visible terms/price/token with the listing
currency and offer_id: null, offer_key: "standard". It is a
provisional comparison, not legal advice, a compliance finding, permission, a
reservation, or a grant.
In active rights-fit mode, recommended_total_cents is the sum of fitting
best_offer prices—the $125 training tier therefore produces a $125 total.
Rows without a fit contribute zero, and an all-no-fit response emits the field
explicitly as 0. If required_rights is omitted, null, or {}, the legacy
standard-price sum remains unchanged (including historical omission of an
all-zero total). Both meanings are advisory snapshot arithmetic. The
response does not include raw seller ids, document body,
data_ref, data_dictionary, inner-agent reason, or confidence scores. The
inner agent cannot send prose back across the wall; everything free-form it
learned dies with the session. decision: "buy" means the inner agent found a
content match; it is not a rights verdict. In active rights-fit mode a buy may
therefore contain zero full fits—inspect every rights_fit and do not purchase a
partial/no-fit/unknown row. decision: "no_match" means no content match and the
recommended list is empty; you pay nothing. A no-match result may include a
class-only gap_report:
Inside the wall, the forgetful agent can compare the listing's license ladder as
well as its data. Its internal get_listing response always carries a non-empty
top-level license_offers list: the virtual standard tier first, then active
seller-composed tiers. Each offer contains the exact enum-only terms, price,
currency, and quote token. When licensing is enabled, the platform re-reads the
same current listing/offer records after the wall returns and attaches the
quotable ladder shown above to each outer recommendation. The inner agent still
returns only listing ids; it cannot author or alter these fields. When the flag
is off, license is omitted from recommendations.
After reviewing the operative exact text, copy both selectors from a fit into
the later purchase: send best_offer.offer_id (omit it for the standard tier)
and send best_offer.token as expect_token. Do not infer rights from the
recommendation. Purchase re-resolves the quote, and only the returned grant is
license evidence.
Daily enter_market session quota follows that same final response boundary:
only decision: "buy" with at least one recommended_listings entry consumes
the daily quota. no_match, rejected requests, setup failures, timeouts, and
platform faults do not consume daily session quota; concurrency limits still
apply while the session is active.
{
"decision": "no_match",
"gap_report": {
"unmet": [
{ "class": "freshness" },
{ "class": "granularity" },
{ "class": "price" }
]
}
}
gap_report.unmet[].class is closed vocabulary (coverage, freshness,
granularity, format, price, trust, rights). It never carries notes, reasons, or
other inner-agent prose.
If enter_market fails, treat it as a tool error with sanitized text. Use the
structured error_code when present (insufficient_credit, rate_limited,
buyer_context_invalid, platform_terminated, platform_error, and related
bounded codes) rather than parsing raw prose. Error responses do not expose
seller content, inner-agent reasoning, raw stream details, tokens, URLs, or stack
traces. A debug_id, when present, is safe to share with support.
2. purchase
You decide. Buy exactly the listings you want. The debit is atomic
(all-or-nothing), and the response confirms the ownership handles you can
re-access with ownership_download.
The MCP purchase tool spends wallet credit by default. For a single item, set
funding_mode to card_shortfall or card_full to start hosted card checkout
and receive checkout_url, session_id, status, and card funding fields.
Input:
{
"listing_ids": ["9dad1234-5678-90ab-cdef-1234567890ab"]
}
Offer-aware purchases use items instead:
{
"items": [
{
"listing_id": "9dad1234-5678-90ab-cdef-1234567890ab",
"offer_id": "optional composed-offer UUID",
"expect_token": "optional quote token",
"renew_of_grant_id": "optional grant UUID to renew"
}
]
}
Output:
{
"purchases": [
{
"listing_id": "9dad1234-5678-90ab-cdef-1234567890ab",
"seller_id": "b1c2d3e4-…",
"title": "US Retail Foot-Traffic — Weekly, 500 locations",
"price_cents": 4500,
"data_format": "text/plain",
"data_size_bytes": 52418,
"outcome": "purchased",
"offer_id": null,
"offer_key": "standard",
"terms_hash": "sha256:…",
"grant_id": "grant UUID"
}
],
"total_cents": 4500,
"balance_cents_after": 500
}
Your credit balance is debited by total_cents. A retry for rights you already
hold returns outcome: "already_licensed" instead of charging again. If the
balance can't cover the order, nothing is purchased and nothing is debited.
Card modes are single-listing checkout starts: the purchase settles after the
payment webhook. card_shortfall calculates the card charge from the current
wallet shortfall, then settlement rechecks wallet credit; if the wallet portion
changed before settlement, the payment can become wallet credit without a license.
The corresponding settlement outcome is funded_only: paid funds remain
spendable, nonwithdrawable wallet credit and no license grant is minted. This provisional
drift policy is not a promise that the stale recommendation will be fulfilled.
card_full charges the full price by card.
Offer-level conflicts return structured error_code, listing_id, and detail
fields (offer_unavailable, price_changed, or renewal_invalid).
offer_unavailable.detail.live_offers includes replacement offers with terms,
terms schema version, DLS version, price, and token.
Arbitrary uploaded-file listings must have a clean malware scan before purchase;
files still pending scan, infected, or failed closed are treated as unavailable.
3. Re-access what you bought: ownership_list + ownership_download
A purchase creates durable ownership/download access for the listing. Usage rights
are governed by the license grant returned by purchase and may expire or require
renewal. When a purchase result has left your agent's context (a later session,
a different machine), the two ownership tools recover the owned listing without
buying again:
ownership_list(no arguments) — every listing your account owns, enriched with title, description, and the price you paid:{ "owned": [ { "listing_id": "9dad1234-…", "title": "US Retail Foot-Traffic — Weekly, 500 locations", "description": "…", "category": "datasets", "price_paid_cents": 4500, "currency": "usd", "acquired_at": "2026-06-04T18:21:09Z" } ] }ownership_download({ "listing_id": "…" }) — a short-lived (5-minute) presigned download URL for a listing you own. Fortext/plainpayloads up to 256 KiB, the response also includesbodyinline:{ "download_url": "https://…", "data_format": "text/plain", "data_size_bytes": 52418, "body": "location_id,week,visits\nSAT-001,2026-W01,1843\n…" }Refuses anything your account hasn't bought, or any arbitrary uploaded file whose latest malware scan is not clean.
The same surface exists over REST if you're outside an agent: GET /api/v1/ownership and GET /api/v1/ownership/{listingId}/download. Both
paths are ownership-checked and identity comes from your bearer credential. See
the API reference.
Helpers: balance + list_images
Two read-only tools support the loop:
balance(no arguments) — your spendable credit, e.g.{ "balance_cents": 4200, "currency": "USD" }. Check it beforepurchaseinstead of discovering an unaffordable bundle through a failed buy.list_images(no arguments) — the inner-agent images you can run viaenter_market'simage_id. Each entry carries itsimage_id(a sha256 digest), aname, adescription, anddefault: truefor the one used when you omitimage_id:{ "images": [ { "image_id": "sha256:…", "name": "default-buyer-agent", "description": "The default Amnetic buyer agent.", "default": true } ] }
Credits
New accounts start with a $0 balance. Your account has one
balance, and everything an enter_market run needs draws on it:
- the
enter_marketpre-run affordability check, - the forgetful inner-agent session itself (compute + LLM run-cost, gated by the orchestrator's credit-cap check), and
- the final
purchasedebit when your agent finds a match.
Fund it in the portal with a Stripe top-up, or have the
exchange grant credit during onboarding / design-partner setup. The balance
MCP tool is read-only — it reports your credit but cannot add to it, so funding
is always a portal action. A single top-up is enough to run the whole loop —
exploring (run-cost) and buying draw down the same balance, so a long
exploration reduces the buying power left for purchases.
Next
The tools your client now has — every parameter and return shape — are in the MCP tools reference. For the flows that have no MCP tool (custom buyer-image push, buyer request posting, the signed audit record, dataset intake), see the advanced API reference.