Brian External Apps
Call Notion, Slack, Xero, GitHub, and 3000+ apps through Brian's gateway. One MCP, every integration.
Description
Prerequisites
- Brian connected
- At least one Pipedream app connected
Install in Claude
Download
Sign in, then click Download. You'll get a zip with the skill folder inside.
Upload to Claude
In Claude, go to Settings → Capabilities → Skills → Add custom skill. Upload the zip you just downloaded.
Test it
Try a prompt that should trigger the skill — Brian's docs and the description above show what activates Brian External Apps.
Skill content
name: brian-external-apps description: How to call third-party apps (Notion, Slack, Xero, GitHub, 3000+ others) through Brian. Brian is the gateway — there is no separate MCP per app. Always look up the canonical app_slug before calling, never guess.
brian-external-apps
How to call third-party apps through Brian.
Mental model
Brian proxies every third-party API through Pipedream Connect. Notion, Slack, Xero, Gmail, GitHub, Stripe, OpenAI — all 3000+ apps go through Brian. There is no separate Notion MCP, Slack MCP, etc. If the user mentions a third-party service, the call goes through Brian.
Two execution paths:
run_external_action— Pipedream prebuilt component. Captures sensible defaults and prop normalisation. Prefer this when a prebuilt fits.forward_external_request— raw HTTP. Universal fallback. Use when no prebuilt fits, when the operation is straightforward, or when the prebuilt's prop schema is fighting you.
The canonical flow
Follow this order. Skipping a step is how slug-guessing bugs creep in.
1. list_connected_apps first
Call it before any external work. Returns the apps this user has actually connected, with the canonical app_slug for each. Use that slug verbatim. Don't normalise it, don't strip suffixes, don't substitute a friendlier name.
Pipedream's slugs are sometimes suffixed to disambiguate API versions or auth flavours — xero_accounting_api, airtable_oauth, calendly_v2, salesforce_rest_api. The webhook captures whatever Pipedream gives at CONNECTION_SUCCESS and that IS the slug. Anything you guess is wrong.
2. If the app isn't connected
Call list_pipedream_apps(query) to find the canonical slug for the user's intended app, then point them at the Brian web UI to connect it (they can browse what's available there). Do not attempt the call before connection — Pipedream's proxy needs the OAuth token, which only exists after connect.
3. Discover prebuilts
Prefer fetch_app_soul(app_slug) — Brian auto-derives a soul for every connected app at CONNECTION_SUCCESS. The soul carries top_actions (10 prebuilts with descriptions and a has_reload_props flag), universal_fallback_action (the <slug>-make-an-api-call key when present), dynamic_domain, and auth_type. One round-trip, no walking the catalogue.
Fall back to list_external_actions(app_slug) only when fetch_app_soul returns error_code: "not_found" (typical for the first ~5s after a fresh connect — derivation is fire-and-forget) or when you need an action outside the top 10 (pass query to narrow). Many actions have explicit MCP/AI guidance baked into their descriptions — read them.
4. Choose the path
- Prebuilt fits cleanly →
run_external_action(app_slug, action_id, configured_props). Brian injects the user's auth automatically. - No prebuilt, or the prebuilt is awkward →
forward_external_request(app_slug, method, url, headers?, body?). Use the upstream API's REST shape directly. - Universal fallback for unusual operations → check the soul's
universal_fallback_actionfield. Many apps expose<slug>-make-an-api-call(e.g.xero_accounting_api-make-an-api-call) — a prebuilt that wraps a raw call but keeps Brian's auth injection. Useful when the operation isn't in the top prebuilts.
Gotchas
Prebuilt prop schemas ≠ raw REST schemas
Pipedream components normalise some props. Notion's notion-search takes filter: "page" (string), not Notion's REST {value: "page", property: "object"} shape. If you get a validation error from run_external_action, read logs[].err.body — Pipedream surfaces the upstream message verbatim. The prop name in the error tells you what the component expected.
Two-step prop discovery (reloadProps)
Some actions have dynamic props that load only after another prop is set. Example: HubSpot's create-engagement requires engagementType first; the rest of the schema appears after. This is awkward via MCP today. If you hit a reloadProps: true action and don't have the second-step props, prefer forward_external_request against the upstream REST API.
Dynamic-domain apps (Xero, Zendesk, Zoho, Shopify)
The tenant URL is resolved at runtime. Use a relative path, not the full domain — forward_external_request({ app_slug: "xero_accounting_api", method: "GET", url: "/api.xro/2.0/Invoices" }). list_connected_apps reports dynamic_domain: true for these apps so you know upfront.
Don't fabricate
If a call fails because the app isn't connected, say so and offer the connect path. Do not produce a fake response, draft a markdown artefact, or "simulate" the API call. The user expects the real thing.
Reading errors
run_external_action returns { summary, result, logs }. On failure, summary and result are null, and logs[] carries the upstream error chain — usually with logs[0].err.body containing the real API response. Don't paraphrase; show the upstream message so the user can act on it.
forward_external_request returns the upstream response verbatim — { status, headers, body }. Parse the body as you would any REST response.
Example: send a Slack message
list_connected_apps→ confirmsslackis connected.list_external_actions(app_slug: "slack", query: "send message")→ returnsslack-send-message-to-channel.run_external_action(app_slug: "slack", action_id: "slack-send-message-to-channel", configured_props: { channel: "#general", text: "Hello" }).
Example: a Xero operation with no obvious prebuilt
list_connected_apps→ returnsapp_slug: "xero_accounting_api",dynamic_domain: true.list_external_actions("xero_accounting_api", query: "credit note")→ no exact match.- Fall back:
run_external_action("xero_accounting_api", "xero_accounting_api-make-an-api-call", { method: "POST", path: "/api.xro/2.0/CreditNotes", body: {...} })— universal fallback action — ORforward_external_request("xero_accounting_api", "POST", "/api.xro/2.0/CreditNotes", {...}).
Release notes
Initial drop.
