{"_id":"40mcp","name":"40mcp","dist-tags":{"beta":"0.1.1-beta.0","latest":"0.1.1-beta.0"},"versions":{"0.1.1-beta.0":{"name":"40mcp","version":"0.1.1-beta.0","description":"The universal API-to-MCP bridge. Any API. Any protocol. Token-aware.","type":"module","main":"src/index.js","types":"src/index.d.ts","exports":{".":{"types":"./src/index.d.ts","default":"./src/index.js"},"./loaders":{"types":"./src/loaders/index.d.ts","default":"./src/loaders/index.js"},"./compose":{"types":"./src/compose/index.d.ts","default":"./src/compose/index.js"},"./transport":{"types":"./src/transport/index.d.ts","default":"./src/transport/index.js"},"./reverse":{"types":"./src/reverse/server.d.ts","default":"./src/reverse/server.js"},"./transforms":{"types":"./src/transforms/response.d.ts","default":"./src/transforms/response.js"},"./webhook":{"types":"./src/webhook/listener.d.ts","default":"./src/webhook/listener.js"},"./tenant":{"types":"./src/tenant/scope.d.ts","default":"./src/tenant/scope.js"},"./security":{"types":"./src/security/vault.d.ts","default":"./src/security/vault.js"},"./policy":{"types":"./src/security/policy.d.ts","default":"./src/security/policy.js"},"./steering":{"types":"./src/steering/index.d.ts","default":"./src/steering/index.js"}},"bin":{"40mcp":"src/cli.js"},"scripts":{"pretest":"node scripts/run-tests.mjs --ensure-node-modules","test":"node scripts/run-tests.mjs --root src","test:invariants":"node scripts/run-tests.mjs --root src/security/invariants","check:loc":"node scripts/check-loc.js","test:integration":"node scripts/run-tests.mjs --root test","test:all":"node scripts/run-tests.mjs --roots src,test","trust-matrix":"node scripts/trust-matrix.mjs","trust-demos":"node examples/run-trust-demos.mjs","lint":"eslint src test examples","lint:fix":"eslint src test examples --fix","verify":"npm run lint && npm run test:all && npm pack --dry-run","smoke-test":"bash scripts/smoke-test.sh","ci":"bash scripts/pre-commit.sh","ci:fast":"bash scripts/pre-commit.sh --fast","prepublishOnly":"npm run verify","example":"node examples/petstore.js"},"keywords":["mcp","model-context-protocol","rest","api","graphql","openapi","bridge","shim","claude","cursor","ai","tools","agent","token-aware"],"license":"MIT","author":{"name":"40verse"},"repository":{"type":"git","url":"git+https://github.com/40verse/40mcp.git"},"homepage":"https://github.com/40verse/40mcp#readme","bugs":{"url":"https://github.com/40verse/40mcp/issues"},"engines":{"node":">=18.0.0"},"dependencies":{"@modelcontextprotocol/sdk":"^1.29.0"},"devDependencies":{"@emulators/github":"^0.4.1","@types/node":"^20.0.0","emulate":"^0.4.1","eslint":"^10.2.0"},"gitHead":"fb0050af49a3953b750337bd68bd19865da7a095","_id":"40mcp@0.1.1-beta.0","_nodeVersion":"24.14.0","_npmVersion":"11.9.0","dist":{"integrity":"sha512-xISJVdYo5wdEQhh5W0HZ2qDPZn33nD84p1qKCJwbCueYUc2NmsZwAKRHxqW8SBWPsg9Qk5Y6NtKNE+41VlSX+Q==","shasum":"f111becae51566355eb56ac2f93081ea0f4c379f","tarball":"https://registry.npmjs.org/40mcp/-/40mcp-0.1.1-beta.0.tgz","fileCount":134,"unpackedSize":1527997,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCxgxjWgeQW+BlXPrSbmP95E+Ug09w451oQyWWjhcCXHQIhAPDRhmNUbkPrHHGqZ7GB8z3ZixBYcJ8CW01sLu0rnCnF"}]},"_npmUser":{"name":"40verse","email":"wdgibsonjr@gmail.com"},"directories":{},"maintainers":[{"name":"40verse","email":"wdgibsonjr@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/40mcp_0.1.1-beta.0_1776738186701_0.6339466912756253"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-21T02:23:06.700Z","0.1.1-beta.0":"2026-04-21T02:23:06.871Z","modified":"2026-04-21T02:23:07.060Z"},"maintainers":[{"name":"40verse","email":"wdgibsonjr@gmail.com"}],"description":"The universal API-to-MCP bridge. Any API. Any protocol. Token-aware.","homepage":"https://github.com/40verse/40mcp#readme","keywords":["mcp","model-context-protocol","rest","api","graphql","openapi","bridge","shim","claude","cursor","ai","tools","agent","token-aware"],"repository":{"type":"git","url":"git+https://github.com/40verse/40mcp.git"},"author":{"name":"40verse"},"bugs":{"url":"https://github.com/40verse/40mcp/issues"},"license":"MIT","readme":"# 40mcp\r\n\r\nThe universal API-to-MCP bridge. **Any API. Any direction. Token-aware.**\r\n\r\nA [40verse](https://github.com/40verse) project.\r\n\r\n[![npm version](https://img.shields.io/npm/v/40mcp.svg)](https://www.npmjs.com/package/40mcp)\r\n[![GitHub Stars](https://img.shields.io/github/stars/40verse/40mcp.svg)](https://github.com/40verse/40mcp)\r\n[![npm downloads](https://img.shields.io/npm/dm/40mcp.svg)](https://www.npmjs.com/package/40mcp)\r\n\r\n**[Community configs →](configs/) · [Specification →](SPEC.md) · [API reference →](API.md) · [Troubleshooting →](TROUBLESHOOTING.md) · [Changelog →](CHANGELOG.md)**\r\n\r\n---\r\n\r\n## 60-second golden path\r\n\r\nOne command. No API keys. Two real public MCP upstreams. Proof 40mcp works before you commit to anything:\r\n\r\n```bash\r\nnpx 40mcp@beta link configs/microsoft-huggingface-bridge.mcp.json\r\n```\r\n\r\n**What happens:** 40mcp reads the `.mcp.json`, opens SSE/HTTP connections to Microsoft Learn Docs and HuggingFace's public MCP endpoints, namespaces their tools under `msdocs.*` and `hf.*`, and presents them to your MCP client as one surface. No credentials required — both upstreams are public.\r\n\r\nList the tools without starting the bridge:\r\n\r\n```bash\r\nnpx 40mcp@beta link configs/microsoft-huggingface-bridge.mcp.json --inspect\r\n```\r\n\r\nExpected shape (tool names vary upstream-to-upstream — browse [`configs/microsoft-huggingface-bridge.mcp.json`](configs/microsoft-huggingface-bridge.mcp.json) for the reference config):\r\n\r\n```\r\nmsdocs.microsoft_docs_search        — search Microsoft Learn documentation\r\nmsdocs.microsoft_docs_fetch         — fetch a full doc page as markdown\r\nmsdocs.microsoft_code_sample_search — search Microsoft/Azure code samples\r\nhf.model_search                     — search HuggingFace models\r\nhf.dataset_search                   — search HuggingFace datasets\r\n…\r\n```\r\n\r\n> **Requires Node 18+.** The demo reaches `learn.microsoft.com` and `huggingface.co` — if your network blocks either, the `link` command fails with a clear error rather than hanging.\r\n\r\n### The two linking configs the demo uses\r\n\r\nThe command runs the **aggregator** file in [`configs/`](configs/microsoft-huggingface-bridge.mcp.json), which links the two prefixed upstream entries below as a single MCP surface. You don't run the two config files directly — the aggregator does.\r\n\r\n| Config | Upstream | Prefix |\r\n|--------|----------|--------|\r\n| [`configs/microsoftdocs.mcp.json`](configs/microsoftdocs.mcp.json) | `https://learn.microsoft.com/api/mcp` | `msdocs.` |\r\n| [`configs/huggingface.mcp.json`](configs/huggingface.mcp.json) | `https://huggingface.co/mcp` | `hf.` |\r\n\r\nBoth upstream configs are public, require no credentials, and stay inside the MCP protocol end-to-end. The rest of [`configs/`](configs/) are REST-bridge configs (GitHub, Stripe, Slack, …) rather than MCP-to-MCP linking configs.\r\n\r\n### What to try next\r\n\r\n- **Give it a real API.** [Serve a community config →](#from-a-community-config) — GitHub, Stripe, Slack, Linear, and 25+ more live under [`configs/`](configs/).\r\n- **Publish it.** [Deploy a single authenticated frontdoor over SSE →](docs/FRONTDOOR.md) for remote MCP clients (GPT Actions, claude.ai, Cursor).\r\n- **Learn the mental model.** [Bridge vs frontdoor →](docs/BRIDGE_VS_FRONTDOOR.md) — `serve` exposes one API; `link` fronts many.\r\n\r\n---\r\n\r\n## Why 40mcp over basic OpenAPI bridges?\r\n\r\nBasic OpenAPI → MCP converters exist and are fine for quick prototypes. 40mcp is the choice when you need advanced features:\r\n\r\n| Capability | Basic bridges | 40mcp |\r\n|-----------|--------------|-------|\r\n| Token-aware response shaping | No | Yes — `tokenBudget`, `pick`, `omit`, `limit` |\r\n| Compound tool chains | No | Yes — multi-step sequences as single tools |\r\n| Undocumented API discovery | No | Yes — HAR loader reverse-engineers from traffic |\r\n| Sealed credential vault | No | Yes — AES-256-GCM, zero-plaintext, JIT tokens |\r\n| Human-in-the-loop policy gates | No | Yes — per-tool approval before dispatch |\r\n| Self-referential API archaeology | No | Yes — reverse bridge → HAR → reload → new bridge |\r\n| Multi-tenant auth isolation | No | Yes — per-call auth context, allowlist/blocklist |\r\n| Security controls | Varies | Yes — prototype-safe, SSRF-blocked, injection-hardened, input-validated (see [SAFE-DEFAULTS.md](docs/SAFE-DEFAULTS.md)) |\r\n\r\n**Rule of thumb:** Use a basic bridge for a single documented API in development. Use 40mcp when token efficiency matters, the API is undocumented, or you need security controls, policy gates, or composition.\r\n\r\nFor a head-to-head comparison vs FastMCP specifically, see [docs/COMPARISON.md](docs/COMPARISON.md).\r\n\r\n---\r\n\r\n## Install\r\n\r\n```bash\r\nnpm install -g 40mcp@beta     # or: npx 40mcp@beta <command>\r\n```\r\n\r\n**Node.js >= 18 required.** Tested on 18.x LTS, 20.x, 22.x. Linux / macOS / Windows. One production dependency (`@modelcontextprotocol/sdk`).\r\n\r\n### Release status\r\n\r\n| | |\r\n|---|---|\r\n| **Version** | See [`package.json`](package.json) — 0.1.x beta line |\r\n| **Versioning** | [Semver](https://semver.org). Pre-1.0: minor versions may include breaking changes; patch versions are backwards-compatible fixes |\r\n| **Open security blockers** | 0 — see [security advisories](https://github.com/40verse/40mcp/security/advisories) |\r\n| **Supported Node versions** | 18.x LTS · 20.x · 22.x |\r\n| **Test suite** | `npm run test:all` — runs on every PR against Node 18 / 20 / 22 via GitHub Actions |\r\n| **Contract** | See [SPEC.md](SPEC.md) — security model, operational limits, non-goals |\r\n\r\n---\r\n\r\n## Features\r\n\r\n| Feature | What it does |\r\n|---------|-------------|\r\n| **OpenAPI/Swagger loader** | Spec → MCP tools (3.x + 2.x) |\r\n| **GraphQL loader** | Introspection → MCP tools |\r\n| **HAR loader** | Browser traffic → MCP tools (no spec needed) |\r\n| **Plugin system** | `registerLoader()` for gRPC, WebSocket, SOAP, etc. |\r\n| **Response transforms** | Token-aware shaping — pick, omit, limit, tokenBudget |\r\n| **Compound chains** | Multi-call sequences as single tools (depth-guarded) |\r\n| **Server mixing** | Combine N APIs into one MCP server |\r\n| **SSE transport** | Serve remotely, not just stdio |\r\n| **Config validation** | Catch errors before runtime with `40mcp validate` |\r\n| **Authentication** | Header, bearer, basic, OAuth2 (auto-refresh) |\r\n| **AI generation** | Deterministic: spec → config, no LLM needed |\r\n| **TUI** | Spinner, progress, tables, color, NO_COLOR compliant |\r\n| **Reverse bridge** | MCP tools → REST API + auto-generated OpenAPI spec |\r\n| **Webhook ingestion** | HTTP webhooks → tool dispatch with HMAC validation |\r\n| **Multi-tenant** | Per-call auth context, tool allowlist/blocklist |\r\n| **Sealed vault** | AES-256-GCM envelope encryption for API keys |\r\n| **Policy gates** | Human-in-the-loop approval for dangerous actions |\r\n| **MCP linking** | Connect to existing MCP servers, re-expose with policy gates |\r\n| **Steering** (`40mcp/steering`) | Forced-inference write classification for agent memory tools |\r\n| **AI generation (LLM prompt)** | Describe an API → get a prompt pair for any LLM |\r\n\r\nSee [SPEC.md](SPEC.md) for the full contract and non-goals.\r\n\r\n## Other quick-starts\r\n\r\nAfter the golden path above, here are the other entry points by input type:\r\n\r\n```bash\r\nnpx 40mcp from-openapi ./swagger.json       # Spec → MCP server\r\nnpx 40mcp from ./recording.har              # Auto-detect (OpenAPI / GraphQL / HAR)\r\nnpx 40mcp serve configs/github.json         # Ship a community config\r\nnpx 40mcp link .mcp.json                    # Link to existing MCP servers\r\nnpx 40mcp generate spec.json --out api.json # Generate config from spec (deterministic)\r\n```\r\n\r\n### AI coding-agent onboarding\r\n\r\nPaste this into Claude Code, Cursor, or any coding agent to wire 40mcp into your workflow automatically:\r\n\r\n> **Set up 40mcp in my workflow.**\r\n>\r\n> I want to use 40mcp to bridge MCP servers into my AI tooling. Please:\r\n>\r\n> 1. Install: `npm install -g 40mcp` (or run via `npx 40mcp`).\r\n> 2. Read `configs/microsoft-huggingface-bridge.mcp.json` and `test/bridge-microsoft-huggingface.test.js` so you understand the linking model.\r\n> 3. Look at my existing `.mcp.json` (or create one if it doesn't exist).\r\n> 4. Add 40mcp as a linker in front of my current MCP servers so I get token-aware response shaping, policy gates, and a unified tool namespace. Use `connectFromConfig` under the hood.\r\n> 5. Smoke-test with two upstream servers:\r\n>    - **Microsoft Learn Docs** (`https://learn.microsoft.com/api/mcp`) — no auth required, prefix `msdocs.`\r\n>    - **HuggingFace** (`https://huggingface.co/mcp`) — public read-only works without a token, prefix `hf.`\r\n> 6. Run `npx 40mcp link <config> --inspect` and show me the resulting tool list. Confirm dispatch works for `msdocs.microsoft_docs_search` and `hf.model_search`.\r\n> 7. If anything fails (network blocked, etc.) stop and tell me exactly what to do — don't silently skip.\r\n>\r\n> Reference implementation: `configs/microsoft-huggingface-bridge.mcp.json`\r\n\r\n## Input-type reference\r\n\r\nEvery format 40mcp can consume, one section each.\r\n\r\n### From an OpenAPI Spec\r\n\r\n```bash\r\nnpx 40mcp from-openapi ./swagger.json\r\n```\r\n\r\n### From Browser Traffic (No Spec)\r\n\r\n```bash\r\nnpx 40mcp from-har ./recording.har --min-observations 3\r\n```\r\n\r\n### From a Community Config\r\n\r\n```bash\r\nnpx 40mcp serve configs/github.json\r\n```\r\n\r\nConfigs available across GitHub, Stripe, Slack, Jira, Linear, Notion, and more. [Browse all configs →](configs/)\r\n\r\n### Add to `.mcp.json`\r\n\r\n```json\r\n{\r\n  \"mcpServers\": {\r\n    \"my-api\": {\r\n      \"command\": \"npx\",\r\n      \"args\": [\"40mcp\", \"from-openapi\", \"./swagger.json\"],\r\n      \"env\": { \"API_KEY\": \"\" }\r\n    }\r\n  }\r\n}\r\n```\r\n\r\n> **Credential handling:** Leave `\"API_KEY\": \"\"` — the value is populated from your shell environment at runtime, not from this file. Never put a plaintext API key as the value here; `.mcp.json` is typically committed to version control. For production use, see [Sealed Vault](#sealed-vault-zero-plaintext-credentials) to manage secrets without exposing them in any environment variable.\r\n\r\n### Programmatic\r\n\r\n```js\r\nimport { createRestBridge, loadOpenApiSpec } from '40mcp';\r\n\r\nconst { baseUrl, tools } = await loadOpenApiSpec('./swagger.json');\r\nawait createRestBridge({\r\n  name: 'my-api',\r\n  baseUrl,\r\n  tools,\r\n  auth: { type: 'bearer', envVar: 'API_TOKEN' }, // credentials from env, not hardcoded\r\n}).start();\r\n```\r\n\r\n## Response Transforms (Token-Aware)\r\n\r\nMCP tools dump raw JSON into the agent's context. A `list_users` returning 500 users burns 50k tokens. Response transforms shape output before it hits the context window.\r\n\r\n```json\r\n{\r\n  \"response\": {\r\n    \"pick\": [\"id\", \"name\", \"email\"],\r\n    \"limit\": 25,\r\n    \"summary\": \"Showing {shown} of {total} users\",\r\n    \"tokenBudget\": 4000\r\n  }\r\n}\r\n```\r\n\r\n| Transform | What it does |\r\n|-----------|-------------|\r\n| `pick` | Keep only these fields (dot-notation: `\"user.name\"`) |\r\n| `omit` | Remove these fields |\r\n| `limit` | Cap array to N items |\r\n| `summary` | Prepend count metadata when truncated |\r\n| `tokenBudget` | Hard cap -- truncate to fit token budget |\r\n| `flatten` | Nested objects -> dot-notation keys |\r\n| `template` | Format each item: `\"{name} ({email})\"` |\r\n\r\n## Compound Tool Chains\r\n\r\nMulti-call sequences as a single tool. The bridge resolves `$references`, parallelizes independent steps, and merges results. Recursion depth guard prevents infinite loops.\r\n\r\n```json\r\n{\r\n  \"name\": \"get_user_full_profile\",\r\n  \"chain\": [\r\n    { \"call\": \"get_user\", \"args\": { \"user_id\": \"$args.user_id\" }, \"as\": \"user\" },\r\n    { \"call\": \"list_devices\", \"args\": { \"user_id\": \"$args.user_id\" }, \"as\": \"devices\" },\r\n    { \"call\": \"get_activity\", \"args\": { \"user_id\": \"$user.id\" }, \"as\": \"activity\" }\r\n  ]\r\n}\r\n```\r\n\r\n## MCP Server Linking\r\n\r\nConnect to existing MCP servers and re-expose their tools with 40mcp's features on top:\r\n\r\n```bash\r\n40mcp link .mcp.json                    # Connect to all configured servers\r\n40mcp link npx @some/mcp-server         # Connect to a single server\r\n\r\n# Publish a linked frontdoor as a single authenticated SSE MCP endpoint.\r\n# Upstreams run as stdio children inside the same process; only this port\r\n# is public. See docs/FRONTDOOR.md for the full deployment guide.\r\n40mcp link frontdoor.mcp.json --sse 8080 --host 0.0.0.0 \\\r\n  --require-bearer-env FRONTDOOR_TOKEN\r\n```\r\n\r\n```js\r\nimport { connectStdio, connectFromConfig } from '40mcp';\r\n\r\n// Single server\r\nconst server = await connectStdio({\r\n  command: 'npx', args: ['-y', '@modelcontextprotocol/server-filesystem', '/tmp'],\r\n  prefix: 'fs',\r\n});\r\nconsole.log(server.tools); // [{ name: 'fs.read_file', ... }]\r\n\r\n// From .mcp.json\r\nconst cluster = await connectFromConfig(mcpJson.mcpServers);\r\nawait cluster.dispatch('github.list_repos', { owner: 'me' });\r\n```\r\n\r\n## Security Note\r\n\r\n> **Treat a 40mcp config file like executable code.** It controls which APIs are called, which credentials are forwarded, and how tool arguments are processed. Do not load configs from untrusted sources without review. See [SPEC.md](SPEC.md) §7 for the full threat model and security model.\r\n\r\n## Sealed Vault (Zero-Plaintext Credentials)\r\n\r\nAPI keys are envelope-encrypted at rest with AES-256-GCM. At runtime, bridges access them via short-lived JWTs — sealed API keys never appear in `process.env` or config files.\r\n\r\n**Production: use the vault daemon.** The daemon holds the passphrase in memory; bridge processes authenticate with a scoped `VAULT_DAEMON_SECRET` and receive short-lived JWTs. No process ever holds the master passphrase:\r\n\r\n```bash\r\n# Start daemon once (prints VAULT_DAEMON_SECRET — store it securely)\r\n40mcp vault daemon start --background\r\n```\r\n\r\n```js\r\nimport { createVaultDaemonClient, createRestBridge } from '40mcp';\r\n\r\nconst vaultClient = createVaultDaemonClient({\r\n  daemonSecret: process.env.VAULT_DAEMON_SECRET, // scoped token, not the passphrase\r\n});\r\n\r\nconst bridge = createRestBridge({\r\n  hooks: { beforeRequest: vaultClient.createBearerHook('GITHUB_TOKEN') },\r\n  // ...\r\n});\r\n```\r\n\r\n**Local development only:** passing `VAULT_PASSPHRASE` directly via env var is the quick-start path. Avoid this in production — the passphrase in an env var is readable by any process in the same environment and will appear in process listings.\r\n\r\n```js\r\n// ⚠ Local / development only — do not use in production\r\nconst vault = createVault({ path: '.vault.json', passphrase: process.env.VAULT_PASSPHRASE });\r\n\r\n// Seal a secret (returns seal:// ID)\r\nconst sealId = await vault.set('GITHUB_TOKEN', '<your-github-token>', { service: 'github' });\r\n\r\n// Issue a 5-minute JWT credential\r\nconst { token } = await vault.issueToken('GITHUB_TOKEN');\r\n\r\n// JIT auth hook (unseals per-request, in-memory only)\r\nconst hook = vault.createAuthHook({ GITHUB_TOKEN: 'Authorization' });\r\n```\r\n\r\n## Human-in-the-Loop Policy Gates\r\n\r\nGate dangerous tool calls with configurable approval:\r\n\r\n```js\r\nimport { createPolicyGate, createStdinApprovalHandler } from '40mcp';\r\n\r\nconst gated = createPolicyGate({\r\n  dispatch: bridge.dispatch,\r\n  approvalHandler: createStdinApprovalHandler(), // CLI approval prompt\r\n  toolPolicies: { delete_user: 'require_approval' },\r\n});\r\n```\r\n\r\nTools can declare `\"policy\": \"require_approval\"` in their config. Policy rules: `allow`, `deny`, `require_approval`, `log_only`.\r\n\r\n## AI-Assisted Config Generation\r\n\r\n```bash\r\n# Deterministic: OpenAPI spec → config (no LLM needed)\r\n40mcp generate spec.json --out my-api.json\r\n\r\n# LLM-assisted: get prompt pair for any model\r\n40mcp generate --describe \"Stripe payments API\"\r\n```\r\n\r\n```js\r\nimport { generatePrompt, parseGeneratedConfig, generateFromSpec } from '40mcp';\r\n\r\n// For any LLM\r\nconst { system, user } = generatePrompt({ description: 'GitHub REST API' });\r\n// Feed to Claude, GPT, Gemini, local model, etc.\r\n\r\n// Deterministic (no LLM)\r\nconst config = generateFromSpec(openApiSpec);\r\n```\r\n\r\n## Authentication\r\n\r\n| Mode | Config |\r\n|------|--------|\r\n| Header | `{ type: 'header', header: 'X-API-Key', envVar: 'API_KEY' }` |\r\n| Bearer | `{ type: 'bearer', envVar: 'TOKEN' }` |\r\n| Basic | `{ type: 'basic', envVar: 'CREDS' }` |\r\n| OAuth2 | `{ type: 'oauth2', tokenUrl: '...', clientIdEnv: '...', clientSecretEnv: '...' }` |\r\n\r\nOAuth2 auto-refreshes tokens with expiry-aware caching and concurrent request coalescing.\r\n\r\n## CLI Reference\r\n\r\n```\r\n40mcp serve <config>                  Start MCP server from config\r\n40mcp from <spec-or-url>              Auto-detect format and start\r\n40mcp from-openapi <spec>             OpenAPI/Swagger -> MCP server\r\n40mcp from-graphql <endpoint>         GraphQL -> MCP server\r\n40mcp from-har <recording.har>        HAR -> MCP server\r\n40mcp mix <c1.json> <c2.json> [...]   Combine APIs into one server\r\n40mcp reverse <config> --port 8080    MCP tools -> REST API\r\n40mcp link <.mcp.json|command>        Link to existing MCP servers\r\n40mcp generate <spec|--describe>      Generate config (AI or deterministic)\r\n40mcp inspect <config>                List tools without starting\r\n40mcp validate <config>               Validate config, report errors\r\n40mcp init                            Interactive starter-config wizard\r\n40mcp doctor <config>                 Auth / reachability / shape diagnostics\r\n40mcp vault <sub>                     Sealed-vault ops (init, seal, list,\r\n                                      rotate, rotate-kek, delete, recover, daemon)\r\n```\r\n\r\n## Architecture\r\n\r\nModules mapped to dimensions:\r\n\r\n```\r\nD1: The Line (core bridge)\r\n+-- bridge.js              Dispatch engine\r\n+-- core/client.js         API client (auth, OAuth2, timeout)\r\n+-- core/path.js           URL interpolation, query strings\r\n+-- transport/             stdio + SSE\r\n\r\nD2: The Plane (loaders + discovery)\r\n+-- openapi.js             OpenAPI 3.x + Swagger 2.x\r\n+-- loaders/graphql.js     GraphQL introspection\r\n+-- loaders/har.js         HAR traffic recording\r\n+-- loaders/registry.js    Plugin system + auto-detection\r\n+-- generate.js            AI-assisted config generation\r\n\r\nD3: The Cube (composition + shaping)\r\n+-- compose/chain.js       Compound chains (depth-guarded)\r\n+-- compose/mixer.js       Multi-server mixing\r\n+-- transforms/response.js Token-aware response shaping\r\n+-- connect.js             MCP-to-MCP client connector\r\n+-- webhook/listener.js    Webhook ingestion\r\n+-- tenant/scope.js        Multi-tenant scoping\r\n\r\nD4: The Tesseract (self-reference + security)\r\n+-- reverse/server.js      MCP -> REST (+ auto OpenAPI spec)\r\n+-- security/vault.js      Sealed credential vault (envelope encryption)\r\n+-- security/policy.js     Human-in-the-loop gates\r\n+-- validate.js            Config validation\r\n+-- tui.js                 Terminal UI primitives\r\n\r\nMeta:\r\n+-- index.d.ts             Full TypeScript declarations\r\n+-- errors.js              Structured error taxonomy (19 codes)\r\n+-- cli.js                 14 subcommands\r\n\r\nconfigs/                   Community configs (see directory for current list)\r\n+-- github.json            GitHub API\r\n+-- stripe.json            Stripe API\r\n+-- slack.json             Slack API\r\n+-- linear.json            Linear API\r\n+-- sentry.json            Sentry API\r\n+-- ...and more            [Browse all configs →](configs/)\r\n```\r\n\r\nOne dependency: `@modelcontextprotocol/sdk`. All other functionality uses Node builtins. Full TypeScript via `.d.ts`.\r\n\r\n## The Tesseract\r\n\r\n40mcp builds through four dimensions, where each folds the previous into itself:\r\n\r\n```\r\nD1: REST Bridge        D2: Loaders           D3: Composition       D4: Self-Reference\r\n   *------*            ==========            +---------+           +---------+\r\n                       ==========            | Mixer   |          /| Reverse/|\r\n  REST API             OpenAPI               | Chain   |         +---------+ |\r\n     |                 GraphQL               | Shape   |         | Bridge  | |\r\n  MCP Tools            HAR replay            +---------+         | wraps   |/\r\n                          |                      |               +-itself--+\r\n                       MCP Tools             MCP Tools                |\r\n                                                                 loop\r\n```\r\n\r\n**D1 -- The Line.** One REST API becomes MCP tools.\r\n\r\n**D2 -- The Plane.** Multiple protocols (OpenAPI, GraphQL, HAR, plugins) converge onto one tool surface.\r\n\r\n**D3 -- The Cube.** Tools interact. Mixer combines APIs. Chains compose multi-step operations. Transforms shape output.\r\n\r\n**D4 -- The Tesseract.** Reverse Bridge inverts direction — MCP tools become REST endpoints. Then: reverse bridge → HAR capture → HAR loader → new bridge. **40mcp wraps itself.**\r\n\r\n**Deep dive:** [CONCEPT.md](CONCEPT.md) — why each dimension is a structural transformation, not a feature list.\r\n\r\n## Local Development Without API Keys\r\n\r\n40mcp works offline against [vercel-labs/emulate](https://github.com/vercel-labs/emulate) — a local API emulator that runs production-fidelity servers for GitHub, Vercel, Slack, Google, Microsoft, and AWS on localhost. No real credentials, no network calls, no rate limits.\r\n\r\n> **Repo-only fixtures.** The `.emulate/` directory below lives in the git checkout of this repository and is **not** bundled into the published npm package. To use these seed configs, clone the repo (`git clone https://github.com/40verse/40mcp`) and run the commands from the repo root. Consumers installing via `npm install 40mcp` should point `npx emulate` at their own config and bridge it to 40mcp with their own config files.\r\n\r\n```bash\r\n# Inside a git checkout of 40mcp:\r\n\r\n# Start emulated APIs\r\nnpx emulate --service github,vercel,slack --seed .emulate/emulate.config.yaml\r\n\r\n# Start 40mcp pointing at them (pre-built configs included in the repo)\r\nnpx 40mcp serve .emulate/configs/github.json   # → localhost:4001\r\nnpx 40mcp serve .emulate/configs/vercel.json   # → localhost:4000\r\nnpx 40mcp serve .emulate/configs/slack.json    # → localhost:4003\r\n\r\n# Or mix into one MCP server\r\nnpx 40mcp mix .emulate/configs/github.json .emulate/configs/vercel.json .emulate/configs/slack.json\r\n```\r\n\r\nSeed data, CI integration, and file persistence docs live in [`.emulate/README.md`](https://github.com/40verse/40mcp/blob/main/.emulate/README.md) (repo-only).\r\n\r\n## Deploy\r\n\r\n### One-Click Cloud Deploy\r\n\r\nDeploy 40mcp to a cloud platform with your bridge config mounted as a volume or secret:\r\n\r\n[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/new/template?template=https://github.com/40verse/40mcp)\r\n&nbsp;&nbsp;\r\n[![Deploy to Fly.io](https://www.deploytoflyio.com/button.svg)](https://fly.io/launch?from=github.com/40verse/40mcp)\r\n\r\n### Docker\r\n\r\n```bash\r\n# Pull the official image\r\ndocker pull ghcr.io/40verse/40mcp:latest\r\n\r\n# Run with your config\r\ndocker run -p 8080:8080 \\\r\n  -v ./my-config.json:/config.json:ro \\\r\n  --env-file .env \\\r\n  ghcr.io/40verse/40mcp:latest\r\n\r\n# Or use the docker-compose template\r\ncp examples/docker-compose.yml .\r\ndocker compose up\r\n```\r\n\r\nSee [`examples/docker-compose.yml`](examples/docker-compose.yml) for vault volume, env file, and port mapping.\r\nSee [`fly.toml`](fly.toml) and [`railway.json`](railway.json) for platform-specific config.\r\n\r\n## Contributing\r\n\r\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and PR guidelines.\r\n\r\nFor AI-assisted contributors: [AGENTS.md](AGENTS.md) covers codebase orientation, architecture rules, and security-critical surfaces.\r\n\r\n## Integration Guide\r\n\r\nDetailed setup for Claude Desktop, Cursor, VS Code, Claude Code, and SSE deployments: [docs/ai-workflow.md](docs/ai-workflow.md)\r\n\r\n## Configuration & Operator Docs\r\n\r\n- [docs/CONFIGURATION_MODEL.md](docs/CONFIGURATION_MODEL.md) — topology vs runtime, identity, precedence\r\n- [docs/SETTINGS.md](docs/SETTINGS.md) — `40mcp.settings.json` operator guide with recipes\r\n- [docs/BRIDGE_VS_FRONTDOOR.md](docs/BRIDGE_VS_FRONTDOOR.md) — `serve` vs `link` mental model\r\n- [docs/COMMANDS/settings-and-doctor.md](docs/COMMANDS/settings-and-doctor.md) — `settings show` and `doctor` scope\r\n- [docs/FRONTDOOR.md](docs/FRONTDOOR.md) — published SSE deployment patterns\r\n- [docs/TESTING.md](docs/TESTING.md) — operator testing strategy for bridges, frontdoors, tenants, and policy gates\r\n- [docs/COMPARISON.md](docs/COMPARISON.md) — 40mcp vs FastMCP capability matrix\r\n- [docs/MIGRATION_FROM_FASTMCP.md](docs/MIGRATION_FROM_FASTMCP.md) — FastMCP → 40mcp primitive mapping and divergences\r\n\r\n## License\r\n\r\nMIT. 40mcp is MIT-licensed and fully usable for self-hosting. Hosted or commercial layers may exist separately, but the core bridge remains open source.\r\n","readmeFilename":"README.md","_rev":"1-d9ac9455f6ef1b3845358b74b8fee5db"}