Namespaced tools
Every tool is exposed as server__tool, 64 characters at most. Name collisions get a deterministic _2, _3 suffix.
127.0.0.1:4747/mcp
A native macOS menu bar app. Claude Code, Cursor and Claude Desktop share the same server processes, and one click swaps the whole tool set.
:4747/mcp
waiting for tools/call…Works with any MCP server, stdio or Streamable HTTP
Three clients and four servers means twelve processes and three JSON files to keep in sync. Tulma runs each server once and serves it to everyone.
Group servers into “Work”, “Personal”, “Client X”. Only the active workspace runs. Connected clients get tools/list_changed and pick up the new tools without reconnecting.
Active8 of 8 running
Click a workspace, a server or any switch. The demo runs by itself until you do.
Every tool is exposed as server__tool, 64 characters at most. Name collisions get a deterministic _2, _3 suffix.
Hide individual tools from clients without removing the server.
Matching env vars and headers move to the macOS Keychain. config.json keeps only a reference.
Crashed servers restart with backoff: 1, 2, 4, 8, 16 s, then they stay in the error state. On quit, each server's whole process group is killed, so npx grandchildren don't outlive it.
Paste or drop configs from Claude Desktop, Cursor or VS Code. JSONC is fine. A preview marks each entry as new, duplicate or invalid.
Download a workspace as mcpServers JSON. Secrets are left out unless you ask for them.
Tool calls, lifecycle events and server stderr go to SQLite, with secret redaction and a retention limit.
Prompts and resources are proxied as well.
Streamable HTTP for clients that support it, and a stdio bridge, tulma connect, for those that don't. Connect a client… in the window footer shows these snippets with the real paths filled in.
tulma connect reads the URL from endpoint.json--urlThe app owns the UI only. The daemon is a child process that owns all state, the servers and the HTTP endpoint. They talk NDJSON JSON-RPC over stdin/stdout. Quit the app, stdin closes, and the daemon stops every server and exits.
The endpoint listens on loopback only and refuses requests that carry a foreign Host or Origin, which blocks DNS-rebinding attacks from web pages.
Secret values leave the Keychain only to start a server, to show them in the server editor, or in an export with Include secrets on.
Bearer tokens and token=, key=, password= style values are masked before they are stored.
By default, tokens in URLs and arguments (?token=…, --api-key …) become ***. The file is safe to share.
Servers run as your user, the same as when a client starts them. Only add servers you trust.
There are no prebuilt binaries yet. Build from source with Xcode and Rust. A free Apple ID is enough to sign it, or sign ad-hoc.