A modular MCP server that exposes wrapped local CLIs and remote APIs to MCP clients like Claude.ai and Claude Code over HTTPS.
Each tool surface lives in its own module under src/tools/. The current modules wrap @steipete/bird for X/Twitter, the Perplexity API for web-grounded search and reasoning, and Cloudflare Browser Rendering for headless-browser page capture.
Some CLIs only work where they're installed: cookies, auth files, local paths, system access. Running them on a single host and exposing them over MCP means any client can call them without re-authenticating per machine.
| Tool | Description |
|---|---|
bird_whoami |
Verify which X account is authenticated |
bird_read |
Read a tweet by ID/URL (JSON) |
bird_thread |
Full thread containing a tweet (JSON) |
bird_replies |
Replies to a tweet (JSON) |
bird_search |
Search tweets, supports operators (JSON) |
bird_mentions |
Mentions of a user (defaults to authenticated account) |
bird_bookmarks |
Read your bookmarks (JSON) |
bird_post_tweet |
Write. Post a tweet |
bird_reply |
Write. Reply to a tweet |
@steipete/bird is deprecated upstream but still working. Its cookies rotate every few weeks; when bird_whoami returns auth errors, re-extract auth_token and ct0 from a logged-in browser and update the env.
Calls the Perplexity REST API directly. Requires PERPLEXITY_API_KEY in the server env. All four tools are read-only and billed to that key.
| Tool | Description |
|---|---|
perplexity_search |
Ranked search results (title, URL, snippet, date) |
perplexity_ask |
Quick web-grounded answer with citations (sonar-pro) |
perplexity_research |
Deep multi-source research (sonar-deep-research, 30s+ per call) |
perplexity_reason |
Step-by-step reasoning with web grounding (sonar-reasoning-pro) |
Calls the Browser Rendering REST API directly — a headless browser in Cloudflare's cloud, so any client (including ones with no local browser) can render a page, screenshot it, or extract structure. Requires CLOUDFLARE_ACCOUNT_ID and a CLOUDFLARE_API_TOKEN with the Browser Rendering — Edit permission. All tools are read-only and bill to that account's browser-time quota (free plan: 10 min/day, 3 concurrent browsers).
| Tool | Description |
|---|---|
browser_markdown |
Render a page (post-JavaScript) and return clean Markdown |
browser_content |
Render a page and return its full HTML |
browser_screenshot |
Capture a screenshot, returned as an image |
browser_scrape |
Extract elements matching CSS selectors |
browser_links |
Extract all links from a page |
browser_snapshot |
Multiple formats in one call (HTML, screenshot, Markdown, accessibility tree) — one browser-time charge |
Because billing is on browser time, the cheapest patterns are browser_snapshot (several formats per render) and passing rejectResourceTypes (e.g. ["image","font"]) to cut render time. The page targeted by these tools must be reachable from the public internet — Cloudflare's browser cannot reach localhost.
The Claude-facing OAuth surface is served by the MCP SDK (via mcp-server-kit).
- OAuth 2.1 + PKCE, with discovery at
/.well-known/oauth-authorization-server, the approval page at/authorize, and token exchange at/token(compatible with Claude.ai's connector flow) - The approval page is guarded: the server refuses to start unless one of
APPROVAL_PASSWORD(a password on the approval page),MCP_CLIENT_SECRET(a secret required at token exchange), orAPPROVAL_OPEN=true(declares an external gateway already guards/authorize) is set - Optional dynamic client registration for clients that can't be pre-configured (e.g. ChatGPT), enabled by
MCP_DCR_ENABLED; it requiresAPPROVAL_PASSWORD - Static bearer token (
MCP_STATIC_BEARER_TOKEN) for clients that bypass the browser flow - Bearer-gated liveness at
/health— returns 404 unlessHEALTH_TOKENis set, then requires that token; the secret pasted into an uptime monitor grants nothing else and rotates independently of the/mcpauth - CORS allowlisting via
CORS_ALLOWED_ORIGINS - Bird cookies, the Perplexity API key, and the Cloudflare API token live only in the server's environment, never in this repo
git clone git@github.com:nweii/tools-mcp.git
cd tools-mcp
bun install
cp .env.example .env
# fill in BIRD_AUTH_TOKEN, BIRD_CT0, PERPLEXITY_API_KEY, MCP_CLIENT_ID, and a
# guard for /authorize (APPROVAL_PASSWORD, or APPROVAL_OPEN=true for local use)
bun run startThe server listens on PORT (default 3457). MCP endpoint is POST /mcp.
Smoke-test bird without going through MCP:
BIRD_AUTH_TOKEN=… BIRD_CT0=… ./node_modules/.bin/bird whoamiSame shape as obsidian-remote-mcp: Docker behind a reverse proxy that handles TLS, with the public origin set in MCP_BASE_URL.
- Copy
docker-compose.yml.exampletodocker-compose.yml, fill in env values - Point a subdomain (e.g.
tools-mcp.yourdomain.com) at the host via Cloudflare Tunnel / reverse proxy - Set
MCP_BASE_URL=https://tools-mcp.yourdomain.com(match the public URL exactly, no/mcppath) docker compose up -d
- Claude.ai → Settings → Connectors → Add custom MCP
- URL:
https://tools-mcp.yourdomain.com/mcp - Approve in the consent screen that opens on
/authorize
ChatGPT has two setup surfaces — the web app (custom connectors live behind developer mode; see OpenAI's developer mode guide) and the desktop app / Codex (their own connector flow).
The simplest path — and the one the desktop app needs — is to let the client register itself: set MCP_DCR_ENABLED=true on the server (keep APPROVAL_PASSWORD set, it's the gate), then add https://tools-mcp.yourdomain.com/mcp with no client ID or callback to configure. On the web app you can instead pick User-Defined OAuth Client and enter your MCP_CLIENT_ID. Either way, authenticate in the browser and complete the approval-password screen. A non-OAuth client can use the static bearer token (below).
Use the static bearer token instead of the browser flow:
curl -H "Authorization: Bearer $MCP_STATIC_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
https://tools-mcp.yourdomain.com/mcp- Create
src/tools/<name>.tsexportingregister<Name>Tools(server) - Wire it up in
src/tools.ts - Add any required env vars to
.env.example
For CLI wrappers, see src/tools/bird.ts and use runCli/parseJsonOutput from src/exec.ts. For REST API wrappers, see src/tools/perplexity.ts and use native fetch with an AbortController for timeouts.
See .env.example for the full list of environment variables.