feat: add Tailscale Funnel config for Claude AI connector testing

Add docker compose services (tailscale-mcp + nginx-claude-filter) behind
a claude-funnel profile that expose the login-flow MCP server via
Tailscale Funnel with IP-based access control:

- /mcp endpoint restricted to Claude AI outbound IPs (160.79.104.0/21)
- /oauth/*, /.well-known/*, /app paths open to all IPs (user login flow)
- All other paths return 404

Also add favicon.png served at /favicon.ico for connector directory
discovery (Google favicon service).

Usage:
  docker compose --profile login-flow --profile claude-funnel up -d

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-03-29 21:00:00 +02:00
co-authored by Claude Opus 4.6
parent 3cf4c777ed
commit b8dc1d7f52
7 changed files with 177 additions and 2 deletions
+13 -1
View File
@@ -24,7 +24,7 @@ from pydantic import AnyHttpUrl
from starlette.applications import Starlette
from starlette.middleware.authentication import AuthenticationMiddleware
from starlette.middleware.cors import CORSMiddleware
from starlette.responses import JSONResponse, RedirectResponse
from starlette.responses import FileResponse, JSONResponse, RedirectResponse
from starlette.routing import Mount, Route
from starlette.staticfiles import StaticFiles
from starlette.types import ASGIApp, Receive, Send
@@ -2342,6 +2342,18 @@ def get_app(transport: str = "streamable-http", enabled_apps: list[str] | None =
routes.append(Mount("/app", app=browser_app))
logger.info("App routes with session auth: /app, /app/webhooks, /app/revoke")
# Favicon for connector directory discovery (Google favicon service)
favicon_path = os.path.join(
os.path.dirname(__file__), "auth", "static", "favicon.png"
)
if os.path.isfile(favicon_path):
routes.append(
Route(
"/favicon.ico",
lambda request: FileResponse(favicon_path, media_type="image/png"),
)
)
# Mount FastMCP at root last (catch-all, handles OAuth via token_verifier)
routes.append(Mount("/", app=mcp_app))