catalog.py
Web serves the directory and the
connect routes, LLM decides whose tools the loop may offer.
Three kinds of grant
- OAuth2
- Key
- Endpoint
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.For an MCP server that registers clients itself, pass For an app you register, the redirect URI is
mcp= in place of the
endpoints and client. The endpoints are discovered and a client is registered
once per organization, so there is no app to register and no secret to keep.https://my-agent.cycls.ai/connectors/{name}/callback. When the deployment sets
CYCLS_RELAY_URL, every OAuth
connector uses that one shared redirect instead. Pass relay=False to keep a
connector on its own callback.Where a grant lives
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 anapi base and no MCP server, 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. A call with a body is sent as
content-type: application/json.
cycls.connector(name) inside a generated app, so a
dashboard an agent builds can read live data without ever holding a key.
Microsoft 365
Microsoft 365 works over the Graph REST API, with one connector per product so each consent screen asks only for what that product needs. Register a multitenant app in Microsoft Entra, add each connector’s callback as a web redirect URI, and addMICROSOFT_CLIENT_ID and MICROSOFT_CLIENT_SECRET to the container’s
environment.
organizations endpoints keep out personal Outlook.com accounts, and the
Prefer header returns mail bodies as text instead of HTML. The other products
use the same endpoints, client, api and scope with their own scopes:
ChannelMessage.Read.All always needs an admin’s consent.
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 callsfind_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.
Each person picks Auto or Manual in Settings, General, Approvals, the row below
Connectors. It appears only when the deployment declares connectors. Auto is the
default, the choice is kept in the browser, and Manual is sent with each request
as
auto: false, which your code reads as context.auto.
When a call needs approval, the model stops and a card asks above the message
box, where the agent’s other questions appear. It asks about the action in plain
words and shows the connector’s logo, or a trash can for a delete. Approving binds
to those exact arguments, so a changed call asks again. Not now is sent to the
model too. The conversation keeps one line where the call was: waiting, then how
it went (approved, done, failed, declined, or skipped when the person wrote
something else instead).
Each person can also set allow, ask or never per tool, including an API
connector’s single request tool, and an organization admin can switch a connector
off for everyone. Don’t ask again on a card sets that tool to allow. A tool set
to allow still asks before a destructive call.
A connector’s CMS record words its calls with actions. Each entry matches a tool
by name, or an API connector’s call by METHOD path with * for one segment, and
gives the card’s question, a line under it, the button and the receipt, in English
and Arabic. show lists arguments to show as rows, read from the call or from its
JSON body, and an id puts one in the sentences as {id}. A call no entry matches
gets a plain question with the agent’s own note on the call.
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. In code, these connector arguments set it:title and description also describe the connector to the model. To read copy
from a CMS instead, point Web at it:
Next
Sandbox and trust model
What isolates the bash tool, and what it does not cover.