fix(vscode): add alpaca mcp auth diagnostics
This commit is contained in:
parent
d8941c5ad0
commit
1be7a93d52
@ -5,4 +5,5 @@ ALPACA_API_KEY=your_alpaca_api_key
|
|||||||
ALPACA_SECRET_KEY=your_alpaca_secret_key
|
ALPACA_SECRET_KEY=your_alpaca_secret_key
|
||||||
|
|
||||||
# Optional: set to true for paper trading if supported by the installed server version.
|
# Optional: set to true for paper trading if supported by the installed server version.
|
||||||
|
# Use false only with live-trading API keys.
|
||||||
ALPACA_PAPER_TRADE=true
|
ALPACA_PAPER_TRADE=true
|
||||||
|
|||||||
123
scripts/mcp/alpaca-mcp-server.sh
Normal file → Executable file
123
scripts/mcp/alpaca-mcp-server.sh
Normal file → Executable file
@ -4,11 +4,106 @@ set -eu
|
|||||||
SCRIPT_DIR="$(CDPATH= cd "$(dirname "$0")" && pwd -P)"
|
SCRIPT_DIR="$(CDPATH= cd "$(dirname "$0")" && pwd -P)"
|
||||||
ENV_FILE="${ALPACA_MCP_ENV_FILE:-${SCRIPT_DIR}/.env}"
|
ENV_FILE="${ALPACA_MCP_ENV_FILE:-${SCRIPT_DIR}/.env}"
|
||||||
|
|
||||||
if [ -f "${ENV_FILE}" ]; then
|
env_has_credentials() {
|
||||||
set -a
|
python3 - "${ENV_FILE}" <<'PY'
|
||||||
. "${ENV_FILE}"
|
import os
|
||||||
set +a
|
import sys
|
||||||
fi
|
|
||||||
|
env_file = sys.argv[1]
|
||||||
|
values = dict(os.environ)
|
||||||
|
|
||||||
|
if os.path.exists(env_file):
|
||||||
|
with open(env_file, "r", encoding="utf-8-sig") as handle:
|
||||||
|
for raw_line in handle:
|
||||||
|
line = raw_line.strip()
|
||||||
|
if not line or line.startswith("#"):
|
||||||
|
continue
|
||||||
|
if line.startswith("export "):
|
||||||
|
line = line[7:].strip()
|
||||||
|
if "=" not in line:
|
||||||
|
continue
|
||||||
|
key, value = line.split("=", 1)
|
||||||
|
values[key.strip()] = value.strip().strip("'\"")
|
||||||
|
|
||||||
|
api_key = values.get("ALPACA_API_KEY", "")
|
||||||
|
secret_key = values.get("ALPACA_SECRET_KEY", "")
|
||||||
|
placeholders = {"your_alpaca_api_key", "your_alpaca_secret_key"}
|
||||||
|
has_real_values = api_key and secret_key and api_key not in placeholders and secret_key not in placeholders
|
||||||
|
sys.exit(0 if has_real_values else 1)
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
|
||||||
|
run_account_check() {
|
||||||
|
python3 - "${ENV_FILE}" <<'PY'
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import urllib.error
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
env_file = sys.argv[1]
|
||||||
|
|
||||||
|
def load_env(path):
|
||||||
|
values = {}
|
||||||
|
if not os.path.exists(path):
|
||||||
|
return values
|
||||||
|
with open(path, "r", encoding="utf-8-sig") as handle:
|
||||||
|
for raw_line in handle:
|
||||||
|
line = raw_line.strip()
|
||||||
|
if not line or line.startswith("#"):
|
||||||
|
continue
|
||||||
|
if line.startswith("export "):
|
||||||
|
line = line[7:].strip()
|
||||||
|
if "=" not in line:
|
||||||
|
continue
|
||||||
|
key, value = line.split("=", 1)
|
||||||
|
key = key.strip()
|
||||||
|
value = value.strip().strip("'\"")
|
||||||
|
values[key] = value
|
||||||
|
return values
|
||||||
|
|
||||||
|
values = {**os.environ, **load_env(env_file)}
|
||||||
|
api_key = values.get("ALPACA_API_KEY", "")
|
||||||
|
secret_key = values.get("ALPACA_SECRET_KEY", "")
|
||||||
|
paper = values.get("ALPACA_PAPER_TRADE", "true").strip().lower() != "false"
|
||||||
|
placeholders = {"your_alpaca_api_key", "your_alpaca_secret_key"}
|
||||||
|
|
||||||
|
if not api_key or not secret_key or api_key in placeholders or secret_key in placeholders:
|
||||||
|
print("error: ALPACA_API_KEY and ALPACA_SECRET_KEY must both be set")
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
base_url = "https://paper-api.alpaca.markets" if paper else "https://api.alpaca.markets"
|
||||||
|
request = urllib.request.Request(
|
||||||
|
f"{base_url}/v2/account",
|
||||||
|
headers={
|
||||||
|
"APCA-API-KEY-ID": api_key,
|
||||||
|
"APCA-API-SECRET-KEY": secret_key,
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(request, timeout=15) as response:
|
||||||
|
data = json.loads(response.read().decode("utf-8"))
|
||||||
|
except urllib.error.HTTPError as error:
|
||||||
|
body = error.read().decode("utf-8", "replace").strip()
|
||||||
|
print(f"error: Alpaca account check failed with HTTP {error.code} against {base_url}")
|
||||||
|
if error.code in (401, 403):
|
||||||
|
print("hint: verify the key and secret are from the same Alpaca environment and match ALPACA_PAPER_TRADE")
|
||||||
|
if body:
|
||||||
|
print(f"details: {body[:300]}")
|
||||||
|
sys.exit(1)
|
||||||
|
except Exception as error:
|
||||||
|
print(f"error: Alpaca account check could not reach {base_url}: {error}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
status = data.get("status", "unknown")
|
||||||
|
currency = data.get("currency", "unknown")
|
||||||
|
trading_blocked = data.get("trading_blocked", "unknown")
|
||||||
|
print(f"ok: Alpaca account authenticated against {base_url}")
|
||||||
|
print(f"ok: account status={status} currency={currency} trading_blocked={trading_blocked}")
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
|
||||||
if [ "${1:-}" = "--doctor" ]; then
|
if [ "${1:-}" = "--doctor" ]; then
|
||||||
if [ -f "${ENV_FILE}" ]; then
|
if [ -f "${ENV_FILE}" ]; then
|
||||||
@ -17,12 +112,14 @@ if [ "${1:-}" = "--doctor" ]; then
|
|||||||
echo "warn: env file not found (${ENV_FILE}); copy scripts/mcp/.env.example to scripts/mcp/.env"
|
echo "warn: env file not found (${ENV_FILE}); copy scripts/mcp/.env.example to scripts/mcp/.env"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "${ALPACA_API_KEY:-}" ] && [ -n "${ALPACA_SECRET_KEY:-}" ]; then
|
if env_has_credentials; then
|
||||||
echo "ok: Alpaca credential variables are set"
|
echo "ok: Alpaca credential variables are set"
|
||||||
else
|
else
|
||||||
echo "warn: ALPACA_API_KEY and/or ALPACA_SECRET_KEY are not set"
|
echo "warn: ALPACA_API_KEY and/or ALPACA_SECRET_KEY are not set"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "hint: run sh scripts/mcp/alpaca-mcp-server.sh --check-account to validate the key pair with Alpaca"
|
||||||
|
|
||||||
if command -v uvx >/dev/null 2>&1; then
|
if command -v uvx >/dev/null 2>&1; then
|
||||||
echo "ok: uvx available ($(command -v uvx))"
|
echo "ok: uvx available ($(command -v uvx))"
|
||||||
exit 0
|
exit 0
|
||||||
@ -39,15 +136,29 @@ if [ "${1:-}" = "--doctor" ]; then
|
|||||||
exit 127
|
exit 127
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "${1:-}" = "--check-account" ]; then
|
||||||
|
run_account_check
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
if command -v uvx >/dev/null 2>&1; then
|
if command -v uvx >/dev/null 2>&1; then
|
||||||
|
if [ -f "${ENV_FILE}" ]; then
|
||||||
|
exec uvx alpaca-mcp-server --env-file "${ENV_FILE}" "$@"
|
||||||
|
fi
|
||||||
exec uvx alpaca-mcp-server "$@"
|
exec uvx alpaca-mcp-server "$@"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v uv >/dev/null 2>&1; then
|
if command -v uv >/dev/null 2>&1; then
|
||||||
|
if [ -f "${ENV_FILE}" ]; then
|
||||||
|
exec uv tool run alpaca-mcp-server --env-file "${ENV_FILE}" "$@"
|
||||||
|
fi
|
||||||
exec uv tool run alpaca-mcp-server "$@"
|
exec uv tool run alpaca-mcp-server "$@"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v pipx >/dev/null 2>&1; then
|
if command -v pipx >/dev/null 2>&1; then
|
||||||
|
if [ -f "${ENV_FILE}" ]; then
|
||||||
|
exec pipx run alpaca-mcp-server --env-file "${ENV_FILE}" "$@"
|
||||||
|
fi
|
||||||
exec pipx run alpaca-mcp-server "$@"
|
exec pipx run alpaca-mcp-server "$@"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user