Framework recipes

Sandbox live. Create a free sandbox workspace at agentic-sandbox.pinkwallet.com (test credentials only; no money moves). Production is not yet available.

Complete agent loops for the frameworks developers ask about most. The first two were run end to end against the live sandbox (the MCP TypeScript SDK and plain REST). The others follow each vendor's current documentation for Streamable HTTP MCP servers with a Bearer header; the Pink side is identical in every case, so differences are only in how the client is constructed.

MCP TypeScript SDK (verified)

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';

const pink = new Client({ name: 'purchasing-agent', version: '1.0.0' });
await pink.connect(new StreamableHTTPClientTransport(new URL('https://agentic-sandbox.pinkwallet.com/mcp'),
  { requestInit: { headers: { Authorization: 'Bearer <agent_key>' } } }));

const call = async (name, args) => JSON.parse((await pink.callTool({ name, arguments: args })).content[0].text);

const budget = await call('pink.get_budget', {});
const dry = await call('pink.check_policy', { payee_id: 'p_cc', amount: 420, purpose: '20 kg espresso beans' });
if (dry.decision !== 'would_block') {
  let r = await call('pink.request_payment', { payee_id: 'p_cc', amount: 420, purpose: '20 kg espresso beans',
    reason: 'Bean bin at 18%', evidence: ['Quote Q-2291'], idempotency_key: 'order-2291' });
  while (r.decision === 'pending_human') { await new Promise(s => setTimeout(s, 15000)); r = await call('pink.get_credential', { hold_id: r.hold_id }); }
  if (r.credential) { /* pay the supplier with r.credential, then: */
    await call('pink.report_receipt', { payment_id: r.payment_id, receipt: { merchant: 'Counter Culture Coffee', total: 420, reference: 'INV-10442' } });
  }
}

Plain REST, any language (verified)

# 1. dry run
curl -s -X POST https://agentic-sandbox.pinkwallet.com/v1/payments/check -H "Authorization: Bearer <agent_key>" -H "Content-Type: application/json" \
  -d '{"payee_id":"p_cc","amount":420,"purpose":"20 kg espresso beans"}'
# 2. request (201 allowed · 202 pending_human · 403 blocked)
curl -s -X POST https://agentic-sandbox.pinkwallet.com/v1/payments -H "Authorization: Bearer <agent_key>" -H "Idempotency-Key: order-2291" -H "Content-Type: application/json" \
  -d '{"payee_id":"p_cc","amount":420,"purpose":"20 kg espresso beans","reason":"Bean bin at 18%"}'
# 3. poll a hold
curl -s https://agentic-sandbox.pinkwallet.com/v1/payments/pay_xxxx -H "Authorization: Bearer <agent_key>"
# 4. receipt
curl -s -X POST https://agentic-sandbox.pinkwallet.com/v1/payments/pay_xxxx/receipt -H "Authorization: Bearer <agent_key>" -H "Content-Type: application/json" \
  -d '{"merchant":"Counter Culture Coffee","total":420,"reference":"INV-10442"}'

Claude Code / Claude Desktop / Claude.ai

# Claude Code
claude mcp add --transport http pink https://agentic-sandbox.pinkwallet.com/mcp/<agent_key>

# Claude.ai and Claude Desktop: Settings → Connectors → Add custom connector → paste the same URL (no OAuth needed in the sandbox)

# then ask:
# "Check my budget with Pink, then order 20 kg of beans from Counter Culture Coffee for $420 and file the receipt."

Anthropic Messages API (MCP connector)

curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" -H "anthropic-version: 2023-06-01" -H "anthropic-beta: mcp-client-2025-11-20" \
  -H "content-type: application/json" -d '{
    "model": "claude-sonnet-5", "max_tokens": 2000,
    "mcp_servers": [{ "type": "url", "url": "https://agentic-sandbox.pinkwallet.com/mcp", "name": "pink", "authorization_token": "<agent_key>" }],
    "messages": [{ "role": "user", "content": "Reorder 20 kg of espresso beans from Counter Culture Coffee for $420. Check the policy first." }]
  }'

