Optional streamable-HTTP transport for remote hosting#15
Conversation
- research/spotify-mcp-landscape.md: survey of other Spotify MCPs (GitHub + registries), remote-hosting/OAuth comparison, positioning takeaways - PLAN.md: plan to add optional streamable-HTTP transport and deploy remotely (homelab first, Modal stateless second); headless OAuth is the real work
- SPOTIFY_MCP_TRANSPORT=streamable-http serves over HTTP (host/port/stateless via env); stdio stays the zero-config default - headless OAuth: seed SPOTIFY_REFRESH_TOKEN (MemoryCacheHandler) or SPOTIFY_CACHE_PATH (file cache on a volume), open_browser disabled when headless; local stdio unchanged - optional SPOTIFY_MCP_BEARER gates the endpoint via a constant-time bearer check - deploy/modal_app.py (stateless, per Modal's 150s cap) + Dockerfile EXPOSE/env notes - add uvicorn dep (imported directly for the bearer path) - tests for transport dispatch, bearer guard, and headless cache selection - relax the server.json env-var guard to a subset check (optional deploy vars aren't declared in the lean stdio manifest)
Code Review: PR #15 — Optional streamable-HTTP transportOverall this is solid, well-structured work. The transport dispatch is clean, the bearer guard uses constant-time comparison correctly, and the headless OAuth design is sound. A few things worth addressing before merge: Security
Per RFC 7235, a 401 response should include "headers": [
(b"content-type", b"text/plain; charset=utf-8"),
(b"www-authenticate", b"Bearer realm=\"spotify-mcp\""),
],Using Potential Bug: stateless HTTP mode with no
|
adds an optional streamable-HTTP transport so the server can be hosted remotely (homelab, Modal, etc.) instead of only running locally over stdio. stdio stays the default — http is fully opt-in via env. also lands the competitive research that motivated this plus a PLAN.md deployment guide.
SPOTIFY_MCP_TRANSPORT=streamable-httpserves over HTTP (SPOTIFY_MCP_HOST/PORT/STATELESSknobs); stdio remains the zero-config defaultSPOTIFY_REFRESH_TOKEN(in-memory) orSPOTIFY_CACHE_PATH(file on a volume) so hosted deploys never need a browser — local stdio still auto-opens oneSPOTIFY_MCP_BEARERgates the endpoint with a constant-time bearer check; otherwise keep it behind a private networkdeploy/modal_app.pyfor a one-command Modal deploy (stateless, since Modal's 150s request cap breaks stateful SSE) + Dockerfile EXPOSE/env notesuvicornas a direct dep (used for the bearer path)to test:
SPOTIFY_MCP_TRANSPORT=streamable-http uv run spotify-mcpthenclaude mcp add --transport http spotify http://127.0.0.1:8000/mcp→ list tools, run a searchSPOTIFY_MCP_BEARER=…and confirm requests without the header get a 401