81 lines
4.7 KiB
Bash
Executable File
81 lines
4.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
"$ROOT/scripts/build_cpp.sh" >/dev/null
|
|
mkdir -p "$ROOT/reports"
|
|
AMAP_PORT=19781
|
|
CAL_PORT=19782
|
|
MCPD_PORT=19780
|
|
CONFIG="$ROOT/reports/mcpd.generated.config.json"
|
|
cat > "$CONFIG" <<JSON
|
|
{
|
|
"server": {"listen_host": "127.0.0.1", "listen_port": $MCPD_PORT, "rpc_path": "/rpc"},
|
|
"services": [
|
|
{"service_id": "amap", "url": "http://127.0.0.1:$AMAP_PORT/rpc", "tool_prefix": "amap.", "enabled": true},
|
|
{"service_id": "calendar", "url": "http://127.0.0.1:$CAL_PORT/rpc", "tool_prefix": "calendar.", "enabled": true}
|
|
],
|
|
"agents": [
|
|
{"token": "demo-token", "agent_id": "trip_planner", "allow_services": ["amap", "calendar"], "allow_tools": ["amap.maps_weather", "amap.maps_text_search", "amap.maps_direction_transit_integrated_by_address", "amap.hotel_search", "amap.restaurant_search", "calendar.create_event", "calendar.list_events", "calendar.query_free_time"], "admin": true},
|
|
{"token": "weather-token", "agent_id": "weather_agent", "allow_services": ["amap"], "allow_tools": ["amap.maps_weather"], "admin": false},
|
|
{"token": "calendar-token", "agent_id": "calendar_agent", "allow_services": ["calendar"], "allow_tools": ["calendar.create_event", "calendar.list_events", "calendar.query_free_time"], "admin": false}
|
|
],
|
|
"cache": {"max_result_cache_entries": 2, "default_ttl_ms": 60000, "cache_key_hash": "fnv64"},
|
|
"security": {"require_token": true, "max_payload_bytes": 262144, "enable_nonce_replay_guard": true}
|
|
}
|
|
JSON
|
|
"$ROOT/build/cpp/mock_mcp_server" --port "$AMAP_PORT" >"$ROOT/reports/config_amap.log" 2>&1 &
|
|
AMAP_PID=$!
|
|
"$ROOT/build/cpp/mock_calendar_server" --port "$CAL_PORT" >"$ROOT/reports/config_calendar.log" 2>&1 &
|
|
CAL_PID=$!
|
|
for _ in $(seq 1 50); do
|
|
if curl -fsS -H 'Content-Type: application/json' --data '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' "http://127.0.0.1:$AMAP_PORT/rpc" >/dev/null 2>&1 \
|
|
&& curl -fsS -H 'Content-Type: application/json' --data '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' "http://127.0.0.1:$CAL_PORT/rpc" >/dev/null 2>&1; then
|
|
break
|
|
fi
|
|
sleep 0.1
|
|
done
|
|
"$ROOT/build/cpp/mcpd" --config "$CONFIG" >"$ROOT/reports/config_mcpd.log" 2>&1 &
|
|
MCPD_PID=$!
|
|
cleanup() {
|
|
kill "$MCPD_PID" "$AMAP_PID" "$CAL_PID" >/dev/null 2>&1 || true
|
|
wait "$MCPD_PID" "$AMAP_PID" "$CAL_PID" 2>/dev/null || true
|
|
}
|
|
trap cleanup EXIT
|
|
for _ in $(seq 1 50); do
|
|
if curl -fsS "http://127.0.0.1:$MCPD_PORT/healthz" >/dev/null 2>&1; then break; fi
|
|
sleep 0.1
|
|
done
|
|
ROOT="$ROOT" MCPD_PORT="$MCPD_PORT" python3 - <<'PY'
|
|
import json, os, sys, urllib.error, urllib.request
|
|
root=os.environ['ROOT']; port=os.environ['MCPD_PORT']; endpoint=f'http://127.0.0.1:{port}/rpc'; stats_endpoint=f'http://127.0.0.1:{port}/stats'; seq=0; checks=[]
|
|
def rpc(method, params=None, token='demo-token'):
|
|
global seq; seq+=1
|
|
payload={'jsonrpc':'2.0','id':seq,'method':method,'params':params or {}}
|
|
req=urllib.request.Request(endpoint, data=json.dumps(payload, ensure_ascii=False).encode(), headers={'Content-Type':'application/json','X-Agent-Token':token}, method='POST')
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=5) as resp: return resp.status, json.loads(resp.read().decode())
|
|
except urllib.error.HTTPError as e:
|
|
try: body=json.loads(e.read().decode())
|
|
except Exception: body={}
|
|
return e.code, body
|
|
def check(name, cond, detail):
|
|
checks.append({'name':name,'pass':bool(cond),'detail':detail}); print(('[PASS]' if cond else '[FAIL]'), name, detail)
|
|
code, body = rpc('tools/list', {'compact': True})
|
|
names=[t.get('name','') for t in body.get('result',{}).get('tools',[])]
|
|
check('config loads two services', code==200 and any(n.startswith('amap.') for n in names) and any(n.startswith('calendar.') for n in names), names)
|
|
code2, body2 = rpc('tools/call', {'name':'calendar.list_events','arguments':{'date':'2026-05-14','limit':1}})
|
|
check('config routes calendar', code2==200 and body2.get('service_id')=='calendar', body2)
|
|
code3, body3 = rpc('tools/call', {'name':'calendar.list_events','arguments':{'date':'2026-05-14','limit':1}})
|
|
check('config cache policy works', code3==200 and body3.get('cached') is True, body3)
|
|
try:
|
|
with urllib.request.urlopen(stats_endpoint, timeout=5) as resp: st=json.loads(resp.read().decode())['stats']
|
|
except Exception as e:
|
|
st={'error':str(e)}
|
|
check('config max cache entries applied', st.get('result_cache_max_entries') == 2, st)
|
|
summary={'test':'config loading','pass':sum(1 for c in checks if c['pass']),'fail':sum(1 for c in checks if not c['pass']),'checks':checks,'stats':st}
|
|
out=os.path.join(root,'reports','config_loading_summary.json')
|
|
open(out,'w',encoding='utf-8').write(json.dumps(summary, ensure_ascii=False, indent=2))
|
|
print('summary:', out)
|
|
if summary['fail']: sys.exit(1)
|
|
PY
|