arizuko

arizuko › concepts › connectors

connectors

A connector gives the agent a tool it did not ship with, such as a DNS registrar or an internal API. You write one TOML block. routd makes the call on the host, inserts the secret into the request and returns the response body to the agent.

The agent gets the tool and the host keeps the credential

Inside the guest, an MCP tool called cloudflare_dns_list exists, and the connector puts no CF_API_TOKEN in the environment, the filesystem or /proc. A shell:env:<KEY> grant for the same key would still add it to the guest environment.

One block has one of two transports

Every connector is one [[connector]] block, and transport picks its dispatch path. A field that one transport does not use is absent for it, and no field has two meanings:

mcp
The default when you leave out transport. The far side is already an MCP server over HTTP. It reports its own tool list, so you declare no tools. It reads only headers, because an HTTP MCP server takes its credential there.
rest
The far side is an ordinary REST API with no MCP support. You declare one [[connector.tool]] per endpoint, and you can use all three credential sinks.

Connectors use HTTP only. No stdio transport exists.

A REST connector needs one block and one tool entry

# connectors.toml in the instance directory
[[connector]]
name      = "cloudflare"
transport = "rest"
url       = "https://api.cloudflare.com/client/v4"
secrets   = ["CF_API_TOKEN"]
headers   = { Authorization = "Bearer {secret:CF_API_TOKEN}" }

  [[connector.tool]]
  name        = "dns_list"
  description = "List DNS records for a Cloudflare zone."
  scope       = "ext:cloudflare:dns:read"
  method      = "GET"
  path        = "/zones/{zone_id}/dns_records"

The agent sees a tool named cloudflare_dns_list, in the form <provider>_<tool>. The tool takes one argument, zone_id, because {zone_id} is a path placeholder, and the loader writes the schema for placeholders. Any other argument becomes the query string on a GET or DELETE, and the JSON body on a POST, PUT or PATCH.

A placeholder puts the secret where it belongs

No list of auth methods exists. You write {secret:KEY} at the place in the request where the value belongs, and routd renders it on each call into one of three sinks:

sinkexample
headers{ Authorization = "Bearer {secret:CF_API_TOKEN}" }
query{ ApiKey = "{secret:NAMECHEAP_KEY}" }
body{ apikey = "{secret:PB_API_KEY}", secretapikey = "{secret:PB_SECRET}" }

“Bearer” is a literal prefix that you typed, not a mode that the loader knows.

Three things happen to the value on every call:

The key is a row in the secrets table of routd, set with arizuko secret or at /dash/me/secrets. It is not in the daemon environment. One name can exist in both places with different values. GH_TOKEN in the instance .env belongs to a daemon process, and GH_TOKEN in the secrets of a folder is what the connector sends. The connector reads only the row of the folder, through the broker, so a token in .env says nothing about what the connector has.

The grant decides which folder sees the tool

Each tool carries a scope in the form ext:<service>:<operation>. Grants filter registration as for every other MCP tool: the tool joins tools/list only where Authorize(folder, scope) passes. A folder without the grant gets no refusal. It does not see the tool. The check at call time runs anyway, as a second defence.

A grant per folder decides which folder gets which tool:

arizuko group -i <instance> grant folder:main/infra 'main/infra/**' 'ext:cloudflare:dns:read'

Four DNS registrars ship as descriptors

arizuko embeds descriptors in routd/extproviders/. A descriptor does nothing until the operator sets the token of that provider.

providertools
Cloudflaredns_list, dns_create, dns_delete, zone_list
Porkbundns_list, dns_create, dns_delete
Gandidns_list, dns_set
Namecheapdns_list

Connectors have three limits

Egress rules of the guest do not apply
The call leaves from routd on the host, not from the network of the agent. Use the grant, not the allowlist, to decide what a folder can reach.
The agent gets only the response body
No status line and no headers reach the agent. If the agent must branch on a status code, a connector is the wrong tool.
An mcp connector trusts the tool list of the far side
It discovers tools at connect time. If the server adds a tool, your agent gets that tool on the next connect. The grant still applies, but nothing you wrote lists the new tool.

Four pages continue from here

how-to / add a connector adds one step by step. reference / mcp describes the descriptor field by field and every built-in tool. grants explains how the scope is checked. secrets explains where the value is stored and who can read it.