Start

Wrapping an MCP server

An MCP server is a tool surface your agent can call — Linear, Slack, a database, your own. Grenz wraps one the same way it wraps GitHub: the server's real credential goes in the vault, the agent gets a GRENZ_TOKEN, and each JSON-RPC method is matched against your policy.

1. Add the upstream

~/.grenz/grenz.yaml
upstreams:
  linear:
    type: mcp
    base_url: https://mcp.linear.app/sse
    credential: linear_token  # vault key
shell
printf %s "$LINEAR_TOKEN" | grenz vault set linear_token

2. Grant the methods it may call

For MCP upstreams, the action is the tool call the agent is making. Grant reads freely, gate writes behind a human, and deny anything destructive:

~/.grenz/policy.yaml
grants:
  - tool: linear
    allow: [session:*, tools:list, issue:read, comment:read, project:read]
    # Linear writes are upserts — one action covers create and update
    require_approval: [issue:write, comment:write]
    deny: [comment:delete, attachment:delete]

budget:
  per_upstream:
    linear: 60

Not sure what the server exposes? Run in --shadow for a while and read the log: every call the agent attempts is recorded with the action name Grenz derived, including the ones a policy would have blocked. Then grenz policy shrinkwrap can tighten the allow list to what was actually used.

3. Point the agent's MCP config at Grenz

Change the server URL to Grenz's route for that upstream, and pass the GRENZ_TOKEN:

mcp config
{
  "mcpServers": {
    "linear": {
      "url": "http://127.0.0.1:8787/u/linear",
      "headers": { "Authorization": "Bearer cav_…" }
    }
  }
}

Batched calls

One MCP request can carry a JSON-RPC batch of several calls. Grenz evaluates each one against the policy rather than judging the envelope, so a batch cannot smuggle a denied action alongside allowed ones. Budgets are counted the same way — a batch bills the sum of its actions, not one.

A note on transports. If you run Grenz in socket mode, MCP clients that take a URL cannot reach it — they generally cannot speak unix sockets. That is why socket mode is opt-in; an MCP-based agent stays on the loopback port.