← Back to product·Simulmedia VAMOS Docs · v1 draft·Get started / Sandbox tutorial: drive the sandbox with an agent
Get started

Sandbox tutorial: drive the sandbox with an agent

The sandbox tutorial walks the REST path by hand. This one hands the same walk to an agent: point an MCP client at the sandbox and drive a real order from catalog browse to placed, through the seller's approval gate, without writing a request yourself. Budget twenty minutes. Every response on this page is real sandbox output.

The tools mirror the REST endpoints one to one and read the same state, so an agent and a human working the same run see the same order book. That is the part worth checking: the agent is not a side channel.

1. What you are connecting to

Seven tools over one endpoint. Six belong to the buyer and one to the seller, and which ones answer depends on the secret you present.

ToolDoesSide
list_sellersList sellers available to the authenticated buyerBuyer
search_catalogSearch a seller catalog by package typeBuyer
get_availsAvailability and locked rates for packages and ISO weeksBuyer
create_orderCreate an idempotent order using the canonical order requestBuyer
get_orderThe current folded snapshot for an orderBuyer
get_eventsReplay an order's events in sequenceBuyer
decide_orderApprove, reject, or counter an order, per unitSeller

The seed is the same one the REST tutorial uses: Acme Broadcasting, four packages, thirteen weeks. tools/list is always the authoritative set; the MCP server reference describes the fuller surface that production will carry.

2. Endpoint and credentials

https://mcp.sandbox.vamos.simulmedia.com/mcp

Streamable HTTP. Two headers on every request, and both matter:

Authorization: Bearer <your buyer or seller secret>
X-Sandbox-Session: <your run id>

You are issued two bearer secrets, one per side, because neither side can act for the other. There is no client id and no token endpoint: see Sandbox. The run id is yours to invent (a UUID is a good choice). It namespaces your fake data so nobody else's traffic touches your orders, and it is collision avoidance rather than a credential, so it is fine to share with a colleague testing alongside you.

MCP requires the session header; REST does not. Omit X-Sandbox-Session on a REST call and the sandbox mints a run for you and returns it. Omit it here and the call fails with -32600 and "Set the X-Sandbox-Session header to a unique run id", because an MCP client that silently landed in a fresh run on every call would never find the order it just created.

3. Connect two clients

Set up the buyer and the seller as two separate MCP servers: same endpoint, same run id, different secrets. A connection never switches secrets, which is the point. In the real model the buyer's agent and the seller's approver are different principals, and the sandbox holds that line rather than pretending it away.

A client that takes headers on the command line

<your-cli> mcp add --transport http vamos-buyer \
  https://mcp.sandbox.vamos.simulmedia.com/mcp \
  --header "Authorization: Bearer <BUYER_SECRET>" \
  --header "X-Sandbox-Session: <your run id>"

<your-cli> mcp add --transport http vamos-seller \
  https://mcp.sandbox.vamos.simulmedia.com/mcp \
  --header "Authorization: Bearer <SELLER_SECRET>" \
  --header "X-Sandbox-Session: <your run id>"

A client configured by file

Desktop clients that cannot set headers on a remote server directly can forward them through the mcp-remote bridge, which needs Node locally:

{
  "mcpServers": {
    "vamos-buyer": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://mcp.sandbox.vamos.simulmedia.com/mcp",
        "--header", "Authorization: Bearer <BUYER_SECRET>",
        "--header", "X-Sandbox-Session: <your run id>"
      ]
    },
    "vamos-seller": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://mcp.sandbox.vamos.simulmedia.com/mcp",
        "--header", "Authorization: Bearer <SELLER_SECRET>",
        "--header", "X-Sandbox-Session: <your run id>"
      ]
    }
  }
}

No client at all

MCP Inspector (npx @modelcontextprotocol/inspector) supports custom headers and gives you a point-and-click tool console. Or drive it with curl: see section 7.

A client that offers only a URL and an OAuth flow, with nowhere to put a bearer token or a custom header, cannot reach the sandbox and will fail its handshake. That is a sandbox limitation rather than a product one: production authenticates with OAuth, which is exactly what those clients expect. Until then, use one of the three routes above.

4. The buyer walk

On the buyer connection, ask for the whole path in one go. A well-prompted agent should need no further instruction:

Find the sellers I can reach, search Acme's daypart catalog, get
acme-prime avails for 2026-W41 at :30, then create a catalog order
for 10 preemptible spots at the rate you just read, for advertiser
"Northwind Coffee". Report the order id and its status.

What the tools return along the way, and what to check:

list_sellers
{ "sellers": [ { "seller": "acme", "name": "Acme Broadcasting",
                 "networks": ["ACME"], "level": 2,
                 "catalog_url": "/v1/sellers/acme/catalog" } ] }

search_catalog { "seller": "acme", "package_type": "daypart" }
[ "acme-prime", "acme-daytime" ]

get_avails { "package_id": "acme-prime", "weeks": ["2026-W41"], "spot_length": 30 }
{ "week": "2026-W41", "state": "available", "sellout_level": 0.62,
  "spots": { "available": 42, "total": 80 } }

get_avails takes no seller argument when the package id is unambiguous, and the rate cell it returns carries the locked_rate_id the order line has to quote. That handle is the whole price-clearance invariant: an agent transacts the price it was shown, or the order fails loudly rather than clearing at a surprise. It is the single most useful thing to verify about an agent buying on your behalf.

