#!/usr/bin/env bash

set -uo pipefail

VERSION="1.0.0"
PROTOCOL_VERSION="2025-06-18"

usage() {
  printf '%s\n' \
    "MCP Endpoint Check ${VERSION}" \
    "" \
    "Usage:" \
    "  bash mcp-endpoint-check.sh https://example.com/mcp" \
    "" \
    "This tool sends only unauthenticated discovery and initialize requests." \
    "Do not place credentials, tokens, or user information in the URL."
}

if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
  usage
  exit 0
fi

if [[ "$#" -ne 1 ]]; then
  usage >&2
  exit 64
fi

TARGET_URL="$1"
case "$TARGET_URL" in
  https://*|http://localhost*|http://127.0.0.1*|http://\[::1\]*)
    ;;
  http://*)
    printf 'ERROR: Remote MCP endpoints should use HTTPS. Refusing cleartext URL.\n' >&2
    exit 64
    ;;
  *)
    printf 'ERROR: Provide a complete HTTPS URL, including the MCP path.\n' >&2
    exit 64
    ;;
esac

if [[ "$TARGET_URL" =~ ^https?://[^/]*@ ]]; then
  printf 'ERROR: User information in URLs can leak credentials. Remove it and retry.\n' >&2
  exit 64
fi

if ! command -v curl >/dev/null 2>&1; then
  printf 'ERROR: curl is required.\n' >&2
  exit 69
fi

WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/mcp-endpoint-check.XXXXXX")"
trap 'rm -rf "$WORK_DIR"' EXIT

INIT_HEADERS="$WORK_DIR/initialize.headers"
INIT_BODY="$WORK_DIR/initialize.body"
INIT_META="$WORK_DIR/initialize.meta"
PRM_HEADERS="$WORK_DIR/prm.headers"
PRM_BODY="$WORK_DIR/prm.body"
PRM_META="$WORK_DIR/prm.meta"

redact_headers() {
  sed -E \
    -e 's/^([Aa]uthorization:).*/\1 [REDACTED]/' \
    -e 's/^([Pp]roxy-[Aa]uthorization:).*/\1 [REDACTED]/' \
    -e 's/^([Cc]ookie:).*/\1 [REDACTED]/' \
    -e 's/^([Ss]et-[Cc]ookie:).*/\1 [REDACTED]/' \
    -e 's/^([Mm]cp-[Ss]ession-[Ii]d:).*/\1 [REDACTED]/' \
    "$1"
}

header_value() {
  local header_name="$1"
  local header_file="$2"
  awk -v wanted="$header_name" '
    BEGIN { IGNORECASE = 1 }
    {
      line = $0
      sub(/\r$/, "", line)
      split(line, parts, ":")
      if (tolower(parts[1]) == tolower(wanted)) {
        sub(/^[^:]+:[[:space:]]*/, "", line)
        value = line
      }
    }
    END { print value }
  ' "$header_file"
}

derive_prm_url() {
  if command -v python3 >/dev/null 2>&1; then
    python3 - "$TARGET_URL" <<'PY'
import sys
from urllib.parse import urlsplit, urlunsplit

target = urlsplit(sys.argv[1])
path = target.path or ""
metadata_path = "/.well-known/oauth-protected-resource"
if path and path != "/":
    metadata_path += "/" + path.lstrip("/")
print(urlunsplit((target.scheme, target.netloc, metadata_path, target.query, "")))
PY
    return
  fi

  local origin
  origin="${TARGET_URL%%://*}://${TARGET_URL#*://}"
  origin="${origin%%/*}"
  printf '%s/.well-known/oauth-protected-resource\n' "$origin"
}

printf '# MCP endpoint evidence report\n\n'
printf -- '- Checked at: %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
printf -- '- Target: %s\n' "$TARGET_URL"
printf -- '- Checker: %s\n' "$VERSION"
printf -- '- Protocol offered: %s\n\n' "$PROTOCOL_VERSION"

INITIALIZE_PAYLOAD='{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"'"$PROTOCOL_VERSION"'","capabilities":{},"clientInfo":{"name":"agent-rescue-endpoint-check","version":"'"$VERSION"'"}}}'

curl \
  --silent \
  --show-error \
  --connect-timeout 8 \
  --max-time 15 \
  --max-redirs 0 \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json, text/event-stream' \
  --header "MCP-Protocol-Version: ${PROTOCOL_VERSION}" \
  --data "$INITIALIZE_PAYLOAD" \
  --dump-header "$INIT_HEADERS" \
  --output "$INIT_BODY" \
  --write-out $'http_code=%{http_code}\nremote_ip=%{remote_ip}\ncontent_type=%{content_type}\nredirect_url=%{redirect_url}\nssl_verify_result=%{ssl_verify_result}\ntime_connect=%{time_connect}\ntime_starttransfer=%{time_starttransfer}\ntime_total=%{time_total}\nurl_effective=%{url_effective}\n' \
  "$TARGET_URL" >"$INIT_META"
CURL_STATUS=$?

printf '## Unauthenticated initialize probe\n\n'
if [[ "$CURL_STATUS" -ne 0 ]]; then
  printf 'curl failed with exit code %s.\n\n' "$CURL_STATUS"
else
  printf '```text\n'
  sed -n '1,12p' "$INIT_META"
  redact_headers "$INIT_HEADERS" |
    grep -Ei '^(HTTP/|location:|content-type:|www-authenticate:|mcp-protocol-version:|mcp-session-id:|server:)' |
    sed -n '1,20p'
  printf '```\n\n'
fi

HTTP_CODE="$(sed -n 's/^http_code=//p' "$INIT_META" 2>/dev/null || true)"
CONTENT_TYPE="$(sed -n 's/^content_type=//p' "$INIT_META" 2>/dev/null || true)"
LOCATION="$(header_value "location" "$INIT_HEADERS")"
WWW_AUTHENTICATE="$(header_value "www-authenticate" "$INIT_HEADERS")"

case "$HTTP_CODE" in
  2??)
    printf '**Interpretation:** the endpoint accepted the initialize request.\n\n'
    case "$CONTENT_TYPE" in
      application/json*|text/event-stream*)
        ;;
      *)
        printf '**Warning:** a successful Streamable HTTP response normally uses JSON or an event stream; observed `%s`.\n\n' "${CONTENT_TYPE:-unknown}"
        ;;
    esac
    ;;
  401)
    printf '**Interpretation:** the endpoint is reachable and requires authentication.\n\n'
    if [[ "$WWW_AUTHENTICATE" != *resource_metadata=* ]]; then
      printf '**Warning:** `WWW-Authenticate` did not advertise `resource_metadata`; OAuth discovery may fail in MCP clients.\n\n'
    fi
    ;;
  403)
    printf '**Interpretation:** a gateway or resource policy refused the unauthenticated request. Verify host allowlists, identity, audience, and permission separately.\n\n'
    ;;
  404)
    printf '**Interpretation:** the host responded, but this MCP path was not found. Verify the deployed route and base path.\n\n'
    ;;
  405)
    printf '**Interpretation:** the route does not accept POST. Verify that this is the Streamable HTTP endpoint rather than a health or legacy SSE URL.\n\n'
    ;;
  3??)
    printf '**Interpretation:** the MCP URL redirected to `%s`. Verify that the client uses the canonical endpoint and that authorization headers are not lost across hosts.\n\n' "${LOCATION:-an undisclosed location}"
    ;;
  000|"")
    printf '**Interpretation:** no HTTP response was received. Start with DNS, TLS, proxy, firewall, and deployed-runtime egress evidence.\n\n'
    ;;
  *)
    printf '**Interpretation:** the endpoint returned HTTP %s. Inspect gateway and server logs for the same timestamp.\n\n' "$HTTP_CODE"
    ;;
