Developer docs

BactoAI MCP server

BactoAI exposes a Model Context Protocol (MCP) server so external AI assistants — Claude, ChatGPT, Cursor, Codex or any MCP-compatible client — can read BactoAI content and, for authorised internal users, review website submissions. Every call is authenticated with OAuth 2.1 and executes under the same row-level security rules as the web app.

Endpoint

The server speaks MCP over Streamable HTTP. Point your client at the URL below; it handles discovery, authorization and JSON-RPC on the same path.

Server URL
https://bactoai.lovable.app/mcp

Protected-resource metadata is published at /.well-known/oauth-protected-resource, which clients fetch automatically to discover the authorization server.

OAuth 2.1 & consent flow

The MCP server is a resource server: it only verifies bearer tokens. Authorization is handled by BactoAI's managed auth service, with dynamic client registration enabled so clients self-register — no manual client ID or secret to copy.

  1. You add the server URL in your MCP client and start the connection. The client reads /.well-known/oauth-protected-resource and the authorization server's discovery document.
  2. The client registers itself dynamically (RFC 7591) and opens the authorize URL in your browser with PKCE.
  3. If you are not signed in you land on /auth. Sign in (or create an account) with the same email you use for the BactoAI console; the consent URL is preserved and you are returned to it afterwards.
  4. The consent screen at /.lovable/oauth/consent?authorization_id=… names the requesting client and explains that it will act as you. Choose Approve or Deny.
  5. On approval you are redirected back to the client, which exchanges the code for an access token and calls the MCP endpoint with Authorization: Bearer <token>. Already-approved clients skip the consent screen on reconnect.

Tokens are issued only through this OAuth flow. Pasting an app session token will not work — the server requires a token that carries an OAuth client_id claim.

Permissions. The token identifies you, and database access runs as your account. list_articles works for any signed-in user. The submission tools return rows only if your account holds the admin role; otherwise they return an empty result rather than an error.

Transport & request format

All calls are JSON-RPC 2.0 over HTTP POST. Streamable HTTP requires both content types in the Accept header — omitting either returns 406 Not Acceptable.

Required headers
POST https://bactoai.lovable.app/mcp
Content-Type: application/json
Accept: application/json, text/event-stream
Authorization: Bearer <access_token>
Initialize
{
  "jsonrpc": "2.0",
  "id": 0,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-06-18",
    "capabilities": {},
    "clientInfo": { "name": "my-assistant", "version": "1.0.0" }
  }
}
List tools
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list",
  "params": {}
}
curl example
curl -X POST https://bactoai.lovable.app/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer $BACTOAI_MCP_TOKEN" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

Client configuration

Most clients only need the URL. For editors that use a JSON config file, use a remote HTTP server entry:

mcp.json
{
  "mcpServers": {
    "bactoai": {
      "type": "http",
      "url": "https://bactoai.lovable.app/mcp"
    }
  }
}

In Claude or ChatGPT, add BactoAI as a custom connector using the same URL and complete the browser sign-in when prompted.

Tool reference

Three tools are advertised. All are read-only and idempotent — nothing in this server mutates or deletes data.

list_contact_submissionsAdmin role requiredread-only

Lists website submissions (demo requests, partner inquiries, newsletter signups, general contact). Rows are returned through row-level security as the signed-in user, so non-admin accounts see none.

Input schema
{
  "type": "object",
  "properties": {
    "form_type": {
      "type": "string",
      "enum": ["demo", "partner", "newsletter", "contact"],
      "description": "Filter by submission type."
    },
    "limit": {
      "type": "integer",
      "description": "Max rows to return (default 25, max 100)."
    }
  }
}
Example request
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "list_contact_submissions",
    "arguments": { "form_type": "demo", "limit": 10 }
  }
}
submission_statsAdmin role requiredread-only

Summarises submissions by form type and returns the most recent submission timestamp. Takes no arguments.

Input schema
{
  "type": "object",
  "properties": {}
}
Example request
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": { "name": "submission_stats", "arguments": {} }
}
list_articlesAny signed-in accountread-only

Lists published BactoAI blog and resource articles with title, excerpt, category, publication date and reading time. Optionally filtered by category.

Input schema
{
  "type": "object",
  "properties": {
    "category": {
      "type": "string",
      "enum": ["Research", "Clinical", "Public Health", "Product"],
      "description": "Filter articles by category."
    }
  }
}
Example request
{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "tools/call",
  "params": {
    "name": "list_articles",
    "arguments": { "category": "Clinical" }
  }
}

Responses & errors

Every tool returns MCP content: a human-readable text block containing JSON, plus structuredContent for programmatic use.

Example result
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [
      { "type": "text", "text": "{\"total\": 42, \"by_form_type\": {\"demo\": 18, \"newsletter\": 21, \"partner\": 3}, \"latest\": \"2026-08-11T09:14:02.331Z\"}" }
    ],
    "structuredContent": {
      "total": 42,
      "by_form_type": { "demo": 18, "newsletter": 21, "partner": 3 },
      "latest": "2026-08-11T09:14:02.331Z"
    }
  }
}
  • 401 Unauthorized — missing, expired or non-OAuth token. Re-run the connection flow; the response includes a WWW-Authenticate header pointing at the metadata document.
  • 406 Not Acceptable — the Accept header is missing application/json or text/event-stream.
  • Tool-level failures come back as a normal result with isError: true and a message — for example "Not authenticated", or a note that no submissions are visible because the account lacks the admin role.

Access & support

Admin access to submission data is granted manually by the BactoAI team. Sign up at /auth, then email bactoai01@gmail.com with the address you registered and we will attach the admin role.