Second Brain Copilot Documentation

Second Brain Copilot exposes your Notion HuB databases through two interfaces: the Model Context Protocol (MCP) for AI agents and a REST API for custom applications.

Both interfaces use the same OAuth2 authentication, permissions model, and people-based access filtering.

Authentication

All access requires a session token obtained via Notion OAuth:

  1. Visit /auth/notion in your browser
  2. Authorize the integration in Notion
  3. Copy your session token from the callback page
  4. Use it as a Bearer token: Authorization: Bearer <token>

Tokens are single-use: once an MCP client connects with a token, it activates into a session. For the web dashboard, tokens are stored in localStorage and validated via /auth/status.

Claude Desktop Configuration

Add this to your Claude Desktop MCP config file (claude_desktop_config.json):

{
  "mcpServers": {
    "second-brain": {
      "url": "http://localhost:3100/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_SESSION_TOKEN"
      }
    }
  }
}

After adding the config, restart Claude Desktop. The Second Brain tools will appear in the tools menu.

Cursor Configuration

In Cursor settings, navigate to MCP Servers and add:

{
  "second-brain": {
    "url": "http://localhost:3100/mcp",
    "headers": {
      "Authorization": "Bearer YOUR_SESSION_TOKEN"
    }
  }
}

Claude Code Configuration

In your project's .mcp.json or Claude Code settings, add:

{
  "mcpServers": {
    "second-brain": {
      "type": "url",
      "url": "http://localhost:3100/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_SESSION_TOKEN"
      }
    }
  }
}

Custom MCP Clients

Use the @modelcontextprotocol/sdk package to build custom clients:

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from
  "@modelcontextprotocol/sdk/client/streamableHttp.js";

const transport = new StreamableHTTPClientTransport(
  new URL("http://localhost:3100/mcp"),
  {
    requestInit: {
      headers: {
        Authorization: "Bearer YOUR_SESSION_TOKEN",
      },
    },
  }
);

const client = new Client({ name: "my-app", version: "1.0" });
await client.connect(transport);

// List available tools
const tools = await client.listTools();
console.log(tools);

// Call a tool
const result = await client.callTool({
  name: "query_hub",
  arguments: { hub: "action", limit: 5 },
});
console.log(result);

Available MCP Tools

The following tools are available through the MCP protocol:

ToolDescriptionParameters
list_hubsList all available HuB databasesNone
get_hub_schemaGet property schema for a HuBhub (string)
query_hubQuery pages from a HuB with filtershub, filter?, sort?, limit?
create_pageCreate a new page in a HuBhub, properties (object)
update_pageUpdate an existing pagepageId, hub, properties
get_pageGet a single page by IDpageId
comment_on_pageAdd a comment to a pagepageId, text
search_peopleSearch the people directoryquery

REST API — Authentication

All API requests require a Bearer token in the Authorization header:

Authorization: Bearer YOUR_SESSION_TOKEN

Obtain a token by completing the OAuth flow at /auth/notion.

You can validate your token with:

GET /auth/status

Returns { "authenticated": true, "user": { "name", "email", "role" } } or { "authenticated": false }.

Base URL & Error Format

Base URL: http://localhost:3100/api

All error responses follow this format:

{
  "error": "Human-readable error message"
}

HTTP status codes: 400 bad request, 401 unauthorized, 403 forbidden, 404 not found, 500 server error.

List HuBs

GET /api/hubs

Returns all configured HuB databases.

Response:

{
  "hubs": [
    { "key": "action", "label": "Action", "description": "Tasks, actions...", "propertyCount": 28 },
    { "key": "outcome", "label": "Outcome", ... }
  ]
}

Get HuB Schema

GET /api/hubs/:key

Returns the full schema for a HuB including all properties, their types, options, and your access level.

Response:

{
  "hub": "action",
  "label": "Action",
  "databaseId": "22430fc8...",
  "properties": [
    { "name": "Title", "type": "title" },
    { "name": "Status", "type": "status", "options": ["Backlog", "In Progress", "Done", "Cancelled"] },
    { "name": "Priority", "type": "select", "options": ["P0 – Critical", ...] }
  ],
  "access": { "canWrite": true, "writableHubs": ["*"], "role": "admin" }
}

