Reference

MCP tools reference

Ellivate's MCP server exposes two flavors of tools to your AI coding tool. **Guidance** tools (eight of them: persistence, blob storage, collections, reasoning, time, notifications, user-auth, import-path) return formatted instructions the LLM follows while writing code. **Action** tools (six of them: doctor, publish, list-apps, get-app, connect-secret, viewer) talk to the Ellivate cloud directly. Together they mean the LLM produces Ellivate-ready code from the first prompt and can publish without leaving the IDE.

Install

Pick your AI tool:

  • Claude Code: claude mcp add ellivate --scope user --transport stdio -- npx -y @ellivateai/mcp-server
  • Cursor: Settings → MCP Servers → Add; command npx -y @ellivateai/mcp-server, transport stdio.
  • Windsurf: edit ~/.codeium/windsurf/mcp_config.json — see the walkthrough.

The tools

ellivate_user_auth

Returns the Ellivate identity pattern. The LLM calls this before writing any sign-in / session / auth-related code. Teaches:

  • No third-party auth libraries (NextAuth, Clerk, Firebase Auth, etc.) — they fight the shared-shell.
  • No /login / /signup / /auth/callback routes.
  • Viewer identity comes from the Ellivate SDK — ellivate.viewer() in both JS and Python.
  • Shape: { id, email, username, verified } or null for anonymous.

Input: none.

Output: Markdown-formatted guidance the LLM incorporates into its code.

ellivate_persistence

Returns the KV storage pattern. The LLM calls this before writing any data-reading / data-writing code. Teaches:

  • Don't use localStorage, files, sqlite3, IndexedDB, redux-persist, or any persistence plugin.
  • Use ellivate.kv.get/set/delete/list. The key prefix sets the scope, resolved deterministically at runtime — no scope argument. No prefix or personal: is "just for you" (private per viewer, the default); shared: is "shared with this group"; public: is "anyone who opens it." Forget the prefix and the data stays private.
  • Concrete code examples for JS/TS and Python.

Input: none.

Output: Markdown-formatted guidance.

ellivate_collections

Returns the "many similar things" pattern. The LLM calls this whenever the data shape is a list of items you'd want to filter, sort, paginate, or address by id — a roster, a feed, a list of expenses, a stack of calendar events. Teaches:

  • Don't model many-similar-rows as sqlite + INSERT/SELECT loops, or as a list-of-dicts in a single KV value, or as the prefix-key workaround ellivate.set("things:" + i, ...) in a loop.
  • Use ellivate.collection(name).add/list/get/update/replace/delete — platform-assigned ids, native filtering, pagination.
  • Scope works the same as KV — by prefix on the name. No prefix or personal: is private per viewer (the default); ellivate.collection("shared:trips") is shared with the group; public: is readable by anyone. No scope argument — the prefix is it.
  • Reserved field names (id, createdAt, updatedAt) and v1 limits on where / sort.

Input: none.

Output: Markdown-formatted guidance.

ellivate_reasoning

Returns the LLM-completion pattern. The LLM calls this before writing any code that imports a provider SDK (@anthropic-ai/sdk, openai, @google/genai, langchain, etc.) or when the user describes the task as "extract from text", "summarize", "classify", "draft a reply", etc. Teaches:

  • Don't import provider SDKs directly — that requires per-app env vars and the user manages a key per tool.
  • Use ellivate.reason({ prompt, system?, model?, maxTokens? }) — single-shot completion via the owner's BYO API key, proxied server-side. One key in account settings; every tool benefits.
  • Model registry: model name picks the provider (claude-* → Anthropic, gpt-* → OpenAI, gemini-* → Google). Omit model for the cheapest default.
  • Per-app monthly soft cap (default $5) with warning field surfacing on threshold; never refuses.

Input: none.

Output: Markdown-formatted guidance.

ellivate_time

Returns the scheduling pattern. The LLM calls this before writing any code that runs on a recurrence, fires after a delay, or needs to schedule something for a specific datetime. Triggers: setInterval / setTimeout for app logic that should survive restarts, node-cron, apscheduler,celery beat, croniter, BullMQ schedules, AWS EventBridge, Vercel Cron Jobs, OR user phrasing like "every Monday", "daily at 9am", "remind me in 7 days", "weekly digest", "before the event". Teaches:

  • Use ellivate.schedule.{at, cron, in} — the platform calls back to your handler at the scheduled time.
  • What the handler receives: { scheduleId, firedAt, data } body + schedule-fire / app-id / viewer-token headers.
  • All times UTC; min interval 1 minute; per-app cap 1000 schedules; per-app fire rate 1000/hour soft; auto-pause after 5 consecutive failures.
  • Don't embed large state in data (16KB cap). Reference by id, look up in the handler.

Input: none.

Output: Markdown-formatted guidance.

ellivate_blob_storage