OpenAI Responses API (remote MCP tool)

from openai import OpenAI
client = OpenAI()
resp = client.responses.create(
    model="gpt-5",
    tools=[{ "type": "mcp", "server_label": "pink", "server_url": "https://agentic-sandbox.pinkwallet.com/mcp",
              "headers": { "Authorization": "Bearer <agent_key>" }, "require_approval": "never" }],
    input="Check my Pink budget, then request $420 to Counter Culture Coffee (payee p_cc) for 20 kg of beans.")
print(resp.output_text)

OpenAI Agents SDK (Python)

from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp

async def main():
    async with MCPServerStreamableHttp(name="pink", params={"url": "https://agentic-sandbox.pinkwallet.com/mcp",
            "headers": {"Authorization": "Bearer <agent_key>"}, "timeout": 30}, cache_tools_list=True) as pink:
        agent = Agent(name="Purchasing AI", instructions="You buy supplies for a coffee shop. Always check the policy before requesting a payment, and file the receipt after paying.", mcp_servers=[pink])
        result = await Runner.run(agent, "We are low on espresso beans. Reorder 20 kg from Counter Culture Coffee for $420.")
        print(result.final_output)

LangChain / LangGraph

from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain.agents import create_agent

client = MultiServerMCPClient({"pink": {"transport": "streamable_http", "url": "https://agentic-sandbox.pinkwallet.com/mcp",
                                          "headers": {"Authorization": "Bearer <agent_key>"}}})
tools = await client.get_tools()
agent = create_agent("anthropic:claude-sonnet-5", tools)
out = await agent.ainvoke({"messages": "Reorder 20 kg of espresso beans from Counter Culture Coffee for $420; check the policy first."})

CrewAI

from crewai import Agent, Task, Crew
from crewai_tools import MCPServerAdapter

with MCPServerAdapter({"url": "https://agentic-sandbox.pinkwallet.com/mcp", "transport": "streamable-http",
                        "headers": {"Authorization": "Bearer <agent_key>"}}) as tools:
    buyer = Agent(role="Purchasing AI", goal="Keep the kitchen stocked inside the payment policy", backstory="Coffee shop procurement agent", tools=tools)
    task = Task(description="Reorder 20 kg of espresso beans from Counter Culture Coffee for $420. Check the policy, request the payment, file the receipt.", expected_output="Decision and payment id", agent=buyer)
    Crew(agents=[buyer], tasks=[task]).kickoff()

Gemini CLI · Cursor · VS Code · Copilot Studio · n8n

# Gemini CLI
gemini mcp add --transport http --header "Authorization: Bearer <agent_key>" pink https://agentic-sandbox.pinkwallet.com/mcp

# Cursor / Windsurf  (.cursor/mcp.json)
{ "mcpServers": { "pink": { "url": "https://agentic-sandbox.pinkwallet.com/mcp", "headers": { "Authorization": "Bearer <agent_key>" } } } }

# VS Code  (.vscode/mcp.json)
{ "servers": { "pink": { "type": "http", "url": "https://agentic-sandbox.pinkwallet.com/mcp", "headers": { "Authorization": "Bearer <agent_key>" } } } }

# Microsoft Copilot Studio: Tools → Add a tool → Model Context Protocol → paste https://agentic-sandbox.pinkwallet.com/mcp/<agent_key>
# n8n: MCP Client node → HTTP Streamable → Bearer Auth credential = <agent_key>

Prompting the agent

Pink returns server instructions at initialize that explain the four-call flow, so most clients need no extra prompt. For agents you control, three lines help: check the policy before planning a purchase; always send an idempotency key; poll holds no more than every 15 seconds and stop at declined or expired.

See also: MCP tools reference · Policy rules reference · Security model.