Query Pages

GET /api/hubs/:key/pages

Query pages from a HuB with optional filtering, sorting, and pagination.

Query parameters:

  • filter — Filter expression (see Filter Syntax)
  • sort — Sort expression (see Sort Syntax)
  • limit — Max results (default 20, max 100)

Example:

GET /api/hubs/action/pages?filter=Status=In Progress&sort=Due:asc&limit=10

Response:

{
  "hub": "action",
  "count": 3,
  "pages": [
    {
      "id": "abc123...",
      "created_time": "2025-01-15T10:30:00Z",
      "last_edited_time": "2025-01-16T08:00:00Z",
      "properties": {
        "Title": "Review Q4 report",
        "Status": "In Progress",
        "Priority": "P1 – Major",
        "Due": { "start": "2025-01-20", "end": null }
      }
    }
  ]
}

Create Page

POST /api/hubs/:key/pages

Create a new page in a HuB. Requires write permission.

Request body:

{
  "properties": {
    "Title": "Follow up with client",
    "Status": "Backlog",
    "Priority": "P2 – Moderate",
    "Domain": "Sales",
    "Due": "2025-02-01"
  }
}

Response: 201 Created

{
  "success": true,
  "page": { "id": "new-page-id", ... }
}

Get Page

GET /api/pages/:id

Retrieve a single page by its Notion page ID. Access is checked against people properties.

Response:

{
  "id": "abc123...",
  "created_time": "2025-01-15T10:30:00Z",
  "last_edited_time": "2025-01-16T08:00:00Z",
  "properties": { ... }
}

Update Page

PATCH /api/pages/:id

Update properties on an existing page. Requires write permission for the HuB.

Request body:

{
  "hub": "action",
  "properties": {
    "Status": "Done",
    "Done Date": "2025-01-18"
  }
}

Add Comment

POST /api/pages/:id/comments

Add a comment to a page.

Request body:

{
  "text": "Updated the status and assigned to Pankaj."
}

Response: { "success": true, "commentId": "..." }

Search People

GET /api/people?q=search_term

Search the workspace people directory by name or email.

Response:

{
  "query": "pankaj",
  "count": 1,
  "people": [{ "id": "...", "name": "Pankaj", "type": "person", "email": "pankaj.shrestha@itsutra.com" }]
}

People Directory

GET /api/people/directory

Returns the full people directory (all workspace members and bots). Used by the web dashboard for client-side name resolution.

Response:

{
  "count": 42,
  "people": [
    { "id": "...", "name": "Pankaj", "type": "person", "email": "pankaj.shrestha@itsutra.com" },
    { "id": "...", "name": "Zapier", "type": "bot", "email": null }
  ]
}

Property Value Formats

When creating or updating pages, property values should be provided in simplified format:

TypeValue FormatExample
titleString"Review Q4 report"
rich_textString"Some notes here"
selectOption name (string)"P1 – Major"
multi_selectArray of option names["IT", "HR"]
statusStatus name (string)"In Progress"
dateISO date string"2025-01-20"
numberNumber42
checkboxBooleantrue
peopleArray of user IDs["user-id-1"]
relationArray of page IDs["page-id-1"]
urlURL string"https://example.com"
emailEmail string"user@example.com"
phone_numberPhone string"+1-555-0123"

Filter Syntax

Filters use a simple Property=Value format passed as a query string parameter:

# Simple equality
filter=Status=In Progress

# Multiple filters (AND)
filter=Status=In Progress,Priority=P1 – Major

# The server translates these into Notion API filter objects
# based on the property type.

The server automatically determines the correct filter operator based on the property type (e.g., equals for select, contains for text).

Sort Syntax

Sorts use Property:direction format:

# Single sort
sort=Due:asc

# Multiple sorts (comma-separated)
sort=Priority:desc,Due:asc

Direction is either asc (ascending) or desc (descending).