Skip to main content
Use a custom app when the built-in catalog does not cover the service you need, or when you’re connecting to an internal API. Search the live catalog first:
Before writing code, read the service’s current API or MCP documentation. Prefer an authoritative llms.txt, OpenAPI or GraphQL schema, or API reference over inferred endpoints and payloads.

Two layers: catalog + code

A credentialed custom integration has two parts. Do not skip either when the user should connect the app in the product UI. Code-only defineIntegration does not register an org catalog app. Without integrations create, the service will not show up in the Connect dialog, and luno connect <slug> will not work for it. Actions that need no credentials stay as plain defineAction in src/actions/ — no app required.

End-to-end happy path

  1. Search the catalog — confirm the app is missing (luno integrations search).
  2. Register the app — luno integrations create so it is connectable in the org.
  3. Connect — user enters the secret in the web Integrations flow (luno connect <slug> or the MCP connect_app link), or luno credentials create for a static key you already have in the shell.
  4. Author in code — defineIntegration + app.action(...) (or luno integrations sync <slug> to scaffold the app module from the platform template).
  5. Deploy — ship actions/agents/workflows that use those tools.
When a public OpenAPI, GraphQL, or MCP URL is available, prefer URL-based create so auth and fields are detected:
Remove --preview after reviewing the assembled request. Use only the flag matching the source. For a manual API-key app:
--description defaults to --name when omitted. At least one --field is required. Optional --logo is a public image URL shown in the Integrations catalog and Connect UI. In the web dashboard, click the logo placeholder on create to upload an image or paste a URL. Custom org apps are registered as {organization}/{name} (for example wells/demo-echo), not a bare demo-echo slug. Use that full id for connect, credentials create, integrations sync, and defineIntegration({ slug }). After create, prefer:
That writes src/integrations/<name>/integration.ts with the correct slug and credential fields. luno integrations get <org>/<name> returns { kind: "custom", app: { … } } with the credential template. Built-in catalog apps return { kind: "catalog", app: { package, … } } instead. integrations actions list means different things by app kind: For a custom MCP, the list is an inspect step: every tool the server advertises, whether or not you have wrapped it in app.action(...). Smoke-test a remote tool with integrations execute, then author the TypeScript wrappers you need. This split will improve later; today treat MCP list as remote discovery, not a project action inventory. See the CLI apps reference.

Smoke-testing custom app actions

luno integrations execute runs built-in catalog toolkit actions and custom MCP remote tools. It cannot invoke defineIntegration(...).action(...) code in your project for non-MCP custom apps. To smoke-test a custom (non-MCP) app action before wiring it into larger agents/workflows:
  1. Author the action with defineIntegration(...).action(...) (or integrations sync + fill in the handler).
  2. Connect the app credential (luno connect <org>/<name>).
  3. Add a thin workflow that calls that one action.
  4. Deploy, then run it for real:
For MCP custom apps, connect, list tools, then smoke-test a remote tool with the same command family as catalog apps:
Then wrap the tools you need in app.action(...) and attach them (or the whole app) to an agent. To remove a custom org app (and all of its credentials), use:
Official catalog apps cannot be deleted. In the web dashboard, open Connect an app, select the custom app, and click Delete.

Example requests

Ask your coding agent which API or MCP server you want to reach. It should register a connectable app when needed, define the app in code, write actions, and attach them.
“Connect to our internal billing API and add actions to look up and refund an invoice.”
“Attach the DeepWiki MCP server to the research agent so it can answer questions about any GitHub repo.”

Define a custom app (required for credentialed integrations)

For any new HTTP integration that needs a secret or connected account, create an app wrapper and define actions from it. Credentials belong on the app; actions are created with app.action().
src/integrations/acme/integration.ts
src/actions/create-acme-ticket.ts
The app slug must match the catalog/credential key from integrations create (the full {org}/{name} id). Official catalog apps can still support self-hosted instances when their credential shape includes a base URL or similar field.
Anti-pattern: do not create src/credentials/foo.ts plus a lone defineAction for a new API. Create src/integrations/foo/integration.ts with defineIntegration and app.action(...) instead.
After the app is registered and connected:
See using credentials in code for scope resolution.

When standalone defineCredential is appropriate

Reserve ad-hoc defineCredential() for rare cases — for example a shared secret used by unrelated actions that are not one service, or an MCP auth helper. It is not the default path for a new integration.

Connect an MCP server

Register the MCP as an org app, connect auth, then inspect live tools, smoke-test with integrations execute, author app.action wrappers, and attach the app (or individual actions) to the agent. Credentials stay on the platform; code mirrors the app slug. You cannot list MCP tools until the app is connected (OAuth or API key). NO_AUTH servers are the only exception.
integrations actions list on a custom MCP uses the connected credential to call the remote tools/list. That output is every tool the server exposes right now — it is not the same as listing official catalog actions, and those tools may not exist as TypeScript app.action(...) functions yet. Use integrations execute to smoke-test a remote tool, then wrap what you need. If nothing is connected yet, the CLI tells you to connect first.
A deployed run resolves the project’s credential for the app (project, then organization). A failing tool throws with the tool’s error text.

Who can execute a tool

Running a tool has real effects (it sends the email, writes the CRM record), so seeing a credential is not enough to use it. The rule depends on who the credential belongs to: Runs nobody started by hand (cron, webhooks, polls) have no acting user. They use shared credentials, and a personal credential only when its owner assigned it in the project (luno credentials assignments assign …). In a run started by a person, only that person’s own personal credentials execute. Refusals answer 403 with a code you can branch on: personal_credential_not_owned (someone else’s personal credential) or run_permission_required (shared credential, read-only access). From the CLI or API without --project, a shared credential needs project:run organization-wide (org owner or admin, or a permission group); pass --project <slug> to use your role in that Space. Listing tools (integrations actions list) still only needs to see the credential. Attach tools either way:
Assign the app credential to the agent, then deploy. Do not import defineMcp from @meetluno/luno/agent (it is not a supported authoring API). OAuth MCP apps expose accessToken for use as Authorization: Bearer against the remote MCP URL.

Current limitations

luno.config.ts has an integrations option, but custom HTTP integration mounting is not a shipped user-facing extension point yet. Today, use:
  • defineIntegration() + app.action() for credentialed custom HTTP APIs (default).
  • luno integrations create when the app must appear in Connect / the org catalog.
  • luno integrations delete <org>/<name> to remove a custom org catalog app (and its credentials).
  • Plain defineAction() for project actions that need no credentials.
  • luno integrations create --mcp + connect + integrations actions list + integrations execute for custom MCP servers.
Search Luno OS docs (luno docs search) before inventing MCP integration patterns. Live tool lists fill gaps when vendor docs are thin.

Next steps

Using credentials in code

Understand credential declaration, scopes, and defaults.

Actions

Build actions that consume your app credential.

Build agents

Attach actions as agent tools.

Connect and manage apps

Store the API key or OAuth credential your code will resolve.