Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins/brains/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "brains",
"description": "Your memory layer: Gmail, Calendar, Drive, and prior Claude conversations as queryable pages, with reflexive recall, hook-driven turn-by-turn capture and inbox delivery, and boards/automations/workflows on top.",
"version": "2.8.2",
"version": "2.8.3",
"author": {
"name": "brains (ssvlabs)"
},
Expand Down
2 changes: 1 addition & 1 deletion plugins/brains/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "brains",
"version": "2.8.2",
"version": "2.8.3",
"description": "Your personal memory layer for Codex: query Gmail, Calendar, Drive, and prior conversations, then build boards, automations, and workflows.",
"author": {
"name": "brains (ssvlabs)",
Expand Down
168 changes: 132 additions & 36 deletions plugins/brains/skills/brains-integrations/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,153 @@
---
name: brains-integrations
description: Managing which integrations the user has — listing what's connected (Gmail, Calendar, Drive, Monday, GitHub, …), and installing / upgrading / uninstalling them via codex recipes and the starter-pack bundle. Use when the user asks "what's connected", "can brains read from X", or wants to set up / add / update / remove an integration.
description: Managing which integrations the user has — listing what's connected (Gmail, Calendar, Drive, Monday, GitHub, …), and installing / upgrading / uninstalling them via codex recipes and the starter-pack bundle. Also covers authoring — publishing your own recipe to the catalog and managing it as a publisher. Use when the user asks "what's connected", "can brains read from X", wants to set up / add / update / remove an integration, or wants to publish a recipe of their own.
---

# Integration lifecycle

## Know what's connected (do this before suggesting a source)

`list_integrations` once per session (cache it). One row per known integration:
`name`, `label`, `connected`, `supports.{fetch,act,adapter}`, `description`. Lean
on it before proposing `fetch_from_integration` / `act_on_integration` so you
don't name a source the user hasn't wired up — the `source` enums are filtered
per-user too. It's also the canonical answer to "what integrations do I have."
`list_integrations` returns one row per known integration: `name`, `label`,
`connected`, `supports.{fetch,act,adapter}`, `description`. Caching it for the
session is fine, but re-read it after you install / pause / resume / uninstall
anything and after a source call fails on connection state — it's a live read,
and the cache goes stale exactly when it matters. Read it before proposing
`fetch_from_integration` / `act_on_integration`: those tools advertise a
**static** `source` enum listing every registered source whether or not this
user connected it, so presence in the enum proves nothing. No failure comes
either: an unconnected source returns a success-shaped fetch with
`ingested_count: 0` and a `note`, or an action with `kind: "noop"` and a
`reason`. Read those before treating empty as "nothing there" — then re-read
`list_integrations`, get the user to connect or resume at `/integrations`, and
retry. `list_integrations` is the per-user answer, and the canonical one to
"what integrations do I have."

## Install / re-install
## Browse the catalog

`list_recipes` enumerates what's installable. `tier` is `'catalog'` (the
default — the public catalog: platform-curated rows plus recipes other users
published publicly, so don't present a row as vetted; `owner_user_id: null` is
what marks a curated one), `'yours'` (the caller's own), or `'community'`
(other users' published ones); `kind` / `category` / `q` narrow it. Each row
carries the slug's current `version`. Use it to find slugs the user doesn't
already know rather than sending them to a browser. `recommend_recipes` picks a
few with an evidenced reason when they have no specific target in mind.
`/recipes` in the web UI is the same catalog with pictures.

You can install a single integration **from here over MCP** — no need to
bounce the user to the browser for the common case:
## Install / re-install

- **MCP (preferred for one integration):** `install_integration slug=<recipe>`.
api_key / none-auth recipes install immediately; oauth2 recipes return an
`authorize_url` the user opens in a browser to finish the handshake. Common
- **One integration:** `install_integration slug=<recipe>`. none-auth installs
immediately; api_key installs immediately when you pass `api_key`, and
otherwise creates **no** install — it returns a `configure_url` for the user
to enter the key on the web (never collect a key through chat); oauth2 and
composio recipes return an `authorize_url` to open in a browser. Common
slugs: `gmail-inbox`, `gcalendar`, `gdrive-files`, `github-issues`,
`monday-items`. Idempotent — re-calling for an already-installed recipe
returns the existing install.
- **Web UI — browse catalog:** for "what's available?" send the user to
`/recipes` (integrations, boards, automations, workflows, bundles). There's
no MCP tool to enumerate the catalog, so the web UI is the way to discover
slugs the user doesn't already know.
- **Web UI — starter pack:** for "set me up" / "install the basics", send the
user to `/recipes/starter-pack` — one click bootstraps the three Google
integrations (gmail-inbox, gcalendar, gdrive-files) atomically. Bundle
cascades only run through the web UI.
`monday-items`. Idempotent — an already-installed recipe returns the existing
install, and an oauth2 one still awaiting its handshake hands back the
`authorize_url` again. A composio recipe does **not** replay that link: a
pending re-call just reports "already installed", so send the user to
`/integrations` to finish connecting.
- **Anything else, bundles included:** `install_recipe slug=<slug>
brain_id=<uuid>` — boards, automations, workflows, integrations and bundles,
missing dependencies auto-installed children-first, never duplicating rows on
a re-run. Keep installs one-at-a-time — the dedup is a check-then-insert, so
two overlapping calls can both insert; never retry while a previous call may
still be running. `slug="starter-pack"` is the full bootstrap: two boards,
seven automations, and the three Google integrations. Its oauth2 children land
unauthorized and this call returns no link for them, so follow up with
`install_integration` per oauth2 child to collect each `authorize_url` — or
send the user to `/recipes/starter-pack` and let the web UI walk them
through it.

## Upgrade

Upgrades run through the web UI (there is no MCP upgrade tool). Each
integration page (`/integrations/<install_id>`) shows a "Newer version
available" banner when an upgrade exists. If the install came via the
starter-pack bundle (`source_bundle_slug` is set), the Upgrade button
redirects to upgrading the **bundle** — don't upgrade bundle-sourced
integrations in isolation.
`list_installed_versions` returns one row per install — `{slug, name, kind,
version, state}`, `kind` being `automation`, `bundle` or `integration` (narrow
with `kinds`; boards and workflows never appear). Compare `version` against the
matching `list_recipes` row to spot drift, but treat it as a provenance stamp,
not proof of what's running: a re-apply advances it without necessarily
bringing the content along, so never conclude "already current" from equality
alone.

The upgrade itself runs through the web UI; there is no MCP upgrade tool, and
`install_recipe` is not a stand-in. A re-apply reuses the existing entity — it
restamps provenance, writes the automation's state (reviving an uninstalled
one, otherwise matching the template's paused/active), and overwrites an
un-customized board dashboard — but leaves the board's schema, rows,
description and recipe-shipped skills, the automation's intent, model, triggers
and grants, and a workflow's contents entirely, on the old version.

Where to send the user depends on the row's `kind`:

- `integration` → its install page, `/integrations/<install_id>`, whose upgrade
banner is the authoritative check for this kind.
- `automation` → the recipe's own page, `/recipes/<slug>`, which carries a
per-entity upgrade banner for a standalone install. Same page for a board or
workflow you're checking by slug.
- `bundle` → `/recipes/<bundle-slug>`, which fans the upgrade across every
child.

Children of a bundle deliberately show no upgrade of their own. For a
bundle-sourced integration the install page is authoritative: follow the bundle
link when it renders one, and take the standalone "Upgrade to vN" when it
renders that instead — it means the bundle path is a no-op while the leaf lags.
Never bypass the page by hand-reinstalling.

## Uninstall / manage

Over MCP: `uninstall_integration install_id=<uuid>` (revokes + wipes the
user's secrets for that install), `pause_integration` / `resume_integration`
(stop / restart cron firings), `list_my_integrations` (the user's codex
installs with state + cost), and `get_integration_status` (one install's
health). Get the `install_id` from `list_my_integrations`.

The web UI also exposes Uninstall on each entity page with a 7-day recovery
window; from `/recipes/<slug>` it cascades to every entity whose
`source_recipe_slug` matches (and for a bundle, every child). Use
`list_uninstalled` when the user asks "what did I just uninstall" before they
click Recover.
installs with slug, name, version and state), and `get_integration_status`
(one install's state, config overrides, and a sanitized `last_error`). Get the
`install_id` from `list_my_integrations`.

Tune an install instead of reinstalling it: `set_integration_config`
(installer-side config, e.g. which repos github-issues ingests),
`set_integration_overrides` (per-run / per-month limits — you can only lower
the recipe's caps, higher values are clamped down silently), and
`run_integration_once` for an on-demand test run instead of waiting for cron
(the install must be `active`).

The whole-recipe cascade is callable over MCP too: `preview_uninstall_recipe
slug=<slug> brain_id=<uuid>` shows the blast radius — entities stamped with
the slug, plus deps that would be orphaned — and mutates nothing, then
`uninstall_recipe` does it in one transaction, or takes `entity_id` instead to
drop a single entity. Always preview first for a bundle. Uninstalled boards,
automations and workflows sit in a 7-day recovery window; `list_uninstalled`
answers "what did I just uninstall" before they click Recover. Integration
installs are **not** in that window — uninstall revokes them outright, and
recovering one means installing it again.

## Authoring a recipe

Publishing is blocked on the autonomous surfaces (the web agent and Telegram),
so if the user wants to publish something of their own, here is where it
happens.

- **Start with the playbook, not a blank template.** To author an integration
recipe, call **`create_integration_flow`** first — it returns the full
IntegrationTemplate shape (auth, trigger, source, outputs, allowed_origins,
tool_grants, …) as a playbook to follow. Do NOT hand-roll a template or guess
at its fields; the spec lives in the playbook, not here.
- **Publish:** `publish_recipe` (kind-agnostic — board / automation / workflow /
bundle / integration) or `publish_integration_recipe` (integration-specific).
Both validate the template server-side and write nothing if it's invalid.
`unpublish_recipe` / `unpublish_integration_recipe` pull a recipe from the
catalog; existing installs are untouched — uninstall those separately.
- **Versioning is append-only.** A brand-new slug publishes at v1; re-publishing
a slug you own inserts `max(version)+1`, the old version stays as a
historical row, and existing installs keep their pinned version. There is no
in-place edit — fix a mistake by publishing the next version. You cannot
publish under a slug someone else owns.
- **`private` defaults to TRUE** for a new slug: visible and installable only by
the author until it's published with `private: false`. Say this out loud
after publishing, or the user will wonder why nobody can see it. A version
bump inherits current visibility unless `private` is passed.
- **Manage what you publish:** `list_publisher_recipes` (your integration
recipes + install counts by state), `get_publisher_recipe` (one recipe's
detail — auth, limits, recent install errors, publisher-secret *names*), the
publisher vault via `list_publisher_secrets` / `rotate_publisher_secret` /
`delete_publisher_secret`, and `bulk_set_integration_state` to move every
connected (non-revoked, non-pending) install of a recipe you own to active /
paused / stopped.
Loading