Skip to main content
A connector is a grant a person makes, for themselves or for a workspace, plus the policy over the tools that use it. You declare it once, the directory renders it, and the credential is attached server-side when a tool runs. Your code never holds a token and neither does the browser.
catalog.py
Declare the same objects on both builders. Web serves the directory and the connect routes, LLM decides whose tools the loop may offer.

Three kinds of grant

Authorization code with PKCE against an app you registered, or against an MCP server that publishes its own authorization server and registers clients dynamically.
Tokens refresh when stale. The state that rides through the provider is signed and names the user and workspace, so the callback carries no JWT.

Where a grant lives

A mailbox is a person, so user. A team analytics key is shared, so workspace. either lets an admin connect one account for everyone while individuals can still link their own.

Calling a REST API

Give a connector an api base and the loop offers one {name}_request tool. The model builds a path, the platform attaches the credential, and the host is bounded to that base.
The same relay backs cycls.connector(name) inside a generated app, so a dashboard an agent builds can read live data without ever holding a key.

MCP servers

cycls.MCP connects to a remote MCP server. The harness speaks Streamable HTTP itself, so every provider gets MCP, not just Anthropic. Tools are discovered once and cached, and a session opens only when a tool is actually called.

Loading tools on demand

A directory of connectors can hold hundreds of tools, and their schemas would not fit in a system prompt. Each connector contributes one index line of about fifteen tokens instead, and the model calls find_tools to load the schemas it needs. Loaded tools stay for the rest of the conversation.

Approvals

Every tool call is classified before it runs. When a call needs approval, the model gets a stop and the user gets a confirm card naming the tool and the arguments. Approving binds to those exact arguments, so a changed call asks again. Each person can also set allow, ask or never per tool, and an organization admin can switch a connector off for everyone. Deletes through bash are not destructive by default, because the sandbox shims rm into a thirty day trash. Commands with nothing behind them, such as shred, mkfs or git push --force, always ask.

Directory copy

What the directory shows can come from your code or from a CMS, field by field, with code winning.
This keeps behavior in Python, where it belongs, and copy in a CMS where a writer can change it without a redeploy.
Connector objects are built on your machine and pickled into the deployment. Keep the declaration file free of functions and module-level values a callable would close over: a writes classifier or a tool handler must live in the file you deploy, not in an imported module.

Next

Sandbox and trust model

What isolates the bash tool, and what it does not cover.