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
defineApp does not register an org catalog app. Without apps create, the service will not show up in the Connect dialog, and keystroke 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
- Search the catalog — confirm the app is missing (
keystroke apps search). - Register the app —
keystroke apps createso it is connectable in the org. - Connect — user enters the secret in the web Apps flow (
keystroke connect <slug>or the MCPconnect_applink), orkeystroke credentials createfor a static key you already have in the shell. - Author in code —
defineApp+app.action(...)(orkeystroke apps sync <slug>to scaffold the app module from the platform template). - Deploy — ship actions/agents/workflows that use those tools.
--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 Apps 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, apps sync, and defineApp({ slug }). After create, prefer:
src/apps/<name>/app.ts with the correct slug and credential fields.
keystroke apps get <org>/<name> returns { kind: "custom", app: { … } } with the credential template. Built-in catalog apps return { kind: "catalog", app: { package, … } } instead.
apps 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 apps 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
keystroke apps execute runs built-in catalog toolkit actions and custom MCP remote tools. It cannot invoke defineApp(...).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:
- Author the action with
defineApp(...).action(...)(orapps sync+ fill in the handler). - Connect the app credential (
keystroke connect <org>/<name>). - Add a thin workflow that calls that one action.
- Deploy, then run it for real:
app.action(...) and attach them (or the whole app) to an agent.
To remove a custom org app (and all of its credentials), use:
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 withapp.action().
src/apps/acme/app.ts
src/actions/create-acme-ticket.ts
slug must match the catalog/credential key from apps 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.
After the app is registered and connected:
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 withapps 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.
apps 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 apps execute to smoke-test a remote tool, then wrap what you need. If nothing is connected yet, the CLI tells you to connect first.
defineMcp from @keystrokehq/keystroke/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
keystroke.config.ts has an integrations option, but custom HTTP integration mounting is not a shipped user-facing extension point yet. Today, use:
defineApp()+app.action()for credentialed custom HTTP APIs (default).keystroke apps createwhen the app must appear in Connect / the org catalog.keystroke apps delete <org>/<name>to remove a custom org catalog app (and its credentials).- Plain
defineAction()for project actions that need no credentials. keystroke apps create --mcp+ connect +apps actions list+apps executefor custom MCP servers.
Search Keystroke docs (
keystroke 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.