esac

PRM_URL="$(derive_prm_url)"
curl \
  --silent \
  --show-error \
  --connect-timeout 8 \
  --max-time 12 \
  --max-redirs 0 \
  --request GET \
  --header 'Accept: application/json' \
  --dump-header "$PRM_HEADERS" \
  --output "$PRM_BODY" \
  --write-out $'http_code=%{http_code}\ncontent_type=%{content_type}\ntime_total=%{time_total}\nurl_effective=%{url_effective}\n' \
  "$PRM_URL" >"$PRM_META"
PRM_CURL_STATUS=$?

printf '## OAuth protected-resource metadata\n\n'
printf -- '- Derived URL: %s\n' "$PRM_URL"
if [[ "$PRM_CURL_STATUS" -ne 0 ]]; then
  printf -- '- Result: curl exit %s\n\n' "$PRM_CURL_STATUS"
else
  PRM_HTTP_CODE="$(sed -n 's/^http_code=//p' "$PRM_META" 2>/dev/null || true)"
  PRM_CONTENT_TYPE="$(sed -n 's/^content_type=//p' "$PRM_META" 2>/dev/null || true)"
  printf -- '- HTTP status: %s\n' "$PRM_HTTP_CODE"
  printf -- '- Content type: %s\n\n' "${PRM_CONTENT_TYPE:-unknown}"
  if [[ "$PRM_HTTP_CODE" == "200" && "$PRM_CONTENT_TYPE" != application/json* ]]; then
    printf '**Warning:** successful protected-resource metadata must be a JSON object served as `application/json`.\n\n'
  fi
fi

printf '## Next proof\n\n'
printf '%s\n' \
  '1. Compare this timestamp with reverse-proxy and MCP server logs.' \
  '2. If initialize reached the server, inspect capability negotiation and any returned `Mcp-Session-Id`.' \
  '3. If authentication is required, validate protected-resource metadata, issuer, audience, scopes, expiry, and deployed client identity without sharing tokens.' \
  '4. Use the official MCP Inspector for authenticated discovery and one minimal direct tool call.' \
  '5. Prove the complete client workflow only after the direct transport and tool contract pass.'
