I built mcpfs to treat API data as what it is — files:
cat /mnt/mcpfs/stripe/balance.json | jq
cat /mnt/mcpfs/posthog/dashboards.json | jq '.[].name'
cat /mnt/mcpfs/linear/issues.json | jq '.[].title'
Every service works the same way. No flags to remember — not "gh --json --jq", not "stripe --format json", not "kubectl -o json". Just cat | jq. Discovery is ls. You already know the entire interface.You can compose native CLIs too — but you need to remember each one's JSON flags, and some services (PostHog, Linear, Notion) have no CLI at all. With mcpfs every service is the same syntax, so cross-service pipes just work:
# Paying customers with no analytics activity
comm -23 \
<(cat /mnt/mcpfs/stripe/customers.json | jq -r '.[].email' | sort) \
<(cat /mnt/mcpfs/posthog/events.json | jq -r '.[].distinct_id' | sort)
For writes, where good CLIs exist (gh, stripe, kubectl), use them. For services with no CLI — PostHog, Linear, Notion — mcpfs proxies their upstream MCP tools into a CLI: mcpfs-posthog create-feature-flag --key my-flag --active
mcpfs-linear create_issue --teamId abc --title "Fix login bug"
Under the hood: each server is a Go binary (200–400 lines) that calls the service API and exposes data as MCP resources. mcpfs mounts them via FUSE. The MCP ecosystem has 18,000+ servers that all expose tools — mcpfs flips that: reads are resources (files), writes stay as tools (CLI). git clone <https://github.com/airshelf/mcpfs> && cd mcpfs && go build ./...
<https://github.com/airshelf/mcpfs>