Returns the binary-storage pattern. The LLM calls this before writing any code that handles photos, audio, files, or other binary uploads. Teaches:

  • Don't base64-encode files into ellivate.set — KV is capped at 256KB per value.
  • Use ellivate.blob.put/get/list/del for bytes up to 25MB. The proxy URL goes in KV alongside any metadata.
  • How FileReader.readAsDataURL, canvas.toDataURL, and base64.b64encode patterns map to blob.put.

Input: none.

Output: Markdown-formatted guidance.

ellivate_notifications

Returns the canonical ellivate.notify(...) server-side pattern. The LLM calls this before reaching for expo-notifications, OneSignal, Firebase Cloud Messaging, web-push, the browser Notification API, or APNs. Also fires on natural intent: "send me a push when X", "remind me when Y", "ping me about Z". Teaches:

  • ellivate.notify is server-only — calling from a 'use client' component or vanilla browser script throws synchronously and the publish-time scanner flags it.
  • Targeting forms: omitted (current viewer), "owner", { username | userId | email }, { spaceId: "..." } (posts to that Space's chat, rendered as a card — the tool must be attached to that Space), or { space: "current" } (the Space the tool was opened from, resolved server-side — loud-fails with a 400 if the tool wasn't opened from a Space; the default form for "post to this Space").
  • Rate limits exist (5/hour per pair, 100/hour per app) — no setInterval(notify, ...).
  • Why no native push libs work: App Store guideline 4.7 prohibits native bridges in WebView.

Input: none.

Output: Markdown-formatted guidance.

ellivate_connections

Returns the canonical ellivate.connection(name) managed-OAuth pattern. The LLM calls this BEFORE writing or refactoring any code that talks to a third-party system with an OAuth-authenticated API — Google Calendar / Drive / Gmail, Slack, Notion, Linear, GitHub, Spotify, Strava. ALSO fires on direct provider-SDK imports (googleapiclient, googleapis npm, @slack/web-api, notion-client), on raw OAuth code (fetch("oauth2.googleapis.com/token") refresh, redirect-handler routes), and on natural intent like "add to my calendar", "sync with Google Calendar", "post to Slack", "create a Notion page". Teaches:

  • ellivate.connection(name) is sync + lazy — returns a handle whose .fetch() / .fetchJson() attach a valid auto-refreshing OAuth token.
  • The curated integration list — google-calendar, google-drive, google-sheets, slack, strava at time of writing. Inventing keys (notion, whoop, etc.) throws at runtime.
  • PERSONAL (default) = per-viewer, lazy at runtime. SHARED = builder-owned, blocks publish until connected.
  • EllivateConnectionNeeded exception surfaces a "connect your account" prompt — must not be swallowed.
  • Retrofit pattern for raw googleapiclient / googleapis npm usage: refactor to ellivate.connection, drop the SDK from dependencies.

Input: none.

Output: Markdown-formatted guidance.

ellivate_import_path

Given the current file's path relative to the project root, returns the correct relative import specifier for the Ellivate SDK. Prevents the classic bare-specifier bug (from "ellivate-client") that breaks npm ci.

Input: { filePath: string } — e.g. "app/dashboard/page.tsx".

Output: the relative import specifier — e.g. "../../ellivate-client".

How the LLM uses them

A typical flow:

  1. You prompt: "Build a reading tracker for Ellivate with personal books per user."
  2. The LLM calls ellivate_user_auth to learn the identity pattern. Output says "no login route, use ellivate.viewer()".
  3. The LLM calls ellivate_persistence to learn the storage pattern. Output shows the KV SDK signatures and the scope prefixes — private by default, shared: to share with the group.
  4. The LLM calls ellivate_import_path with the file it's writing (e.g. "app/page.tsx"). Output: "../ellivate-client".
  5. The LLM writes code that follows the conventions, using the returned import path.
  6. You run ellivate publish. No translator rewrites needed; the code was already shaped right.

Source

The MCP server is open source. Tool implementations live in packages/mcp-server/src/tools/ in the Ellivate repo.

Guidance tools (return formatted instructions for the LLM):

  • persistence.ts — KV guidance
  • blob-storage.ts — binary file storage
  • collections.ts — Memory:S structured lists
  • reasoning.ts — LLM calls via ellivate.reason
  • time.ts — cron / scheduled handlers via ellivate.schedule
  • notifications.ts — push delivery
  • user-auth.ts — identity guidance
  • import-path.ts — relative-path resolver

Action tools (talk to the Ellivate cloud):

  • doctor.ts — diagnose a publish or app health
  • publish.ts — publish the current project
  • list-apps.ts — list the user's apps
  • get-app.ts — fetch one app's details
  • connect-secret.ts — wire up a connection
  • viewer.ts — get the signed-in viewer

Tool descriptions are kept in sync with Ellivate's server-side translators — when a pattern changes on one side, it changes on the other.

What's next