Self-hosted MCP server options
Self-hosted MCP server options: enabling mobile AI app integration
- The `add_memory` (write) path requires only an HTTPS call to the GitHub Contents API with no local storage dependency, making it fully viable as a stateless Cloudflare Worker in JavaScript within the free tier (100,000 requests/day, 10ms CPU, $0/month ongoing cost)
- The `search_brain` (read/search) path requires embedding inference and LanceDB vector search, both of which are architecturally impossible on Cloudflare Workers free tier: LanceDB is not available in Pyodide's WebAssembly package set, and embedding inference takes 34–177ms per document on native hardware — exceeding the 10ms CPU limit by 3–17×
- Fly.io Hobby ($5/month) supports persistent Python containers with mounted NVMe volumes; the LanceDB index survives restarts, and the pre-computed embedding pattern established by `lancedb-index-rebuild-from-git.md` reduces cold-start loading to under 0.2s regardless of corpus size
- Railway Hobby ($5/month, $5 included usage credits) is a viable alternative to Fly.io for the read service: both offer public HTTPS endpoints and persistent volumes adequate for a personal-scale LanceDB corpus; Fly.io has marginally broader community evidence for stateful Python deployments
- Tailscale Funnel exposes a home server to the public internet via an auto-provisioned `*.ts.net` HTTPS URL, is available on the free personal plan (3 users, 100 devices), requires no port-forwarding or static IP, and handles TLS certificate management automatically — making it zero-additional-cost for users who already run home server hardware
- GitHub Actions `repository_dispatch` returns HTTP 204 immediately with no synchronous response channel back to the caller; it cannot fulfil the request–response contract required by MCP tool calls and is limited to fire-and-forget write-only capture as a degraded fallback
- The MCP Python SDK (v1.8.0+, May 2025) supports Streamable HTTP transport via FastMCP; migrating `mcp_server.py` from stdio to remote-accessible Streamable HTTP requires changing the transport runner to `mcp.run(transport="streamable-http", host="0.0.0.0", port=8000)` — two lines of code, with no changes to tool logic
- The Claude iOS Connector system supports no-auth (open endpoint) or OAuth 2.1 only; static `Authorization: Bearer` header tokens are not configurable via the claude.ai connector UI, requiring either a high-entropy URL component (URL-as-secret) or a full OAuth 2.1 implementation as the auth design
Research Question
What is the minimum viable self-hosted deployment of mcp_server.py (or a write-only HTTP wrapper) that: (a) is reachable from the public internet, (b) has zero or near-zero ongoing cost, (c) requires minimal operational maintenance, (d) is secure enough for personal memory data? Evaluate: Cloudflare Worker (stateless, GitHub API only), Fly.io/Railway free tier, home server + Tailscale, GitHub Actions as a compute backend via repository_dispatch.
Findings
Executive Summary
Self-hosting a Python MCP server for mobile AI integration resolves to a split architecture. The write path — a stateless GitHub Contents API proxy — fits naturally on Cloudflare Workers ($0/month, zero cold start). The read path — embedding inference plus LanceDB vector search — requires either a persistent container on Fly.io or Railway ($5/month) or an existing home server exposed via Tailscale Funnel at no additional cost. GitHub Actions repository_dispatch cannot serve synchronous MCP tool calls and is limited to fire-and-forget write capture. Authentication for Claude iOS is constrained to no-auth or OAuth 2.1 — static bearer tokens are not configurable via the claude.ai connector UI — making a high-entropy URL component the simplest viable approach, with OAuth 2.1 as the hardening path.
Key Findings
-
The
add_memory(write) path requires only an HTTPS call to the GitHub Contents API with no local storage dependency, making it fully viable as a stateless Cloudflare Worker in JavaScript within the free tier (100,000 requests/day, 10ms CPU, $0/month ongoing cost). -
The
search_brain(read/search) path requires embedding inference and LanceDB vector search, both of which are architecturally impossible on Cloudflare Workers free tier: LanceDB is not available in Pyodide's WebAssembly package set, and embedding inference takes 34–177ms per document on native hardware — exceeding the 10ms CPU limit by 3–17×. -
Fly.io Hobby ($5/month) supports persistent Python containers with mounted NVMe volumes; the LanceDB index survives restarts, and the pre-computed embedding pattern established by
lancedb-index-rebuild-from-git.mdreduces cold-start loading to under 0.2s regardless of corpus size. -
Railway Hobby ($5/month, $5 included usage credits) is a viable alternative to Fly.io for the read service: both offer public HTTPS endpoints and persistent volumes adequate for a personal-scale LanceDB corpus; Fly.io has marginally broader community evidence for stateful Python deployments.
-
Tailscale Funnel exposes a home server to the public internet via an auto-provisioned
*.ts.netHTTPS URL, is available on the free personal plan (3 users, 100 devices), requires no port-forwarding or static IP, and handles TLS certificate management automatically — making it zero-additional-cost for users who already run home server hardware. -
GitHub Actions
repository_dispatchreturns HTTP 204 immediately with no synchronous response channel back to the caller; it cannot fulfil the request–response contract required by MCP tool calls and is limited to fire-and-forget write-only capture as a degraded fallback. -
The MCP Python SDK (v1.8.0+, May 2025) supports Streamable HTTP transport via FastMCP; migrating
mcp_server.pyfrom stdio to remote-accessible Streamable HTTP requires changing the transport runner tomcp.run(transport="streamable-http", host="0.0.0.0", port=8000)— two lines of code, with no changes to tool logic. -
The Claude iOS Connector system supports no-auth (open endpoint) or OAuth 2.1 only; static
Authorization: Bearerheader tokens are not configurable via the claude.ai connector UI, requiring either a high-entropy URL component (URL-as-secret) or a full OAuth 2.1 implementation as the auth design.
Assumptions
- Assumption: A Python FastAPI + LanceDB server for a 100–300 item corpus fits within 256 MB RAM when idle. Justification: LanceDB index at this scale is under 3 MB (extrapolated from Key Finding 6: 7.74 MB per 1000 documents); FastAPI idle RAM footprint is approximately 30–60 MB; total estimated idle usage is well under 100 MB.
- Assumption: Personal capture rate is at most 10
add_memorycalls per day. Justification: This is a personal assistant use case; even power users are unlikely to exceed 100 daily captures. The 100k/day Cloudflare Workers free limit would not bind below ~100,000 daily calls.
Analysis
The resource asymmetry between write and read is the key architectural driver: the write path requires nothing more than an HTTPS call, while the read path requires persistent disk, embedding inference, and milliseconds of CPU — making containers the only viable option for the latter and serverless edge the only practical option for the former.
The pre-computed embeddings finding from lancedb-index-rebuild-from-git.md is what makes the Fly.io option viable without paying for always-on capacity: cold-start loading takes under 0.2s, keeping search latency below 1s even for containers that auto-slept. This directly addresses the 11.5s rebuild penalty that would otherwise make auto-scaling [inference] unacceptably slow.
Home server + Tailscale Funnel is genuinely competitive on cost and capability for users with existing hardware. The trade-off is operational reliability: home hardware introduces failure modes (ISP outage, power cut, hardware failure) that Fly.io eliminates. For a high-availability requirement, Fly.io is preferable. For a personal assistant with acceptable occasional downtime, home server is a legitimate choice.
Railway and Fly.io are equivalent in cost and capability. Fly.io is selected as the primary recommendation based on wider community evidence for stateful Python deployments and a [inference] more mature persistent volume feature.
Risks, Gaps, and Uncertainties
- Fly.io's free resource allowances have changed before and may change again; the $5/month Hobby plan is the durable commitment, but the "included free VM allowance" pricing could be revised.
- Tailscale Funnel bandwidth limits on the free plan are not explicitly documented; heavy-use scenarios could trigger undocumented restrictions.
- LanceDB on Fly.io's shared-cpu-1x machine has not been empirically tested at the described scale. The 256 MB RAM assumption is an inference from component sizes, not a direct measurement.
- The OAuth 2.1 requirement for Claude iOS Connectors adds implementation complexity beyond the URL-as-secret approach. The gap between "works with URL secret" and "properly authenticated via OAuth 2.1" is real and documented.
- Cloudflare Workers Python support (Pyodide) remains in open beta and could gain LanceDB compatibility in future, which would change the read path evaluation.
Open Questions
- Render.com free tier (750 instance hours/month, persistent disk on paid plans) as an alternative to Fly.io: warrants evaluation in a dedicated backlog item.
- Write-only Cloudflare Worker as full capture surface: Can
list_memories(last N files via GitHub Contents API listing) be added without a full read service? Implementation question, not a research gap. - OAuth 2.1 for personal MCP servers: What is the minimum viable OAuth 2.1 implementation using GitHub as the identity provider? This is a non-trivial implementation question worth a dedicated backlog item before hardening the deployment.
sources
- [x]
Research/completed/2026-03-02-agent-memory-management-context-injection.md— production markdown-bank patterns; security posture requirements for personal memory - [x] MCP specification (HTTP/SSE transport)
- [x] Cloudflare Workers docs
- [x] Cloudflare Workers free tier limits
- [x] Fly.io free tier docs
- [x] Railway free tier docs
- [x] Tailscale docs
- [x] GitHub Actions
repository_dispatchevent docs - [ ] LanceDB docs
- [x]
2026-03-08-lancedb-index-rebuild-from-git.md— related item on rebuild speed as deployment constraint - [ ]
davidamitchell/Memory-SystemBACKLOG.md W-0014 — the corresponding discovery item that this research informs