MCP & web lookup
Squeezy can call tools on the MCP servers you configure, and it can reach the public web through two permission-gated tools. Both are off the default path: a server is only contacted once you add and enable it, and a web call is only made when the agent asks and your permission policy allows it. This page covers wiring up servers, the operator commands for inspecting them, how authentication and timeouts are configured, what happens when a server asks the user a question, and the boundaries on web access.
Adding and managing servers
MCP servers are declared in settings and managed with a small set of subcommands. Each server is added to either your user settings or the project's committed settings, so personal tools follow you across repositories while shared ones travel with the team. A server can run as a local subprocess (stdio) or over the network (HTTP, or HTTP+SSE).
Once a server is enabled, Squeezy discovers its tools once per agent turn across all enabled servers. Tool names are namespaced by server so two servers can expose a tool of the same name without colliding, and each server carries a default permission policy of allow, ask, or deny that gates whether its tools run, prompt, or are refused.
- squeezy mcp add <name> --user --transport stdio --command <cmd> [--arg ...], a local subprocess server.
- squeezy mcp add <name> --project --transport http --url <url>, a network server committed with the repo.
- squeezy mcp enable / disable / remove <name> --user|--project, toggle or delete a configured server.
- Per-server default permission is set with --permission-default allow|ask|deny; individual tools can be allow-listed (--enabled-tool) or blocked (--disabled-tool).
squeezy mcp add docs --user --transport stdio --command npx --arg -y --arg @scope/docs-mcp
squeezy mcp add api --project --transport http --url https://mcp.example.com
squeezy mcp enable docs --user
squeezy mcp remove api --project Inspecting servers from the operator side
Two commands tell you what is configured and whether it works. squeezy mcp list prints every configured server with its state, transport, endpoint, and an auth/env column that names the bearer variable and any env or env-sourced headers, never their values. Add --probe to run a live initialize handshake against each enabled server and report ready, stale, failed, or cancelled alongside the advertised tool count, the same check doctor --probe performs but scoped to MCP. Pass --json to either command for a machine-readable form.
squeezy mcp test <name> probes a single server with the full initialize and tool-discovery handshake. It is an explicit operator action, so it runs even when the server is disabled in settings, re-enabling it just for the one-shot probe and saying so in the output, and reports the advertised tool count on success or the failure reason otherwise. A failed handshake exits non-zero so it slots into scripts and CI.
squeezy mcp list --probe
squeezy mcp test docs
squeezy doctor --probe Authentication, working directory, and timeouts
Credentials are never stored in settings as values. For HTTP and SSE servers a bearer token is configured by naming the environment variable that holds it (bearer_token_env_var), resolved at session start; an unset variable is skipped rather than sent empty. Static headers can be attached to every request (http_headers), and headers whose values come from environment variables (env_http_headers) are resolved the same way, with the env-sourced value winning on a name clash. For stdio servers, env passes environment entries to the child process and cwd sets its working directory so relative config paths, Node scripts, and Python virtualenvs resolve from a predictable root.
Timeouts are tunable per phase. timeout_ms sets a single budget for a server; discovery_timeout_ms overrides it for the bring-up and tool-discovery phase, and tool_call_timeout_ms overrides it for tool calls and resource reads. This separates a server that is slow to initialize but fast per call from one with the opposite shape. When a phase exceeds its budget the session is invalidated so the next call starts fresh; the default is 30 seconds.
- bearer_token_env_var, http_headers, env_http_headers, network auth for HTTP/SSE, read from env at session start, only the names ever appear in config or mcp list.
- env and cwd, environment and working directory for stdio subprocesses.
- timeout_ms with optional discovery_timeout_ms / tool_call_timeout_ms overrides, per-phase budgets, 30s default.
SSE transport: reconnect and backoff
Alongside stdio and streamable HTTP, Squeezy speaks HTTP+SSE, where the server holds open an event stream and advertises the URL the client posts back to. If that stream drops, Squeezy reopens it on a full-jitter exponential backoff rather than hammering the server: the delay grows from a small base up to a thirty-second cap, spread randomly so many clients reconnecting at once do not stampede. A successful message resets the backoff, so a healthy server that blips once is not penalized, and after ten consecutive failed reconnects the session gives up instead of spinning forever against a crashed daemon.
Because each reconnect re-advertises the post endpoint, Squeezy adopts the fresh URL on every reconnect and rejects an advertised endpoint that does not share the stream's origin, so a token-bearing POST is never redirected to another host.
When a server asks the user a question
MCP servers can elicit input from the user mid-call, either a form to fill in or a URL to confirm. Squeezy routes these through your permission policy so a server cannot drive the UI unchecked. Under the conservative default, ask, every elicitation is forwarded to you for a decision. Under deny, all elicitations are declined without prompting, so a misbehaving server cannot block the agent waiting on input. Under allow, an empty form with no required fields is accepted silently while anything with required fields, or any URL confirmation, is still shown to you.
Every decision, whether it was auto-accepted, auto-declined, or forwarded to the user, is written to a bounded audit record tagged with the server, the policy in force, and a timestamp. That trail makes each elicitation observable from the host, so a server quietly spamming empty forms is visible rather than silent.
Web fetch and search are gated, not default browsing
Squeezy does not browse the web on its own. Two tools reach the network, and both pass through the permission policy first: websearch finds current or external information through a permission-gated backend, and webfetch retrieves one specific HTTP(S) URL with the host shown in the approval summary. Each returns bounded, redacted text with the source URL, retrieval time, citations, and cache-receipt metadata, so a single fetch cannot dump an unbounded page into the conversation, and a repeat fetch comes back as a receipt instead of the same bytes again.
The boundaries are enforced. webfetch refuses internal addresses, loopback, link-local, private ranges, and cloud metadata endpoints, even behind DNS, and a cross-host redirect requires a fresh approval rather than following silently. The intended use is narrow: current public information the user asked for, a URL the user provided, or a page that local docs point you to. Web tools are not a way around Squeezy's permission policy or shell sandbox. Squeezy's own /help is separate again, it answers from bundled docs and never fetches the network to do so.
- websearch, discovery through a permission-gated backend; query shown in the approval summary.
- webfetch, one HTTP(S) URL; host in the approval summary, internal addresses refused, cross-host redirects re-approved.
- Both return bounded, redacted text with source and cache metadata; neither bypasses your permission policy.