Snippets
Examples
Copy-ready clients for the Streamable HTTP transport.
TypeScript client
client.ts
import { Client } from "@modelcontextprotocol/sdk/client/index.js"
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"
const transport = new StreamableHTTPClientTransport(
new URL(`${process.env.MCP_BASE_URL}/mcp/`),
{ requestInit: { headers: { Authorization: `Bearer ${process.env.MCP_JWT}` } } },
)
const client = new Client({ name: "wm-studio", version: "1.0.0" }, { capabilities: {} })
await client.connect(transport)
const tools = await client.listTools()
console.log(tools.tools.map((t) => t.name))
const result = await client.callTool({
name: "studio_generate_image",
arguments: {
prompt: "minimalist product photo of a black ceramic mug",
model: "fal-ai/flux-2-pro",
aspect_ratio: "4:3",
},
})
console.log(result)Python client
client.py
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
import os
async def main():
url = f"{os.environ['MCP_BASE_URL']}/mcp/"
headers = {"Authorization": f"Bearer {os.environ['MCP_JWT']}"}
async with streamablehttp_client(url, headers=headers) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
print([t.name for t in tools.tools])
result = await session.call_tool("studio_generate_image", {
"prompt": "minimalist product photo of a black ceramic mug",
"model": "fal-ai/flux-2-pro",
})
print(result)
import asyncio
asyncio.run(main())Raw curl
Plain JSON-RPC over Streamable HTTP. Useful for smoke tests in CI.
bash
curl -X POST "$MCP_BASE_URL/mcp/" \
-H "Authorization: Bearer $MCP_JWT" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "studio_upscale_image",
"arguments": {
"image_url": "https://cdn.wm.studio/uploads/portrait.jpg",
"scale": 4
}
}
}'Async polling
Long-running tools (studio_generate_video, studio_digital_twin,studio_convert_to_3d) return a job_id immediately. Poll untilstatus is completed or failed.
ts
async function generateVideoAndWait(prompt: string, imageUrl: string) {
const start = await client.callTool({
name: "studio_generate_video",
arguments: {
prompt,
image_url: imageUrl,
model: "fal-ai/kling-video/v2.5-turbo/pro/image-to-video",
},
}) as { job_id: string }
for (let i = 0; i < 120; i++) {
const status = await client.callTool({
name: "studio_job_status",
arguments: { job_id: start.job_id },
}) as { status: string; video?: { url: string } }
if (status.status === "completed") return status.video?.url
if (status.status === "failed") throw new Error("video render failed")
await new Promise((r) => setTimeout(r, 5000))
}
throw new Error("timed out")
}