Use Kortix connectors to reach external systems from a session. Use the `kortix connectors` CLI for agent work, `@kortix/sdk` for durable TypeScript workflows, and `kortix connectors mcp` when a stdio MCP server is required. Load this skill to inspect, add, connect, or call external tools without exposing third-party credentials to the sandbox.
Installs into .claude/skills of the current project.
Are you the author of Kortix Connectors?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/kortix-ai-kortix-connectors)
---
name: kortix-connectors
description: Use Kortix connectors to reach external systems from a session. Use the `kortix connectors` CLI for agent work, `@kortix/sdk` for durable TypeScript workflows, and `kortix connectors mcp` when a stdio MCP server is required. Load this skill to inspect, add, connect, or call external tools without exposing third-party credentials to the sandbox.
---
<skill name="kortix-connectors">
<overview>
A **connector** defines tools against an external system. A connector is
**not** an account: one connector (e.g. Gmail) can hold several **accounts** —
each one SHARED with the whole project or PRIVATE to one member. A
**connector call** invokes one tool AS one account.
Use the **`kortix connectors` CLI** for normal agent work:
- `kortix connectors ls` lists connectors and actions (an `ACCOUNTS` column
shows how many each holds).
- `kortix connectors discover "<intent>"` searches visible actions.
- `kortix connectors show <connector>.<action>` shows one input schema and risk.
- `kortix connectors accounts <slug>` lists the accounts a connector holds,
default first — see **Choosing the account** below before your first call
on a connector you have not used yet.
- `kortix connectors call <connector> <action> '<json>' [--account <label>]`
invokes one action. Every successful result echoes `account`: say which one
ran when it matters.
- `kortix connectors call … --reason "<what this does>"` tells the human what
the call does if a policy holds it for approval. The approver sees it next
to the real arguments. **Always pass it on a write whose args are only ids**
— `send_draft` (say who it goes to, the subject, and the body), a delete (say
what gets deleted), a merge. Without it the approver sees only `draft_id`.
A gated call returns `approval_url`: share it, then stop the turn. The human's
decision, and any message they add to it, arrives as your next prompt — a
deny with a message is an instruction, not a dead end. In a Slack or Teams
session Kortix posts an approval card in the thread itself
(`approval_instructions` says so): do not repost the link, just stop.
- `kortix connectors call … --attach <file>` attaches a file (see **Attach
files** below). Never put base64 in args.
- `kortix connectors call … --out <file>` writes the full JSON result to
`<file>` and prints only `saved_to`, `bytes`, and `shape` (keys, array
lengths, `pageInfo`). Use it for list and search calls; tool output above
~50 KB is truncated. Then query the file with `jq` or `bun`, never `cat`.
The `kortix-connectors_call` MCP tool does this by itself above 16 KB: it
returns `{ saved_to, bytes, shape, preview }`.
- `kortix connectors add`, `rm`, and `connect` manage connectors and connections.
- `kortix connectors mcp` runs the optional `kortix-connectors` stdio MCP server.
Durable TypeScript workflows use **`@kortix/sdk`** and `createKortix`. Every
call runs through the connector gateway. The
gateway resolves credentials, enforces access and policy, invokes the upstream
system, and records an audit event. The sandbox carries `KORTIX_TOKEN`; it
does not carry raw third-party credentials.
</overview>
<when-to-load>
Load this skill when the user wants to:
- Act in an external app or API.
- Inspect available connectors, actions, or connected computers.
- Add or configure a connector or connection.
- Request connector credentials without exposing the value.
- Build a repeatable workflow that calls external systems.
Do not load it for work that stays inside the local repository or sandbox.
</when-to-load>
<choosing-the-account>
**A connector is not an account.** One connector (e.g. Gmail) can hold many
accounts — shared with the whole project, or private to one member. Never
assume "one connector, one account", and never infer which accounts exist
from a `get_profile`/`whoami`-style call: that only ever answers for the ONE
account it happened to run as. If a human asks which or how many accounts are
connected, list them — do not guess from a profile call.
Procedure, every time you call a connector you have not just listed accounts for:
1. **List the accounts:** `kortix connectors accounts <slug>` (default first;
`(pinned default)` marks the one an unnamed call uses).
2. **One account** → just call. Nothing to choose.
3. **Several accounts, and the human named one** → pass
`--account <label|id|me|project>` (`me` = your own private default,
`project` = the shared default).
4. **Several accounts, and it is unclear which one** → ASK the human. Do not
guess, and do not silently use the default — several real accounts exist
and picking wrong sends the action to the wrong mailbox/workspace. If
nothing is named and nothing is pinned, the call is refused anyway with
reason `account_required` rather than guessing; a human can pin one going
forward with `kortix connectors accounts <slug> --default <label>`.
Always report which account ran when it could matter — read it off the
result's `account` field, never assume.
**Worked example.** A project has one Gmail connector (`gmail-ffiod0`) with
two accounts:
```sh
$ kortix connectors accounts gmail-ffiod0
LABEL OWNER DEFAULT CONNECTION ID
personal@example.com private no 11111111-…
work@example.com private no 22222222-…
kortix connectors call gmail-ffiod0 <action> --account "personal@example.com"
kortix connectors call gmail-ffiod0 <action> --account "work@example.com"
kortix connectors call gmail-ffiod0 <action> --account me
kortix connectors call gmail-ffiod0 <action> --account project
2 accounts
No default pinned — unnamed calls will be refused with account_required;
pass --account or pin one: kortix connectors accounts gmail-ffiod0 --default <label>
```
Neither account is pinned, so — asked "check my gmail" with no account named —
the right move is to ASK which mailbox, not to call `get_profile` on whichever
account resolves first and report "one account connected". If the human says
"the work one", call with `--account "work@example.com"` and report: "Checked
work@example.com — …".
**Adding another account.** When the human wants a new one ("connect my other
Gmail"), mint a link with the MCP `connect` tool and a `label` that tells it
apart (`connect({ slug, label: "Personal Gmail" })`). The link opens a dialog
where the human names the account and chooses who can use it; you are then told
its name. Call it with `--account "<name>"` from then on. `kortix connectors
connect` from a shell cannot name a new account.
</choosing-the-account>
<cli-first-loop>
Use the CLI first. It is pre-authenticated in a session sandbox.
1. List visible connectors:
```sh
kortix connectors ls
```
2. Search by intent:
```sh
kortix connectors discover "send an email"
```
3. Inspect one action before an unfamiliar call:
```sh
kortix connectors show email_email_inbox_bjgk.reply_message
```
4. Call the action:
```sh
kortix connectors call email_email_inbox_bjgk reply_message \
'{"inbox_id":"email-inbox@agentmail.to","message_id":"<message-id>","text":"Reply text"}'
```
**Attach files.** Write the file under `/workspace/artifacts` (or
`output`, `reports`, `deliverables`), put the JSON args in a file, and pass
`--attach`. The CLI stages the raw bytes and the gateway builds the
provider's attachment item (Microsoft Graph, SendGrid, Postmark, Mailjet,
Resend, Brevo, and the Email channel):
```sh
cat > /tmp/mail.json <<'JSON'
{"user":"sender@example.com","body":{"message":{"subject":"Report",
"body":{"contentType":"Text","content":"Attached."},
"toRecipients":[{"emailAddress":{"address":"to@example.com"}}]},
"saveToSentItems":true}}
JSON
kortix connectors call microsoft-graph sendmail @/tmp/mail.json \
--attach /workspace/artifacts/report.pdf
```
Leave the attachments array out of the JSON; `--attach` appends to it. For a
field the CLI cannot find, run `kortix connectors upload <file> --connector
<slug>` and put the printed `ref` (`{"$kortix_attachment":"<id>"}`) where the
file belongs: in an `attachments` array, or as the value of a base64 field
such as `contentBytes`. Pass the body as a JSON object, never as a string.
For GraphQL actions, put selected fields in `args.__select`:
```sh
kortix connectors call internal_graph query.user \
'{"id":"1","__select":"id name email"}'
```
</cli-first-loop>
<sdk-workflows>
Use `@kortix/sdk` for dependent calls, pagination, branching, retries, or
reusable scripts. Read `references/sdk.md` for the full pattern.
```ts
import { createKortix } from '@kortix/sdk';
const kortix = createKortix({
backendUrl: process.env.KORTIX_API_URL!,
getToken: async () => process.env.KORTIX_TOKEN ?? null,
});
const connectors = process.env.KORTIX_PROJECT_ID
? kortix.project(process.env.KORTIX_PROJECT_ID).connectors
: kortix.connectors;
const matches = await connectors.search('send an email', { limit: 5 });
const action = await connectors.describe(matches[0]!.tool);
if (!action) throw new Error('Email action not found');
const result = await connectors.call('email_email_inbox_bjgk.reply_message', {
inbox_id: 'email-inbox@agentmail.to',
message_id: '<message-id>',
text: 'Reply text',
});
if (!result.ok) {
throw new Error(`Connector call failed: ${result.reason ?? result.status ?? 'unknown'}`);
}
```
Run repository scripts with `bun run path/to/script.ts`. Keep provider
credentials out of code and repository files.
</sdk-workflows>
<adding-connectors>
Connector definitions live in `kortix.yaml`. Connections remain server-side.
```yaml
connectors:
- slug: stripe
name: Stripe API
provider: openapi
spec: https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json
auth:
type: bearer
```
Use `composio` for managed SaaS apps. Other supported direct providers are
`mcp`, `openapi`, `postman`, `graphql`, and `http`. Add and connect a Composio
toolkit with:
```sh
kortix connectors add github --provider composio --app github --apply
kortix connectors connect github
```
Pipedream exists only for rollback compatibility with already-declared
connectors. Never add a new Pipedream connector automatically. If Composio
cannot satisfy the request, stop, explain the gap, and ask the human before any
explicit `--allow-legacy-pipedream` retry.
Surface the returned connection URL. For an API key you already have (the user
gave it in chat), store it now: `kortix secrets set NAME=- --scope connector`.
For one you lack, use `kortix secrets request NAME --scope connector`.
Slack uses the channel flow. Do not add a Slack connector. Run:
```sh
kortix channels connect
```
</adding-connectors>
<rules>
- A connector is not an account. One connector (e.g. Gmail) can hold many
accounts (shared or private) — see **Choosing the account** above.
- If the human asks which or how many accounts are connected, ALWAYS list
them with `kortix connectors accounts <slug>`. Never infer accounts from a
profile/whoami call.
- Use `kortix connectors` for one-off agent actions.
- Use `@kortix/sdk` for durable or testable workflows.
- Use Composio for every new managed SaaS connector. Never select Pipedream
unless the human explicitly approves the legacy rollback path.
- Do not use raw provider tokens from the sandbox.
- Treat `denied`, `not_shared`, `needs_auth`, `account_required`, and
`ok: false` as real outcomes — `account_required` means several accounts
are reachable and none was named or pinned; pass `--account` or ask the
human, do not retry blind.
- Report which account ran when it could matter — read the result's
`account` field, never assume.
- Confirm irreversible work before a destructive connector call.
- The `kortix-connectors` MCP server is optional. Use the CLI if it is absent.
</rules>
</skill>