create-holaos-app

star 3

Build a new holaOS App — a TanStack Start MCP server that proxies a third-party API through Composio. Use when the user says "create a holaos app", "make me a <provider> holaos app", "帮我创建一个 <provider> holaos app", "build a holaos app for <X>", or names a Composio toolkit they want wired up that way (e.g. "shopify holaos app", "klaviyo holaos app", "linear holaos app"). Covers the full flow end-to-end: Composio toolkit verification, scaffold from gcalendar, provider-specific client + tools + connection probe, audit log, e2e + live tests, marketplace + workspace + CI registration.

holaboss-ai By holaboss-ai schedule Updated 5/7/2026

name: create-holaos-app description: Build a new holaOS App — a TanStack Start MCP server that proxies a third-party API through Composio. Use when the user says "create a holaos app", "make me a holaos app", "帮我创建一个 holaos app", "build a holaos app for ", or names a Composio toolkit they want wired up that way (e.g. "shopify holaos app", "klaviyo holaos app", "linear holaos app"). Covers the full flow end-to-end: Composio toolkit verification, scaffold from gcalendar, provider-specific client + tools + connection probe, audit log, e2e + live tests, marketplace + workspace + CI registration.

Create a holaOS App

A holaOS App is a self-contained TanStack Start application that exposes a Composio-backed third-party service over MCP. Each app: own SQLite, own MCP server (SSE on /mcp/sse), own audit log, own tiny web UI. No shared packages — copy-paste over abstraction.

When to use

User asks for a new holaOS App wrapping a Composio toolkit (e.g. "create a shopify holaos app", "帮我做一个 figma holaos app", "build a holaos app for linear"). Existing examples in repo: stripe, figma, calendly, discordbot, linear, gcalendar, gdrive, slack, youtube, mailchimp, notion, instagram.

For publishing-style apps (drafts → queue → publish, e.g. twitter/linkedin/reddit), this skill does NOT apply — those follow a different pattern with a SQLite job queue and publisher.ts.

The 7-step flow

1. Verify the Composio toolkit exists FIRST

Don't assume — Threads / TikTok / Pinterest / Buffer are NOT in Composio. Curl the catalog before making promises.

COMPOSIO_API_KEY=<key> curl -s "https://backend.composio.dev/api/v3/toolkits?limit=500" \
  -H "x-api-key: $COMPOSIO_API_KEY" -o /tmp/toolkits.json
python3 -c 'import json; d=json.load(open("/tmp/toolkits.json"))
for t in d["items"]:
  if "<keyword>" in t["slug"].lower(): print(t["slug"], t.get("composio_managed_auth_schemes"))'

Note composio_managed_auth_schemes: if it's ["OAUTH2"], the toolkit is managed (zero-config user OAuth). If empty, the user must pass --api-key/credentials at connect time. Both work; managed is preferred.

API key location: frontend/apps/server/.envCOMPOSIO_API_KEY.

2. Scaffold from gcalendar

cd /Users/joshua/holaboss-ai/holaboss/hola-boss-apps
bash scripts/scaffold-from-gcal.sh <slug> <provider> "<Display>" "<Pascal>"
# e.g.: bash scripts/scaffold-from-gcal.sh stripe stripe "Stripe" Stripe

The script clones gcalendar/, sed-renames prefix tokens (gcal_*<slug>_*, GCalError<Pascal>Error, GCAL_BASE<UPPER>_BASE, etc.) and clears the eight files you must rewrite per-provider.

Then update package.json name:

sed -i '' "s/\"name\": \"gcalendar\"/\"name\": \"<slug>\"/" <slug>/package.json

3. Write the provider-specific files

Eight files per app — see references/file-templates.md for working snippets:

File Content
app.runtime.yaml mcp.tools list (+ tool prefix), data_schema (audit/usage/settings tables), integration.destination = Composio slug
src/lib/types.ts Error code enum, ToolSuccessMeta, <UPPER>_CONFIG
src/server/<slug>-client.ts createIntegrationClient(<slug>) proxy + status mapping (200→ok, 404→not_found, 429→rate_limited, 401/403→not_connected, 4xx→validation_failed, 5xx→upstream_error)
src/server/connection.ts Cheap identity probe (e.g. GET /me or GET /account)
src/server/mcp.ts name: "<Display> App"
src/server/tools.ts 8–13 MCP tools: registerTools + impl functions exported for tests
src/components/connection-status-bar.tsx Polls /api/connection-status
src/routes/__root.tsx + src/routes/index.tsx Page title + heading

