| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- #!/usr/bin/env bash
- # MCP Server Integration Test Script
- # Requires: the app running with MCP server toggled ON
- # Setup: adb forward tcp:8734 tcp:8734
- set -e
- BASE="${1:-http://127.0.0.1:8734}"
- PASS=0
- FAIL=0
- green() { echo -e "\033[32m✓ $*\033[0m"; }
- red() { echo -e "\033[31m✗ $*\033[0m"; ((FAIL++)); }
- header() { echo -e "\n\033[1;36m━━━ $* ━━━\033[0m"; }
- assert_ok() {
- local code=$(echo "$1" | jq -r 'if .result then "ok" else "err:" + .error.message end')
- if [ "$code" = "ok" ]; then
- green "$2"
- else
- red "$2 — $code"
- fi
- }
- assert_eq() {
- local actual=$(echo "$1" | jq -r "$2")
- if [ "$actual" = "$3" ]; then
- green "$4"
- else
- red "$4 — expected '$3', got '$actual'"
- fi
- }
- rpc() {
- # $1 = method, $2 = params JSON (optional), $3 = id (optional, default: 1)
- local method="$1"
- local params="${2:-{}}"
- local id="${3:-1}"
- curl -s -X POST "$BASE/mcp" \
- -H "Content-Type: application/json" \
- -d "{\"jsonrpc\":\"2.0\",\"id\":$id,\"method\":\"$method\",\"params\":$params}"
- }
- # ── 1. Health Check ─────────────────────────────────────────────
- header "1. Health Check"
- HEALTH=$(curl -s "$BASE/health")
- assert_eq "$HEALTH" ".status" "ok" "Health endpoint returns ok"
- # ── 2. Initialize Handshake ─────────────────────────────────────
- header "2. Initialize Handshake"
- INIT=$(rpc "initialize" '{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}')
- assert_ok "$INIT" "Initialize succeeds"
- assert_eq "$INIT" ".result.protocolVersion" "2024-11-05" "Protocol version"
- assert_eq "$INIT" ".result.serverInfo.name" "ijs-mcp" "Server name"
- # Send initialized notification
- INITD=$(rpc "notifications/initialized" '{}' "null")
- echo " Initialized notification → HTTP $(curl -s -o /dev/null -w '%{http_code}' -X POST "$BASE/mcp" -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","method":"notifications/initialized"}')"
- # ── 3. List Tools ───────────────────────────────────────────────
- header "3. List Tools"
- TOOLS=$(rpc "tools/list")
- assert_ok "$TOOLS" "tools/list succeeds"
- TOOL_COUNT=$(echo "$TOOLS" | jq '.result.tools | length')
- echo " Tools available: $TOOL_COUNT"
- for name in evaluate differentiate integrate solve simplify numeric to_latex list_plugins get_plugin create_plugin delete_plugin execute_plugin; do
- if echo "$TOOLS" | jq -e ".result.tools[] | select(.name == \"$name\")" > /dev/null 2>&1; then
- green " Tool: $name"
- else
- red " Tool: $name — MISSING"
- fi
- done
- # ── 4. Call Tools — Core Math ───────────────────────────────────
- header "4. Call Tools — Core Math"
- # 4a. evaluate: basic arithmetic
- R=$(rpc "tools/call" '{"name":"evaluate","arguments":{"expression":"2 + 2"}}')
- assert_ok "$R" "evaluate: 2+2"
- assert_eq "$R" '.result.content[0].text' "4" "evaluate: result = 4"
- # 4b. evaluate: variable assignment (shared session)
- R=$(rpc "tools/call" '{"name":"evaluate","arguments":{"expression":"myTestVar = 42"}}' 3)
- assert_ok "$R" "evaluate: myTestVar = 42"
- # 4c. evaluate: use assigned variable
- R=$(rpc "tools/call" '{"name":"evaluate","arguments":{"expression":"myTestVar * 2"}}' 4)
- assert_eq "$R" '.result.content[0].text' "84" "evaluate: myTestVar * 2 = 84 (shared session)"
- # 4d. differentiate (use clean variable 'y' to avoid collision with myTestVar)
- R=$(rpc "tools/call" '{"name":"differentiate","arguments":{"expression":"y^3","variable":"y"}}' 5)
- assert_eq "$R" '.result.content[0].text' "3*y^2" "differentiate: D[y^3, y] = 3*y^2"
- # 4e. integrate (indefinite) — use variable 't' to avoid collisions
- R=$(rpc "tools/call" '{"name":"integrate","arguments":{"expression":"t^2","variable":"t"}}' 6)
- assert_ok "$R" "integrate: indefinite ∫t²dt"
- echo " Result: $(echo "$R" | jq -r '.result.content[0].text')"
- # 4f. integrate (definite)
- R=$(rpc "tools/call" '{"name":"integrate","arguments":{"expression":"t^2","variable":"t","lower":"0","upper":"1"}}' 7)
- assert_eq "$R" '.result.content[0].text' "1/3" "integrate: definite ∫₀¹t²dt = 1/3"
- # 4g. solve
- R=$(rpc "tools/call" '{"name":"solve","arguments":{"equation":"u^2 - 5*u + 6 == 0","variable":"u"}}' 8)
- assert_ok "$R" "solve: u²-5u+6=0"
- echo " Result: $(echo "$R" | jq -r '.result.content[0].text')"
- # 4h. simplify
- R=$(rpc "tools/call" '{"name":"simplify","arguments":{"expression":"Sin[a]^2 + Cos[a]^2"}}' 9)
- assert_eq "$R" '.result.content[0].text' "1" "simplify: sin²a+cos²a = 1"
- # 4i. numeric
- R=$(rpc "tools/call" '{"name":"numeric","arguments":{"expression":"Pi","precision":"50"}}' 10)
- assert_ok "$R" "numeric: N[Pi, 50]"
- echo " Result: $(echo "$R" | jq -r '.result.content[0].text' | head -c 60)..."
- # 4j. to_latex
- R=$(rpc "tools/call" '{"name":"to_latex","arguments":{"expression":"z^2 + 2*z + 1"}}' 11)
- assert_ok "$R" "to_latex: TeXForm[z²+2z+1]"
- echo " LaTeX: $(echo "$R" | jq -r '.result.content[0].text')"
- # 4k. evaluate: Symja error handling
- R=$(rpc "tools/call" '{"name":"evaluate","arguments":{"expression":"1/0"}}' 12)
- assert_ok "$R" "evaluate: 1/0 (graceful error)"
- echo " Error msg: $(echo "$R" | jq -r '.result.content[0].text' | head -c 80)"
- # ── 5. List Resources ───────────────────────────────────────────
- header "5. List Resources"
- RESOURCES=$(rpc "resources/list")
- assert_ok "$RESOURCES" "resources/list succeeds"
- echo " Resources:"
- echo "$RESOURCES" | jq -r '.result.resources[] | " \(.uri) — \(.name)"'
- # ── 6. Read Resources ───────────────────────────────────────────
- header "6. Read Resources"
- R=$(rpc "resources/read" '{"uri":"variables://current"}' 13)
- assert_ok "$R" "resources/read: variables://current"
- echo " Variables: $(echo "$R" | jq -r '.result.contents[0].text')"
- R=$(rpc "resources/read" '{"uri":"history://recent"}' 14)
- assert_ok "$R" "resources/read: history://recent"
- # ── 7. Plugin CRUD ──────────────────────────────────────────────
- header "7. Plugin CRUD"
- R=$(rpc "tools/call" '{"name":"list_plugins","arguments":{}}' 15)
- assert_ok "$R" "list_plugins"
- R=$(rpc "tools/call" '{"name":"create_plugin","arguments":{"id":"test_hello","name":"Test Hello","icon":"👋","command":"hello","description":"A test plugin","js_code":"function calculate(inputs, Symja) {\n Symja.print(\"Hello from plugin!\");\n return \"Pi*\" + inputs.radius + \"^2\";\n}","inputs":[{"name":"radius","label":"Radius","defaultValue":"1"}]}}' 16)
- assert_ok "$R" "create_plugin: test_hello"
- echo " $R" | jq -r '.result.content[0].text'
- R=$(rpc "tools/call" '{"name":"get_plugin","arguments":{"plugin_id":"test_hello"}}' 17)
- assert_ok "$R" "get_plugin: test_hello"
- R=$(rpc "tools/call" '{"name":"execute_plugin","arguments":{"plugin_id":"test_hello","arguments":{"radius":"5"}}}' 18)
- assert_ok "$R" "execute_plugin: hello[radius=5]"
- echo " Plugin output:"
- echo "$R" | jq -r '.result.content[0].text'
- R=$(rpc "tools/call" '{"name":"delete_plugin","arguments":{"plugin_id":"test_hello"}}' 19)
- assert_ok "$R" "delete_plugin: test_hello"
- # ── 8. JSON-RPC Error Handling ──────────────────────────────────
- header "8. JSON-RPC Error Handling"
- R=$(curl -s -X POST "$BASE/mcp" -H "Content-Type: application/json" -d '{"jsonrpc":"1.0","id":1,"method":"evaluate"}')
- assert_eq "$R" '.error.message' "Invalid Request: jsonrpc must be 2.0" "Rejects non-2.0 jsonrpc"
- R=$(curl -s -X POST "$BASE/mcp" -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1}')
- assert_eq "$R" '.error.message' "Invalid Request: missing method" "Rejects missing method"
- R=$(rpc "nonexistent_method" '{}' 20)
- assert_eq "$R" '.error.code' "-32603" "Unknown method returns proper JSON-RPC error"
- assert_eq "$R" '.id' "20" "Error preserves request id"
- R=$(curl -s -X POST "$BASE/mcp" -H "Content-Type: application/json" -d 'not json')
- assert_eq "$R" '.error.message' "Parse error: invalid JSON" "Rejects invalid JSON"
- # ── Summary ─────────────────────────────────────────────────────
- header "Results"
- echo " Passed: $PASS"
- echo " Failed: $FAIL"
- if [ $FAIL -eq 0 ]; then
- green "All tests passed!"
- else
- red "$FAIL test(s) failed"
- exit 1
- fi
|