Integrations

MCP server

Connect Claude Code, Claude Desktop, Cursor, or any MCP-aware client to a Project88 workspace for read-only access to your data tables, calendar, dial, inbox, pages, and templates — thirty tools, all scoped to a single user's own permissions.

Project88 exposes a hosted Model Context Protocol server so MCP-aware clients — Claude Code, Claude Desktop, Cursor, and others — can read from your org directly. No browser session, no copy-paste of data: the client calls tools against your org, and your permissions follow the token.

The server runs as the mcp Supabase Edge Function, speaks JSON-RPC 2.0 over Streamable HTTP (MCP 2025-06-18), and authenticates via per-user bearer tokens (p88_…).

What it exposes

Thirty read-only tools across the six native apps — Tables, Calendar, Dial, Inbox, Pages, and Templates — plus two system tools.

AppTools
Systemwhoami, get_current_time
Tableslist_tables, get_table, query_table_rows, get_table_row, list_table_views, list_row_activities
Calendarlist_calendars, list_calendar_events, get_calendar_event, search_calendar_events
Diallist_dial_buckets, get_dial_bucket, get_dial_bucket_stats, get_dial_buckets_overview, list_dial_bucket_calls, list_dial_suppressions
Inboxlist_email_accounts, list_inbox, get_email_thread, get_email_threads, list_email_labels
Pageslist_pages, search_pages, search_page_content, get_page
Templateslist_templates, get_template, search_templates

Notes:

  • Row values on tables live in a data object keyed by column id. Call list_tables or get_table first to interpret them.
  • Calendar collapses duplicate mirror copies of the same meeting and reports how many were merged.
  • Dial calls the same dial_bucket_stats / dial_buckets_overview RPCs the app uses, so counts (including meetings) match what a rep sees in the shell.
  • Inbox reads Gmail or Microsoft Graph through your own OAuth connection; prefer the batch get_email_threads over N calls to get_email_thread.
  • Pages are serialized to markdown; if a block type isn't recognised, the response reports it in unrecognized_block_types rather than dropping content silently.
  • Templates preserve merge tags ({{first_name}}) verbatim — they're content, not variables.

No write tools. No send, create, update, or delete surface exists. MCP clients call tools without confirming each one, so a hallucinated delete_event shouldn't be able to land in production data. A confirming write-tool surface is on the roadmap.

Connect Claude Code

  1. Mint a token. In Project88: Settings → MCP access → Create a token. The raw value is shown exactly once — copy it now. Only a sha-256 hash is stored.

  2. Export it in the shell you'll launch Claude Code from:

    export PROJECT88_MCP_TOKEN='p88_...'
  3. Add a .mcp.json at the root of your project (Claude Code auto-loads it):

    {
      "mcpServers": {
        "project88": {
          "type": "http",
          "url": "https://<your-project-ref>.supabase.co/functions/v1/mcp",
          "headers": { "Authorization": "Bearer ${PROJECT88_MCP_TOKEN}" }
        }
      }
    }
  4. Verify. Start a new session and ask "who am I in Project88?" — Claude should call whoami and name your user and org. Or check the connection listing:

    claude mcp list

Connect Claude Desktop, Cursor, or another MCP client

Any client that speaks the same JSON-RPC-over-HTTP transport works the same way. Point it at the endpoint from step 3 above and send the token as Authorization: Bearer p88_….

What a token can and cannot do

A token authenticates as one user in one organization. Every query runs through that user's own Row Level Security policies, so:

  • It reads exactly what its owner can read in the app — no more.
  • It cannot read another organization's data, even by accident — isolation is enforced by Postgres, not by application code remembering to filter.
  • It stops working the moment its owner leaves the org, or the token is revoked.
  • It is read-only.

The token grants your permissions to whoever holds it. Treat it like a password: keep it in an environment variable, never commit it.

Revoking

Settings → MCP access → Revoke token. Revocation is immediate — the server also drops its cached session for the token, so an in-flight client stops working at once. Tokens are revoked rather than deleted, so created_at and last_used_at survive for audit.

Conventions

Pagination. List tools take limit / offset and return pagination.has_more. An exact total is omitted unless you pass include_total: true — computing one is a full scan under the row's RLS predicate.

Errors. Tool failures come back as a normal result with isError: true and a plain-language message, so a model can recover. Database internals are never included; an unrecognised failure returns a generic message plus a short reference id that matches a server log line.

Ids. Tools expect table_id, event_id, bucket_id and friends as UUIDs. Get them from the corresponding list_* tool first — they aren't guessable.

How auth works (under the hood)

Each request carries Authorization: Bearer p88_…. The mcp function:

  1. sha-256-hashes the token and looks it up in mcp_tokens;
  2. exchanges it for a real user Supabase session for the token's owner (cached in-memory and in mcp_sessions with the live session held in Vault, so cold starts don't pay two round trips);
  3. runs the tool's queries through that session — so RLS is the enforcement boundary, not .eq('org_id', …) filters written by hand in each tool.

Unknown, revoked, or non-p88_-prefixed bearer values are rejected with 401 Unauthorized.

The user-session model is what makes Inbox work at all: mailbox credentials in integrations are keyed by user_id, not org_id, so an org-only identity has nothing to resolve against.

Limits and caveats

  • One user + one org per token. If you administer multiple orgs, or want to expose a different member's view, mint a separate token per scope and add each as its own MCP server entry.
  • Read-only by design.
  • Rate limiting. None yet at the MCP layer; upstream providers (Gmail, Graph) enforce their own. Per-token quotas will land before this surface is opened to end-customers.
  • Inbox needs a connected mailbox. The user the token belongs to must have Gmail or Microsoft connected under Integrations; an unconnected user gets an explicit error, not empty results.

Troubleshooting

SymptomLikely cause
401 UnauthorizedToken missing, malformed, revoked, or PROJECT88_MCP_TOKEN not exported in the shell that launched the client.
405 on GETExpected — the server has no server→client stream; clients fall back to POST.
Tools listed but every call failsThe token's owner may have been removed from the org.
Inbox tools say no account connectedThat user has no mailbox connected — integrations are per-user, not per-org.
Inbox says several mailboxes are connectedPass integration_id from list_email_accounts.
A tool returns "not available on this server"Schema drift — a column referenced by a tool has been renamed or dropped. Regenerate the tool's schema and redeploy.

Where to next

  • Tools — how Project88's own agents call tools
  • Data tables — the shape behind query_table_rows
  • Inbox — the mailbox surface behind the inbox tools
  • Vault and secrets — how Project88 stores credentials in general

On this page