Tool naming: snake_case, prefixed with the brand (stripe_list_customers, discord_send_channel_message even when slug is discordbot). Tool descriptions follow hola-boss-apps/docs/MCP_TOOL_DESCRIPTION_CONVENTION.md — agent only sees name+description+inputSchema+annotations.

Mandatory tool: <prefix>_get_connection_status. Always first in app.runtime.yaml's mcp.tools. Wraps getConnectionStatus() from connection.ts.

GraphQL APIs (Linear): write a gql<T>(query, vars) helper instead of apiGet/apiPost. Demote GraphQL errors[] to canonical Holaboss codes by extensions.type (AUTHENTICATION_ERRORnot_connected, INVALID_INPUTvalidation_failed, etc.). See linear/src/server/linear-client.ts.

4. Write tests

test/e2e.test.ts — uses MockBridge (already cloned in fixtures/), 5–6 cases:

  • MCP health endpoint live
  • get_connection_status with bridge succeeding
  • One write tool writes audit row with correct <slug>_record_id + deep_link
  • not_connected short-circuits when bridge throws
  • validation_failed maps a 400
  • (provider-specific) rate_limited maps 429 with retry_after

test/live.test.tsdescribe.skipIf(!process.env.LIVE), shape-only assertions, write tests gated behind LIVE_WRITE=1 plus an env-var-named test resource id (so a test runner can never delete real customer data by accident).

See references/file-templates.md for skeletons.

5. Verify locally

cd <slug>
pnpm rebuild better-sqlite3   # required first time on macOS
pnpm run typecheck            # vite.config.ts will warn — that's a pre-existing gcalendar issue, ignore
pnpm run test:e2e             # all green

6. Register the app

Three files in the repo root — see references/registration.md for full schemas:

  • marketplace.json — append entry with name, description, icon, category, tags, path, provider_id, credential_source: "platform"
  • pnpm-workspace.yaml — add - "<slug>" (alphabetical order)
  • .github/workflows/build-apps.yml — append <slug> to LEGACY_MODULES (env-var name is historical; it's the build allowlist)

Then from repo root: pnpm install to register the new workspace package.

7. Live test (the user runs this; we just commit)

User starts the broker, connects each provider once via OAuth, then LIVE=1 pnpm --filter <slug> run test:live. Connection metadata persists in .composio-connections.json (gitignored). Full instructions in references/live-testing.md.

Conventions you must follow

  • Audit log: every tool wrapped via wrapTool("<prefix>_<name>", impl) — writes a row to <slug>_agent_actions with <slug>_record_id, <slug>_deep_link, outcome, duration. Don't skip this.
  • Deep links: when the provider has a web UI, set <slug>_deep_link to the canonical URL for that resource (e.g. https://dashboard.stripe.com/customers/cus_xyz). Agents surface these to users.
  • Error envelope: only the seven canonical codes (not_found, invalid_state, validation_failed, not_connected, rate_limited, upstream_error, internal). Don't invent new ones.
  • No raw credentials: ALL provider calls go through createIntegrationClient(<slug>).proxy(...). The bridge prepends auth. Never read tokens from env or .env.
  • Live tests are shape-only: expect(Array.isArray(r.data.x)).toBe(true) — never assert on values, since user data drifts.
  • One container, two processes: web app on :3000, MCP server on :3099 (overridden by sandbox runtime via PORT/MCP_PORT env).

Pitfalls (from prior incidents)

  • Don't skip the catalog probe (step 1). Building an app for a slug that doesn't exist on Composio is wasted work — threads was discovered missing only after the app shipped.
  • Don't add a local app→provider table in desktop. The provider_id field in app.runtime.yaml flows through marketplace.json → backend → desktop catalog. Adding apps requires zero desktop edits.
  • pnpm rebuild better-sqlite3 is required the first time you run e2e tests on macOS; the prebuilt native binding doesn't always resolve correctly across new pnpm projects.
  • MockBridge matches via endsWith — query strings break suffix matchers. Test impls without query params, or refactor the impl to the bare endpoint, or extend mock-bridge if you really need it.

References (load on demand)

  • references/file-templates.md — copy-pasteable skeletons for client.ts, connection.ts, tools.ts, e2e.test.ts, live.test.ts, app.runtime.yaml
  • references/registration.md — exact format of marketplace.json entries, pnpm-workspace.yaml, build-apps.yml diffs
  • references/live-testing.md — composio:broker setup, per-provider connect commands, env vars for write-path tests
Install via CLI
npx skills add https://github.com/holaboss-ai/holaboss-apps --skill create-holaos-app
Repository Details
star Stars 3
call_split Forks 2
navigation Branch main
article Path SKILL.md
More from Creator