Example: Multi-API Workflow (GitHub + Stripe)¶
This end-to-end example shows a billing reconciliation workflow that reads open invoices from GitHub Sponsors and creates corresponding Stripe payment intents.
Overview¶
User: "Create payment intents for all overdue GitHub Sponsors invoices"
↓
PlannerGraph (mixed execution)
├── github:list_sponsors_invoices (read)
├── stripe:list_customers (read, parallel with above)
└── for each invoice:
├── stripe:retrieve_customer (read, parallel)
└── stripe:create_payment_intent (write, sequential)
Setup¶
# Generate both servers
api2mcp generate github.yaml --output ./github-server
api2mcp generate stripe.yaml --output ./stripe-server
# Set credentials
export GITHUB_TOKEN="ghp_your_token"
export STRIPE_SECRET_KEY="sk_test_your_key"
Full Workflow Code¶
import asyncio
import os
from langchain_anthropic import ChatAnthropic
from langgraph.checkpoint.sqlite import SqliteSaver
from api2mcp.orchestration.adapters.registry import MCPToolRegistry
from api2mcp.orchestration.graphs.planner import PlannerGraph
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def billing_reconciliation():
registry = MCPToolRegistry()
# Connect GitHub server
github_params = StdioServerParameters(
command="python", args=["github-server/server.py"]
)
# Connect Stripe server
stripe_params = StdioServerParameters(
command="python", args=["stripe-server/server.py"]
)
async with stdio_client(github_params) as (gr, gw):
async with ClientSession(gr, gw) as github_session:
await registry.register_server("github", github_session)
async with stdio_client(stripe_params) as (sr, sw):
async with ClientSession(sr, sw) as stripe_session:
await registry.register_server("stripe", stripe_session)
# Show registered tools
print("Registered tools:")
for tool in registry.get_tools():
print(f" {tool.name}")
# Run the workflow
model = ChatAnthropic(
model="claude-sonnet-4-6",
api_key=os.environ["ANTHROPIC_API_KEY"],
)
checkpointer = SqliteSaver.from_conn_string("billing.db")
graph = PlannerGraph(
model=model,
registry=registry,
api_names=["github", "stripe"],
checkpointer=checkpointer,
execution_mode="mixed",
)
result = await graph.run(
"""
1. List all overdue GitHub Sponsors invoices for org 'api2mcp'.
2. For each overdue invoice, find the matching Stripe customer
by email address.
3. Create a Stripe payment intent for the overdue amount (in cents).
4. Return a summary: number of payment intents created and total amount.
"""
)
print("\nWorkflow result:")
print(result["output"])
asyncio.run(billing_reconciliation())
Observability¶
Stream intermediate events to monitor progress:
async for event in graph.astream(prompt):
event_type = list(event.keys())[0]
if event_type == "planner":
plan = event["planner"].get("plan", [])
print(f"Plan generated: {len(plan)} steps")
for i, step in enumerate(plan, 1):
print(f" {i}. {step['tool']} — {step['description']}")
elif event_type == "executor":
step = event["executor"]
print(f"Executed: {step['tool']} → {step['status']}")
elif event_type == "final":
print(f"\nFinal: {event['final']['output']}")
Testing the Multi-API Workflow¶
import asyncio
from unittest.mock import AsyncMock
from api2mcp.testing import MCPTestClient, CoverageReporter
async def test_multi_api():
# Test each server independently
async with MCPTestClient(server_dir="./github-server") as gh_client:
github_tools = await gh_client.list_tools()
print(f"GitHub tools: {len(github_tools)}")
async with MCPTestClient(server_dir="./stripe-server") as stripe_client:
stripe_tools = await stripe_client.list_tools()
print(f"Stripe tools: {len(stripe_tools)}")
# Test a critical path
result = await stripe_client.call_tool(
"create_payment_intent",
{"amount": 5000, "currency": "usd"},
)
assert result.status == "success"
reporter = CoverageReporter.from_client(stripe_client)
report = reporter.report()
print(report.summary())
asyncio.run(test_multi_api())
What Makes This Powerful¶
- Zero boilerplate — both API specs converted automatically
- Intelligent planning — the LLM generates an optimal execution plan
- Parallel execution — independent reads run concurrently
- Error isolation — one step failing doesn't abort the workflow
- Resumable — checkpointing means you can resume interrupted workflows
- Observable — full streaming for real-time visibility