RUNBOOK
VSlim MCP Runbook
这页是 VSlim\Mcp\App 的手工验证 runbook。
目标是确认:
VSlim\App->mcp()组合入口正常initializeMcp-Session-IdGET /mcpSSE session- queued notification
- queued
sampling/createMessage /admin/runtime/mcp
Start
启动示例:
cd https://github.com/bullsoft/vphpext/tree/main/vhttpd
./vhttpd --config /vphp/vslim-docs/examples/mcp.toml
默认端口:
- data plane:
http://127.0.0.1:19896 - admin plane:
http://127.0.0.1:19996
Step 1: Check HTTP Side
curl --noproxy '*' http://127.0.0.1:19896/meta | jq .
预期包含:
name = "vslim-native-mcp-demo"mcp = "/mcp"native = true
Step 2: Initialize
curl --noproxy '*' -s \
-X POST \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'MCP-Protocol-Version: 2025-11-05' \
-H 'Origin: http://127.0.0.1:19896' \
-D /tmp/vslim_mcp_headers.txt \
--data-binary '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-05","capabilities":{"sampling":{},"roots":{"listChanged":true}}}}' \
http://127.0.0.1:19896/mcp | jq .
预期:
serverInfo.name = "vslim-native-mcp-demo"capabilities至少包含:loggingsamplingtoolsresourcesprompts
Step 3: Extract Session Id
SESSION_ID=$(awk '/^mcp-session-id:/ {print $2}' /tmp/vslim_mcp_headers.txt | tr -d '\r')
echo "$SESSION_ID"
Step 4: Inspect Runtime Session
curl --noproxy '*' \
"http://127.0.0.1:19996/admin/runtime/mcp?details=1&session_id=${SESSION_ID}" | jq .
只看 client capability 快照:
curl --noproxy '*' \
"http://127.0.0.1:19996/admin/runtime/mcp?details=1&session_id=${SESSION_ID}" \
| jq -r '.sessions[0].client_capabilities_json'
Step 5: Open SSE Session
另开一个终端:
curl --noproxy '*' -N \
-H 'Origin: http://127.0.0.1:19896' \
-H "Mcp-Session-Id: ${SESSION_ID}" \
http://127.0.0.1:19896/mcp
Step 6: Verify Queued Notification
curl --noproxy '*' -s \
-X POST \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'MCP-Protocol-Version: 2025-11-05' \
-H 'Origin: http://127.0.0.1:19896' \
-H "Mcp-Session-Id: ${SESSION_ID}" \
--data-binary '{"jsonrpc":"2.0","id":2,"method":"debug/notify","params":{"text":"hello native mcp"}}' \
http://127.0.0.1:19896/mcp | jq .
预期:
POST /mcp立即返回{ "queued": true }- SSE 收到:
event: message
data: {"jsonrpc":"2.0","method":"notifications/message",...}
Step 7: Verify Queued Sampling
curl --noproxy '*' -s \
-X POST \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'MCP-Protocol-Version: 2025-11-05' \
-H 'Origin: http://127.0.0.1:19896' \
-H "Mcp-Session-Id: ${SESSION_ID}" \
--data-binary '{"jsonrpc":"2.0","id":3,"method":"debug/sample","params":{"topic":"native runtime contract"}}' \
http://127.0.0.1:19896/mcp | jq .
预期 SSE 收到:
event: message
data: {"jsonrpc":"2.0","id":"sample-3","method":"sampling/createMessage",...}
Step 8: Verify Native Capability Gating
当前示例里的 debug/sample 已经使用:
VSlim\Mcp\App::client_supports($frame, 'sampling')VSlim\Mcp\App::capability_error(...)
所以缺少 sampling capability 时,会由原生 handler 自己返回 409。
先创建一个不声明 sampling 的 session:
curl --noproxy '*' -s \
-X POST \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'MCP-Protocol-Version: 2025-11-05' \
-H 'Origin: http://127.0.0.1:19896' \
-D /tmp/vslim_mcp_headers_no_sampling.txt \
--data-binary '{"jsonrpc":"2.0","id":11,"method":"initialize","params":{"protocolVersion":"2025-11-05","capabilities":{"roots":{"listChanged":true}}}}' \
http://127.0.0.1:19896/mcp | jq .
SESSION_ID_NO_SAMPLING=$(awk '/^mcp-session-id:/ {print $2}' /tmp/vslim_mcp_headers_no_sampling.txt | tr -d '\r')
echo "$SESSION_ID_NO_SAMPLING"
然后触发:
curl --noproxy '*' -i \
-X POST \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'MCP-Protocol-Version: 2025-11-05' \
-H 'Origin: http://127.0.0.1:19896' \
-H "Mcp-Session-Id: ${SESSION_ID_NO_SAMPLING}" \
--data-binary '{"jsonrpc":"2.0","id":12,"method":"debug/sample","params":{"topic":"native gate"}}' \
http://127.0.0.1:19896/mcp
预期:
- 状态码
409 - body:
{"error":"Sampling capability required by app"}