create_order
{ "order_id": "ord_fcf98aa84946", "external_order_id": "mcp-tutorial-01",
  "buyer_entity": "yourco", "status": "submitted",
  "created_at": "2026-10-01T14:02:11Z" }

get_order { "order_id": "ord_fcf98aa84946" }
{ "order_id": "ord_fcf98aa84946", "status": "seller_review", "last_seq": 4,
  "totals": { "gross": 4050, "currency": "USD" },
  "line_items": [ { "unit_id": "u-0001", "units": 10, "status": "pending" } ] }

It stopped at seller_review and num_accepted is 0. Nothing is booked. No amount of buyer-side retrying moves it, and that is not a sandbox shortcut: the gate is the thing the platform models. An agent cannot approve its own order any more than a buyer can.

5. Be the seller

Switch to the seller connection, same run id, and decide. Per unit, not per order, because that is how real linear approvals work:

Approve every unit on order ord_fcf98aa84946 with decide_order,
as sales@acmebroadcasting.example.
decide_order
{ "order_id": "ord_fcf98aa84946", "status": "placed",
  "units_accepted": 10, "units_countered": 0, "units_rejected": 0 }

Back on the buyer connection, replay the stream and read the whole life of the order:

get_events { "order_id": "ord_fcf98aa84946" }
[ { "seq": 1, "status": "submitted" },
  { "seq": 2, "status": "validating" },
  { "seq": 3, "status": "confirmed" },
  { "seq": 4, "status": "seller_review" },
  { "seq": 5, "status": "placed" } ]

seq is monotonic per order, so an agent can replay from any point with after_seq and never miss or double-count a transition. In production the same events arrive at your notify_url; the replay is the catch-up path, not a second source of truth.

6. Worth breaking on purpose

A demo that only shows the happy path teaches an agent nothing about the real one. Four things to try:

  • The gate is real. Ask the buyer connection to call decide_order: 403. Ask the seller connection for any buyer tool: 403. Both tools are visible in tools/list on either connection, and the secret decides which ones answer.
  • The schema refuses before the API does. Drop buyer_metadata.advertiser and the call comes back INVALID_INPUT with details.missing. The advertiser is required on every order, in every workflow: a seller's traffic desk cannot separate what it cannot name.
  • Idempotency. Repeat an identical create_order and the same acknowledgement comes back rather than a duplicate buy. Change a field under the same external_order_id and you get a 409. The comparison is byte-exact, so an agent that rebuilds its request between attempts can conflict with itself: see Conventions.
  • Isolation. Read the same order id under a different X-Sandbox-Session and it is 404. Two people testing at once do not collide.

The seed carries the rest: 2026-W43 is dark, a rate_version behind the current one returns STALE_RATE_CARD with per-unit drift, and the seller can accept part of a line or counter it instead of saying yes. The sandbox tutorial walks each one with the reasoning.

7. Raw JSON-RPC

The transport is stateless Streamable HTTP, so plain POSTs work with no client at all. One call, fully spelled out:

curl -sS https://mcp.sandbox.vamos.simulmedia.com/mcp \
  -H "Authorization: Bearer $VAMOS_BUYER" \
  -H "X-Sandbox-Session: $VAMOS_SESSION" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
       "params":{"name":"list_sellers","arguments":{}}}'

Swap method for initialize or tools/list to see the handshake, or change params.name and params.arguments for any tool. The create arguments the seed accepts:

{"seller":"acme","workflow":"catalog","external_order_id":"mcp-tutorial-01",
 "rate_version":"acme-2026q4-v1",
 "buyer_metadata":{"advertiser":"Northwind Coffee","campaign":"Q4 launch"},
 "line_items":[{"package_id":"acme-prime","provider":"National ACME",
 "network":"ACME","week":"2026-W41","units":10,"spot_length":30,
 "rate":{"class":"scatter","advertiser_class":"general",
 "buy_type":"preemptible","clearance_tier":"P2","rate_basis":"per_unit",
 "unit_cost":405,"locked_rate_id":"lr_88c1","currency":"USD"}}]}

8. When something answers wrong

SymptomWhat it means
401The secret is missing or wrong. Re-copy it; both surfaces agree on this.
403A valid secret used on the other side's tools. Working as designed: switch connections.
400, "Set the X-Sandbox-Session header"The run-id header did not make it through. Some clients drop custom headers on remote servers; use the bridge in section 3.
404 on an order you just createdThe two connections are on different run ids, or your client started a fresh one. The run id has to match across both.
The order never leaves seller_reviewExpected. Nothing advances it on a timer, and reading it again will not either. It waits for a seller decision.
An old order comes back from create_orderThe order id is derived from external_order_id, so reusing one inside the same run replays the stored acknowledgement. Use a new key, or a new run id.

9. Where to go next

  • Buy with an MCP agent: the guardrails that stay on when an agent transacts, and what a working session looks like.
  • MCP server: the full tool surface, both sides, and what is live in the sandbox today.
  • Sandbox tutorial: the same marketplace by hand, with the pricing model explained week by week.
  • The deal model: what you are actually buying when an agent picks a buy_type.