#!/usr/bin/env bash # One whole read over MCP, with curl and nothing else. # # The Streamable HTTP transport is plain POSTed JSON-RPC, so a shell is enough # to prove a connection works before you wire up a runtime. # # Needs: INSPIRED_KEY (your API key) and a photograph. # export INSPIRED_KEY=isk_your_key_here # ./mcp-session.sh living-room.jpg furniture set -euo pipefail MCP_URL="${MCP_URL:-https://inspired.jetskibay.com/mcp}" PHOTO="${1:?usage: mcp-session.sh [domain]}" DOMAIN="${2:-fashion}" : "${INSPIRED_KEY:?set INSPIRED_KEY to your Inspired API key}" call() { curl -sS "$MCP_URL" \ -H "Authorization: Bearer $INSPIRED_KEY" \ -H 'content-type: application/json' \ -H 'accept: application/json' \ --data-binary @- } # 1. Handshake. The server answers with the one protocol revision it implements. printf '\n== initialize ==\n' call <<'JSON' {"jsonrpc":"2.0","id":1,"method":"initialize","params":{ "protocolVersion":"2026-07-28", "capabilities":{}, "clientInfo":{"name":"mcp-session.sh","version":"1.0.0"}}} JSON # A notification has no id, so the server answers 202 with no body. call >/dev/null <<'JSON' {"jsonrpc":"2.0","method":"notifications/initialized"} JSON # 2. Discovery. Free, but still authenticated: there is no anonymous listing. printf '\n== tools/list ==\n' call <<'JSON' {"jsonrpc":"2.0","id":2,"method":"tools/list"} JSON printf '\n== list_focus_domains ==\n' call <<'JSON' {"jsonrpc":"2.0","id":3,"method":"tools/call", "params":{"name":"list_focus_domains","arguments":{}}} JSON # 3. Start the read. This is the call that spends one request. # The image travels as base64 inside the JSON-RPC arguments. printf '\n== start_visual_search ==\n' case "$PHOTO" in *.png) MEDIA_TYPE=image/png ;; *.webp) MEDIA_TYPE=image/webp ;; *.gif) MEDIA_TYPE=image/gif ;; *) MEDIA_TYPE=image/jpeg ;; esac IMAGE_B64="$(base64 < "$PHOTO" | tr -d '\n')" STARTED="$( jq -nc --arg image "$IMAGE_B64" --arg type "$MEDIA_TYPE" --arg domain "$DOMAIN" \ '{jsonrpc:"2.0",id:4,method:"tools/call",params:{ name:"start_visual_search", arguments:{image:$image,imageMediaType:$type,domain:$domain}}}' | call )" echo "$STARTED" | jq '.result.structuredContent' REQUEST_ID="$(echo "$STARTED" | jq -r '.result.structuredContent.requestId')" if [ "$REQUEST_ID" = "null" ]; then echo "the read did not start; the tool error above says why" >&2 exit 1 fi # 4. Collect it. Polling is free, so the only cost of a slow read is patience. printf '\n== get_visual_search ==\n' for _ in $(seq 1 60); do STATE="$( jq -nc --arg id "$REQUEST_ID" \ '{jsonrpc:"2.0",id:5,method:"tools/call",params:{ name:"get_visual_search",arguments:{requestId:$id}}}' | call )" STATUS="$(echo "$STATE" | jq -r '.result.structuredContent.status')" echo " $STATUS" if [ "$STATUS" = "complete" ]; then echo "$STATE" | jq -r '.result.content[0].text' exit 0 fi if [ "$(echo "$STATE" | jq -r '.result.isError')" = "true" ]; then echo "$STATE" | jq -r '.result.content[0].text' >&2 exit 1 fi sleep "$(echo "$STATE" | jq -r '(.result.structuredContent.pollAfterMs // 3000) / 1000')" done echo "still running after 60 polls; the requestId stays valid: $REQUEST_ID" >&2 exit 1