Code Mode 在 /open/v1/mcp/code 端点暴露 run_code 工具,Agent 写 JavaScript 在沙箱中编排多步 / 条件查询。
| 项目 | 值 |
|---|---|
| URL | https://open.longxiachuxing.com/api/open/v1/mcp/code |
| 方法 | POST |
| 协议 | JSON-RPC 2.0(仅暴露 run_code 一个工具) |
| 鉴权 | HMAC-SHA256 签名或 Bearer Token(与标准 REST 一致) |
| 沙箱 | goja(Go 实现的 JavaScript 解释器),无网络 / 文件 / require |
| 场景 | MCP 标准模式 | Code Mode ✅ |
|---|---|---|
| 单步查询 | ✅ | ❌ 过度设计 |
| 多步依赖(验价 → 下单) | ⚠️ 多次往返 | ✅ 一次沙箱搞定 |
| 条件判断(价格超 X 就查其他日期) | ❌ Agent 上下文浪费 | ✅ 逻辑留在沙箱 |
| 循环筛选(遍历 20 个航班找最优) | ❌ 大 JSON 占上下文 | ✅ 中间数据不出沙箱 |
| 组合查询(往返航班 + 酒店) | ⚠️ 多次工具调用 | ✅ 一段脚本编排 |
原则:简单查询用 MCP 标准模式,复杂编排用 Code Mode。
// 业务能力(与 MCP 标准模式工具一一对应)
rideclaw.flight.search(args)
rideclaw.flight.airport_search(args)
rideclaw.flight.pricing(args)
rideclaw.flight.order_create(args)
rideclaw.flight.order_pay(args)
rideclaw.flight.order_detail(args)
rideclaw.flight.order_list(args)
rideclaw.flight.order_cancel_fee(args)
rideclaw.flight.order_cancel(args)
rideclaw.hotel.search(args)
rideclaw.hotel.rooms(args)
rideclaw.hotel.order_create(args)
rideclaw.taxi.estimate(args)
rideclaw.train.search(args)
rideclaw.bus.search(args)
// 辅助工具
console.log(...) // 日志输出(随 result.log 返回)
parallel([fn, fn, ...]) // 并发辅助(同步沙箱下顺序执行)
| 限制项 | 值 | 超限行为 |
|---|---|---|
| 单次最大能力调用数 | 20 | 抛异常 "超过单次最大能力调用数 20" |
| 执行超时 | 30 秒 | VM 中断 "执行超时(30s)" |
| 网络 / 文件 / require | ❌ 不可用 | 编译/运行时错误 |
await脚本自动包裹为 async IIFE,直接写 await:
// ✅ 正确
const result = await rideclaw.flight.search({ ... });
return result.flights[0];
// ❌ 错误(不需要手动 async function)
async function main() {
const result = await rideclaw.flight.search({ ... });
return result;
}
main();
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "run_code",
"arguments": {
"code": "const flights = await rideclaw.flight.search({trip_mode:'domestic',trip_type:'oneway',from_code:'SZX',to_code:'PEK',depart_date:'2026-06-12'});\nreturn flights.flights.slice(0,3);"
}
},
"id": 1
}
关键字段:
params.name:固定为 "run_code"。params.arguments.code:要执行的 JavaScript 代码(字符串)。{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\"value\":[...],\"tool_calls\":1,\"log\":[]}"
}
],
"isError": false
}
}
result 字段:
value:脚本 return 的值(JSON 序列化)。tool_calls:实际调用的能力次数。log:console.log() 输出(数组)。error:执行错误(编译失败 / 运行时异常)。场景:如果明天票价超 2000 元,就查后天的。
const tomorrow = await rideclaw.flight.search({
trip_mode: "domestic",
trip_type: "oneway",
from_code: "SZX",
to_code: "PEK",
depart_date: "2026-06-12",
passengers: { adult: 1 },
page_size: 5
});
const minPrice = Math.min(...tomorrow.flights.map(f =>
Math.min(...f.cabins.map(c => c.lowest_price))
));
if (minPrice > 2000) {
console.log("明天价格超 2000,查后天");
const dayAfter = await rideclaw.flight.search({
trip_mode: "domestic",
trip_type: "oneway",
from_code: "SZX",
to_code: "PEK",
depart_date: "2026-06-13",
passengers: { adult: 1 },
page_size: 5
});
return {
recommendation: "后天",
min_price: Math.min(...dayAfter.flights.flatMap(f => f.cabins.map(c => c.lowest_price))),
flights: dayAfter.flights.slice(0, 3)
};
}
return {
recommendation: "明天",
min_price: minPrice,
flights: tomorrow.flights.slice(0, 3)
};
能力调用数:最坏情况 2 次(明天 + 后天),最好 1 次(明天价格合适)。
响应示例:
{
"value": {
"recommendation": "后天",
"min_price": 1850,
"flights": [...]
},
"tool_calls": 2,
"log": ["明天价格超 2000,查后天"]
}
场景:搜索后自动验价并下单。
const search = await rideclaw.flight.search({
trip_mode: "domestic",
trip_type: "oneway",
from_code: "SZX",
to_code: "PEK",
depart_date: "2026-06-12",
passengers: { adult: 1 }
});
const cheapest = search.flights[0].cabins[0];
console.log(`最低价舱位: ${cheapest.cabin_name} ${cheapest.lowest_price}元`);
const pricing = await rideclaw.flight.pricing({
search_offer_id: cheapest.search_offer_id,
passengers: [{
type: "adult",
name: "张三",
id_type: "ID_CARD",
id_number: "330106199001011234",
phone: "13800138000"
}]
});
const order = await rideclaw.flight.order_create({
offer_id: pricing.offer_id,
out_trade_no: `TEST_${Date.now()}`,
passengers: pricing.passenger_fares.map((_, i) => ({
type: "adult",
name: "张三",
id_type: "ID_CARD",
id_number: "330106199001011234",
phone: "13800138000"
})),
contact: {
name: "张三",
phone: "13800138000"
}
});
return {
system_no: order.system_no,
total_amount: order.total_amount,
status: order.status
};
能力调用数:3 次(搜索 + 验价 + 下单)。
场景:找出前 20 个航班中起飞时间在 8:00-10:00 的直飞航班。
const search = await rideclaw.flight.search({
trip_mode: "domestic",
trip_type: "oneway",
from_code: "SZX",
to_code: "PEK",
depart_date: "2026-06-12",
passengers: { adult: 1 },
page_size: 20
});
const filtered = search.flights.filter(f => {
const depHour = parseInt(f.dep_time.split(" ")[1].split(":")[0]);
return f.stop_count === 0 && depHour >= 8 && depHour < 10;
});
console.log(`筛选出 ${filtered.length} 个航班`);
return filtered.map(f => ({
flight_no: f.flight_no,
dep_time: f.dep_time,
arr_time: f.arr_time,
min_price: Math.min(...f.cabins.map(c => c.lowest_price))
}));
能力调用数:1 次(筛选逻辑在沙箱内完成,不占 Agent 上下文)。
const outbound = await rideclaw.flight.search({
trip_mode: "domestic",
trip_type: "oneway",
from_code: "SZX",
to_code: "HGH",
depart_date: "2026-06-12",
passengers: { adult: 1 },
page_size: 3
});
const inbound = await rideclaw.flight.search({
trip_mode: "domestic",
trip_type: "oneway",
from_code: "HGH",
to_code: "SZX",
depart_date: "2026-06-14",
passengers: { adult: 1 },
page_size: 3
});
const hotels = await rideclaw.hotel.search({
destination: "杭州西湖",
check_in: "2026-06-12",
check_out: "2026-06-14",
page_size: 3
});
return {
outbound: outbound.flights[0].flight_no,
inbound: inbound.flights[0].flight_no,
hotel: hotels.hotels[0].hotel_name,
total_estimate:
Math.min(...outbound.flights[0].cabins.map(c => c.lowest_price)) +
Math.min(...inbound.flights[0].cabins.map(c => c.lowest_price)) +
hotels.hotels[0].min_price * 2
};
能力调用数:3 次(去程 + 返程 + 酒店)。
console.log("开始搜索航班...");
const result = await rideclaw.flight.search({ ... });
console.log(`找到 ${result.total} 个航班`);
return result.flights.slice(0, 3);
响应:
{
"value": [...],
"tool_calls": 1,
"log": [
"开始搜索航班...",
"找到 42 个航班"
]
}
语法错误:
const result = await rideclaw.flight.search({; // 缺少右花括号
响应:
{
"error": "SyntaxError: Unexpected token ';' at line 1",
"tool_calls": 0
}
运行时错误:
const result = await rideclaw.flight.search({
from_code: "INVALID"
});
响应:
{
"error": "flight.search 调用失败: from_code 无效",
"tool_calls": 1
}
import requests
import json
MCP_URL = "https://open.longxiachuxing.com/api/open/v1/mcp/code"
BEARER_TOKEN = "rdak_live_xxxxxxxx"
code = """
const tomorrow = await rideclaw.flight.search({
trip_mode: "domestic",
trip_type: "oneway",
from_code: "SZX",
to_code: "PEK",
depart_date: "2026-06-12",
passengers: { adult: 1 },
page_size: 5
});
const minPrice = Math.min(...tomorrow.flights.map(f =>
Math.min(...f.cabins.map(c => c.lowest_price))
));
if (minPrice > 2000) {
const dayAfter = await rideclaw.flight.search({
trip_mode: "domestic",
trip_type: "oneway",
from_code: "SZX",
to_code: "PEK",
depart_date: "2026-06-13",
passengers: { adult: 1 },
page_size: 5
});
return { recommendation: "后天", flights: dayAfter.flights.slice(0, 3) };
}
return { recommendation: "明天", flights: tomorrow.flights.slice(0, 3) };
"""
resp = requests.post(MCP_URL, json={
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "run_code",
"arguments": {"code": code}
},
"id": 1
}, headers={
"Authorization": f"Bearer {BEARER_TOKEN}",
"Content-Type": "application/json"
})
result = resp.json()
print(json.dumps(result, ensure_ascii=False, indent=2))
// ✅ 好:找到一个符合条件的就返回
for (const flight of flights) {
if (flight.cabins[0].lowest_price < 1000) {
return flight;
}
}
// ❌ 差:遍历完所有航班再返回
const cheap = flights.filter(f => f.cabins[0].lowest_price < 1000);
return cheap[0];
// ❌ 错误:沙箱会在 30 秒超时
while (true) {
await rideclaw.flight.search({ ... });
}
// ✅ 正确:设置最大次数
let attempts = 0;
while (attempts < 5) {
const result = await rideclaw.flight.search({ ... });
if (result.flights.length > 0) break;
attempts++;
}
console.log("搜索参数:", JSON.stringify(params));
const result = await rideclaw.flight.search(params);
console.log("返回航班数:", result.total);
Agent 可以根据 log 判断脚本执行到哪一步、中间数据是什么。
try {
const result = await rideclaw.flight.search({ ... });
return result.flights[0];
} catch (e) {
console.log("搜索失败:", e);
return { error: "未找到航班" };
}
沙箱内能力调用失败会抛异常,用 try/catch 捕获后可自定义返回值。
const page1 = await rideclaw.flight.search({ ..., page: 1, page_size: 20 });
const page2 = await rideclaw.flight.search({ ..., page: 2, page_size: 20 });
const allFlights = [...page1.flights, ...page2.flights];
return allFlights.slice(0, 10);
注意能力调用数限制(20 次),不要无限分页。
A: 略慢(+50~200ms 沙箱启动开销),但多步编排场景下总耗时更短:
A: 部分支持。goja 基于 ES5.1,但支持 const/let/箭头函数/模板字符串/解构赋值。不支持 async/await(已由沙箱自动包裹)、class(用普通函数代替)、import/export(无模块系统)。
A: 拆分成多次 run_code 调用,或切回 MCP 标准模式单步调用。Code Mode 适合中等复杂度(3~15 次调用)的编排场景。
A: 不能。沙箱无网络、无文件,唯一 I/O 是注入的 rideclaw.* 能力。如需调用外部 API,在 Agent 侧完成后把结果传入脚本。
A: 不能。每次 run_code 都是全新 VM,状态不保留。需要跨次调用的数据由 Agent 在上下文中维护。
| 维度 | MCP 标准模式 | Code Mode |
|---|---|---|
| 工具数量 | 15 个(按业务线拆分) | 1 个(run_code) |
| 调用次数 | 每个问题可能多次工具调用 | 一次沙箱执行多次内部调用 |
| Agent 上下文 | 每次工具调用占用上下文 | 中间数据留在沙箱 |
| 适用场景 | 简单查询、单步操作 | 多步编排、条件判断、循环筛选 |
| 延迟 | 单步快(50~200ms) | 首次慢(+沙箱启动),多步总体快 |
| 调试 | 工具调用日志 | console.log + result.log |
| 错误恢复 | Agent 自我修正参数 | 脚本 try/catch 或 Agent 修正代码 |
选择建议:从 MCP 标准模式起步,遇到 3 步以上编排或条件判断时切换到 Code Mode。