Local MCP (Model Context Protocol) server for the Appo app
platform. It gives an AI agent the same app lifecycle outcomes as the appo CLI
— create, configure, preview, publish, push, rejection recovery, test builds,
resubmission — plus local code-generation dev-tools for the
@appolabs/appo SDK.
The server carries no API client of its own: every lifecycle call reuses the
CLI core (@appolabs/appo ops/api/config). Tool names mirror the canonical
/mcp AppoServer vocabulary documented in
apps-web-app/docs/CROSS-SURFACE-PARITY.md, so an agent sees one vocabulary
across both MCP surfaces.
Add to your .mcp.json:
{
"mcpServers": {
"appo": {
"command": "npx",
"args": ["-y", "@appolabs/appo-mcp"]
}
}
}The server does not prompt for credentials or hold credential state. It resolves auth exactly as the CLI does, so both surfaces always target the same environment:
appo loginonce. After a device-flow login with the CLI, the token is stored in the active profile (~/.appo/config.json). The server reads it through the shared config module — no extra configuration.APPO_TOKENoverride. SetAPPO_TOKENto supply a PAT directly (CI / non-interactive contexts); it takes precedence over the stored profile token.- Base URL / profile.
APPO_API_BASEenv → the active profile'sapi_base→http://localhost:8002. Profile selection:APPO_ENV→ configcurrent→default.
If no token can be resolved, every lifecycle tool returns a structured
{ error: "not_authenticated", message, next_actions: [] } envelope (it never
crashes) — the message tells the operator to run appo login.
Reuse the CLI core; names match the canonical /mcp inventory.
| Tool | Outcome | Gate |
|---|---|---|
create_app |
Create an app (name, base_url) |
— |
configure_app |
Set content fields (name, base_url); content-only, mirrors CLI apps update |
— |
set_icon |
Set the app icon from an https icon_url |
— |
list_apps |
List the apps owned by the authenticated principal | — |
get_app_overview |
App config, publication state, and metadata in one read; single-app default when app_id is omitted. Start here |
— |
preview_app |
Open-on-device payload (iOS TestFlight URL, Android deeplink, QR target, per-platform readiness) | — |
get_build_status |
Poll a build by id until ready/failed |
— |
get_rejection |
Curated required action for a rejected app (never raw reviewer text) | — |
get_fix_recipe |
Code-free remediation recipes for the current rejection | — |
publish_app |
Start publication to the chosen stores | confirm |
unpublish_app |
Remove an app from the chosen stores | confirm |
send_push |
Send a push to the app's devices | confirm |
trigger_resubmission |
Resubmit a rejected app for review (requires a customer-owned Apple credential) | confirm |
There is intentionally no ship tool and no publish-build trigger.
Agents chain create_app → publish_app (and trigger_resubmission for
rejection cycles). Build triggering is not exposed: the store-publish-kind build
is operator-internal (Nova Release), and self-serve verification is via preview_app
(the managed preview).
Operate on the local project; no CLI or remote-MCP equivalent by design.
| Tool | Description |
|---|---|
generate_hook |
Generate a React hook for an SDK feature |
generate_component |
Generate a UI component with SDK integration |
scaffold_feature |
Scaffold a feature (hook + component + types) |
validate_setup |
Validate SDK installation and configuration |
check_permissions |
Analyze permission-handling patterns |
diagnose_issue |
Diagnose common SDK integration issues |
generate_universal_links |
Emit apple-app-site-association + assetlinks.json with hosting instructions |
Destructive tools (publish_app, unpublish_app, send_push,
trigger_resubmission) take an optional confirm: boolean. Without
confirm:true they perform no write and return a preview envelope
({ error: "confirm_required", will, ..., next_actions }) describing what would
happen. This mirrors the CLI's --confirm flag / exit-code-3 semantics at the
MCP idiom layer.
Errors are returned as structured data, never thrown. Every tool returns
{ error, message, next_actions } (plus status for HTTP errors). HTTP status
maps to a stable code: 401 → not_authenticated, 403 → forbidden,
404 → not_found, 409 → conflict, 422 → validation_error, otherwise
request_failed. Each response carries structuredContent (the canonical
envelope) and a JSON text mirror in content for text-only clients.
| URI | Description |
|---|---|
appo://overview |
SDK overview and capabilities |
appo://api/{feature} |
API reference per feature |
appo://examples/{feature} |
Code examples per feature |
appo://best-practices |
Integration best practices |
appo://troubleshooting |
Common issues and solutions |
Prompts: setup_wizard, integrate_feature, debug_assistant.
Features: push, biometrics, camera, location, haptics, storage, share, network,
device.
This release adopts the canonical tool vocabulary and the CLI-shared auth chain.
get_appremoved — superseded byget_app_overview(single-call config + publication state + metadata, with a single-app default).update_appsplit intoconfigure_app(content fieldsname/base_urlviaPATCH /apps/{id}) andset_icon(POST /apps/{id}/icon, httpsicon_url).update_app's catch-all field list (splash colors, injected CSS/JS, path fields, …) is gone — webview injection is a dashboard concern and is not on the CLI/MCPconfiguresurface.- Env vars renamed —
APPS_API_URL/APPS_API_TOKENare no longer read. Use the CLI-canonicalAPPO_API_BASE/APPO_TOKEN, or simplyappo loginand let the server read the stored profile.
pnpm install
pnpm build # tsup (ESM + CJS + DTS)
pnpm dev # watch
pnpm typecheck # tsc --noEmit
pnpm test # vitestMIT