MCP

Security & configuration

MCP servers run third-party code, so Flowly wraps them with several layers of defense β€” all on where it should be, opt-in where it matters.

Built-in guards

  • OSV malware gate β€” before an npx/uvx server spawns, Flowly queries the OSV database for known-malware advisories and blocks the spawn if any. Fail-open (network errors allow); per-server opt-out with osvCheck: false.
  • Filtered subprocess environment β€” stdio servers get only a safe baseline (PATH, HOME, …) plus the env you list. Flowly's own provider keys never leak into them.
  • Credential redaction β€” tokens and keys in error output are replaced with [REDACTED] before the model or logs see them.
  • Prompt-injection scan β€” tool descriptions are scanned for override patterns and logged (not blocked) so a hostile server is detectable.
  • Circuit breaker β€” a server that fails repeatedly is short-circuited for a cooldown, so the model stops hammering it and recovers automatically.
  • Sandbox β€” under FLOWLY_SANDBOX the whole agent, MCP subprocesses included, runs inside sandbox-exec (macOS) / bwrap (Linux).
Trust the servers you add
These guards reduce risk; they don't make an arbitrary MCP server safe. A server you connect runs real code and sees the arguments the agent sends it. Add servers from sources you trust.

Where to look when a server fails

Subprocess stderr is redirected to ~/.flowly/logs/mcp-stderr.log (so a chatty server can't corrupt the TUI). When a server won't connect:

bash
flowly mcp test <name>            # connect + surface the error
cat ~/.flowly/logs/mcp-stderr.log # the server's own output

mcpServers config reference

Servers live under the top-level mcpServers key in ~/.flowly/config.json. Keys are camelCase on disk; server names and env/headers keys are preserved verbatim.

json
{
  "mcpServers": {
    "example": {
      "enabled": true,
      "command": "npx",                  // stdio: command + args + env
      "args": ["-y", "@scope/pkg"],
      "env": { "TOKEN": "${TOKEN}" },
      "url": "",                         // http/sse: url + headers instead
      "headers": {},
      "transport": "auto",               // auto | stdio | http | sse
      "timeout": 120,                    // per-tool-call seconds
      "connectTimeout": 60,              // initial connect seconds
      "tools": {
        "include": [],                   // whitelist (empty = all)
        "exclude": [],                   // blacklist (ignored if include set)
        "resources": false,             // expose resources/* utility tools
        "prompts": false                // expose prompts/* utility tools
      },
      "auth": "",                        // "" | "oauth"
      "scope": "",                       // optional OAuth scope
      "sslVerify": true,                 // true | false | CA-bundle path
      "clientCert": "",                  // mTLS cert (path or [cert, key])
      "clientKey": "",
      "osvCheck": true,                  // OSV malware gate
      "sampling": {                      // server-initiated LLM (off by default)
        "enabled": false,
        "model": "",
        "maxRpm": 10,
        "maxTokensCap": 4096,
        "allowedModels": []
      }
    }
  }
}