DEV Community

Cover image for Notion API Docs
Dan
Dan

Posted on

Notion API Docs

This is a submission for the Notion MCP Challenge

What I Built

Video Demo

Show us the code

How I Used Notion MCP

Here is pseudocode for the core iMessage bridge server (the "relay" part of your concept #1: iMessage assistant with Notion access). This assumes a realistic 2026 setup where you run a macOS machine (physical Mac mini, old MacBook, or VM) as the iMessage gateway—because Apple still doesn't provide a public API for sending/receiving iMessage programmatically from non-Apple servers.The most practical paths in 2026 are:BlueBubbles server (open-source, REST/WebSocket API for sending/receiving iMessages via a Mac proxy) → easiest for custom bots.
chat.db watcher + AppleScript/osascript (poll the Messages SQLite DB + send via osascript) → lightweight but brittle.
Emerging tools like OpenClaw/Moltbot gateways (SSH tunnel to Mac + imsg CLI tool) for cloud-hosted agents.

I'll give pseudocode for the BlueBubbles-style REST/WebSocket relay (most reliable/flexible for an agent), then note variations.High-Level Architecture

User iPhone <--> iMessage (blue bubbles) <--> macOS Server (BlueBubbles or custom)
|
v
Bridge Server (Node.js / Python / Go)
|
v
AI Agent (Grok / OpenAI / local LLM)
|
v
Notion API (MCP tools: query/create/update)
|
v
Reply back through bridge → iMessage

Pseudocode: Bridge Server (Node.js / Express + WebSocket example)javascript

// bridge-server.js
// Requirements: Node.js, express, ws (WebSocket), axios (for Notion), dotenv

require('dotenv').config();
const express = require('express');
const WebSocket = require('ws');
const axios = require('axios');
const app = express();
app.use(express.json());

const PORT = process.env.PORT || 3000;
const NOTION_TOKEN = process.env.NOTION_TOKEN;
const NOTION_VERSION = "2022-06-28"; // or latest
const AGENT_ENDPOINT = "http://localhost:8000/agent"; // or your LLM endpoint (e.g. Grok API, Ollama, etc.)

// ────────────────────────────────────────────────
// Option A: WebSocket to BlueBubbles server (recommended)
// BlueBubbles runs on your Mac → ws://localhost:1234 or remote
let bbWS = null;

function connectToBlueBubbles() {
bbWS = new WebSocket('ws://your-mac-ip:1234'); // BlueBubbles WS endpoint

bbWS.on('open', () => {
console.log('Connected to BlueBubbles');
// Optional: auth if needed
});

bbWS.on('message', async (data) => {
const msg = JSON.parse(data);
if (msg.type !== 'message' || !msg.text) return;

const from = msg.from;          // phone number or handle
const body = msg.text.trim();

console.log(`Received from ${from}: ${body}`);

// Forward to AI agent
try {
  const agentResponse = await fetchAgentResponse(body, from);
  // Send reply back via BlueBubbles
  bbWS.send(JSON.stringify({
    type: 'send_message',
    to: from,
    text: agentResponse.reply,
    // optional: threadId if group chat
  }));
} catch (err) {
  console.error('Agent error:', err);
  // Fallback reply
  bbWS.send(JSON.stringify({
    type: 'send_message',
    to: from,
    text: "Sorry, something went wrong on my end 😅",
  }));
}
Enter fullscreen mode Exit fullscreen mode

});

bbWS.on('close', () => {
console.log('BlueBubbles disconnected - reconnecting in 5s...');
setTimeout(connectToBlueBubbles, 5000);
});
}

connectToBlueBubbles();

// ────────────────────────────────────────────────
// Option B: Polling chat.db watcher alternative (if no BlueBubbles)
// Run a separate script on Mac that watches chat.db → HTTP POST to this server

app.post('/incoming-imessage', async (req, res) => {
const { from, body, messageId } = req.body; // sent by Mac watcher

// Deduplicate if needed (store last messageId in Redis/memory)

const agentResponse = await fetchAgentResponse(body, from);

// Reply: call BlueBubbles REST API or osascript via SSH
await sendIMessage(from, agentResponse.reply);

res.sendStatus(200);
});

// ────────────────────────────────────────────────
// AI Agent call (could be local Ollama, Grok API, OpenAI, etc.)
async function fetchAgentResponse(userMessage, userId) {
// Optional: maintain short context per user (Redis / in-memory map)
// e.g. const context = getUserContext(userId);

const res = await axios.post(AGENT_ENDPOINT, {
message: userMessage,
// context,
instructions:
You are a helpful Notion-integrated assistant.
Parse commands like:
- "Log an idea: ..." → create Notion page
- "What’s on my plate today?" → query Tasks db
Use Notion MCP tools internally.
Keep replies concise (< 300 chars for iMessage).
Return JSON: { reply: "text here", actionTaken?: "log|query|..." }

});

// In real agent: it would call Notion APIs here (search_pages, create_page, etc.)
// For pseudocode, assume it returns the final user-facing reply
return res.data;
}

// ────────────────────────────────────────────────
// Send function (BlueBubbles REST example)
async function sendIMessage(to, text) {
await axios.post('http://your-mac-ip:1234/api/v1/message', {
recipient: to, // phone number or email
message: text,
// attachments?, effect?, etc.
}, {
headers: { Authorization: Bearer ${process.env.BB_TOKEN} }
});
}

// Or fallback: SSH to Mac and run osascript
// async function sendIMessageFallback(to, text) {
// exec(ssh user@mac 'osascript -e \'tell application "Messages" to send "${text}" to buddy "${to}" of service "E:..."\' ');
// }

app.listen(PORT, () => {
console.log(iMessage Bridge running on port ${PORT});
});

Key Variations / NotesBlueBubbles Setup (Recommended 2026 path)
Install BlueBubbles server on your Mac → pair your iPhone → expose REST/WebSocket API (default port 1234 or custom).
Your bridge talks to it like any chat API. Very reliable for two-way.
chat.db + imsg CLI (lightweight alternative)
On Mac: run a watcher script (Node/Python) that tails ~/Library/Messages/chat.db (use sqlite3 polling or FSEvents).
On new message → POST to your bridge server → bridge calls agent → bridge SSH-es back to Mac → osascript or imsg send "text" --to "+15551234567".
Security / Production Use HTTPS + auth tokens between phone bridge.

Rate-limit per sender to avoid spam.

Store sessions/context (Redis) for multi-turn convos.

For groups: handle chat_identifier / thread IDs.

Apple can detect heavy automation → use sparingly or rotate Apple IDs if needed.

Notion Integration Snippet (inside agent) python

Example Python agent snippet

if "log an idea:" in message.lower():
idea = message.split(":", 1)[1].strip()
notion.pages.create(
parent={"database_id": IDEAS_DB_ID},
properties={"Title": {"title": [{"text": {"content": idea}}]}}
)
return f"Idea logged: {idea}"

Top comments (0)