Skip to content

Add Notion integration for read-only workspace access#182

Open
vltbaudbot wants to merge 1 commit intomodem-dev:mainfrom
vltbaudbot:vltbaudbot/add-notion-integration
Open

Add Notion integration for read-only workspace access#182
vltbaudbot wants to merge 1 commit intomodem-dev:mainfrom
vltbaudbot:vltbaudbot/add-notion-integration

Conversation

@vltbaudbot
Copy link

Overview

This PR contributes a new Notion extension that provides read-only access to Notion workspaces via the Notion REST API. We built and tested this integration in our production Baudbot deployment and are contributing it back upstream.

Features

The notion tool provides four key actions:

🔍 Search

Find pages and databases by text query or filter by object type:

  • Full-text search across accessible workspace content
  • Optional filtering by page vs database
  • Configurable result limits (up to 100)

📄 Get

Retrieve complete page content formatted as markdown:

  • All standard block types: paragraphs, headings, lists, quotes
  • Code blocks with language syntax
  • Callouts with emoji icons
  • Nested content (fetched 1 level deep)
  • Toggles, dividers, child pages/databases
  • Media references: images, videos, files, PDFs, bookmarks

🗂️ List

Query database entries with advanced filtering:

  • JSON-based filter syntax matching Notion's API
  • Sorting by any property
  • Property value extraction (select, multi-select, date, checkbox, etc.)
  • Configurable result limits

🔬 Database

Inspect database schemas:

  • Property names and types
  • Full property list for building dynamic queries

Use Cases

Documentation Lookup

Control agents can retrieve setup guides, API references, and runbooks during task execution:

// Search for deployment docs
notion({ action: "search", query: "kubernetes deployment" })

// Read the full guide
notion({ action: "get", page_id: "..." })

Specification Retrieval

Dev-agents can read PRDs and architecture decision records:

// Find the feature spec
notion({ action: "search", query: "user authentication PRD" })

// Load the full specification
notion({ action: "get", page_id: "..." })

Project Database Queries

Query task databases for context on current work:

// Check sprint backlog
notion({
  action: "list",
  database_id: "...",
  filter: '{"property": "Sprint", "select": {"equals": "Sprint 23"}}'
})

Configuration

Required Environment Variable

NOTION_API_KEY=secret_...

Setup Steps

  1. Create an internal integration at notion.so/my-integrations
  2. Configure Read content capability only
  3. Share pages/databases with the integration via "Add connections"
  4. Copy the integration token to ~/.config/.env
  5. Restart control-agent

Security

Read-only access — Cannot create, update, or delete content
Explicit sharing required — Only sees pages/databases shared with the integration
Token stored securely~/.config/.env with 600 permissions
No credentials leaked — Verified clean of all workspace-specific IDs and tokens
Follows existing patterns — Matches security model of linear.ts and sentry-monitor.ts

Testing

✅ TypeScript compilation: Clean (npm run typecheck)
✅ Biome linting: No warnings (npx biome lint pi/extensions/notion.ts)
✅ Existing test suite: No regressions (same failures as upstream main)
✅ Production tested: Used in our live Baudbot deployment since Feb 26, 2026

Files Changed

  • pi/extensions/notion.ts — Main extension implementation (~400 LOC)
  • .env.schema — Added NOTION_API_KEY configuration
  • CONFIGURATION.md — Added Notion Integration section with setup instructions
  • docs/notion-integration.md — Comprehensive documentation (setup, usage, troubleshooting)

Implementation Details

Architecture

  • Uses Notion REST API v1 (2022-06-28)
  • Async/await with proper error handling
  • TypeBox schema validation for parameters
  • Follows ExtensionAPI patterns from pi-coding-agent

Block Content Formatting

Supports all major Notion block types with markdown conversion:

  • Text formatting preserved
  • Nested lists and indentation
  • Code blocks with language tags
  • Callouts with emoji rendering
  • Media rendered as reference links

API Efficiency

  • Single request for page metadata + content
  • Child blocks fetched only when has_children is true
  • Configurable pagination limits
  • Proper error handling for rate limits

Limitations

📋 Read-only — By design, no write operations
📋 Pagination — Limited to 100 results per query (no auto-pagination)
📋 Nesting depth — Child blocks fetched 1 level deep only
📋 Access control — Integration must be explicitly added to each page

All limitations are documented in docs/notion-integration.md.

Documentation

Added comprehensive documentation covering:

  • ✅ Setup and configuration
  • ✅ Usage examples for all actions
  • ✅ Real-world use cases
  • ✅ Security considerations
  • ✅ Troubleshooting guide
  • ✅ API reference links
  • ✅ Known limitations

Why This Matters

Many engineering teams keep critical documentation in Notion:

  • API specifications
  • Architecture decision records
  • Deployment runbooks
  • Project requirements

This extension lets Baudbot agents access that context during autonomous work, improving code quality and reducing the need for human intervention.

Contribution Notes

  • No dependencies added (uses built-in fetch API)
  • Follows existing extension patterns (linear.ts, sentry-monitor.ts)
  • All credentials removed from contributed code
  • Clean git history (single focused commit)
  • Ready to merge

We're using this in production and would love to see it land upstream so other Baudbot users can benefit! 🚀

Add notion.ts extension providing read-only access to Notion workspaces via
the Notion REST API. Enables agents to search for pages/databases, retrieve
full page content with nested blocks, query database entries with filters,
and inspect database schemas.

Features:
- Search: Find pages and databases by text query or type filter
- Get: Retrieve complete page content formatted as markdown, including
  paragraphs, headings, lists, code blocks, callouts, and nested content
  (up to 1 level deep)
- List: Query database entries with JSON filters and sorting
- Database: Inspect schema and property types

Configuration:
- NOTION_API_KEY (internal integration token) in ~/.config/.env
- Pages/databases must be explicitly shared with the integration
- Read-only access (no write, update, or delete capabilities)

Documentation:
- Updated .env.schema with NOTION_API_KEY configuration
- Updated CONFIGURATION.md with setup instructions and capabilities
- Added comprehensive docs/notion-integration.md covering setup,
  usage examples, use cases, limitations, security, and troubleshooting

Use cases:
- Documentation lookup during task execution
- Specification and ADR retrieval for dev-agents
- Project database queries for task context

The integration follows the same pattern as existing extensions (linear.ts,
sentry-monitor.ts) and includes proper error handling, type safety, and
security considerations (token stored in 600-perms env file, read-only
access enforced by API capabilities).

Tested with TypeScript compilation (no errors) and biome linting (clean).
@greptile-apps
Copy link

greptile-apps bot commented Feb 27, 2026

Greptile Summary

This PR adds a well-structured read-only Notion integration that enables Baudbot agents to access workspace documentation. The implementation follows established patterns from linear.ts and properly restricts access to read-only operations.

Key Changes:

  • Implemented notion.ts extension with 4 actions: search, get, list, database
  • Comprehensive markdown formatting for all Notion block types
  • Environment configuration and documentation in .env.schema, CONFIGURATION.md, and dedicated docs
  • Zero new dependencies (uses native fetch API)

Notable Implementation Details:

  • Proper API key validation and error handling
  • Rich block content formatting with support for paragraphs, headings, lists, code blocks, callouts, and media
  • Nested block fetching (1 level deep)
  • ID sanitization (removes hyphens from page/database IDs)

Documentation Quality:

  • Extensive user documentation with setup, usage examples, and troubleshooting
  • Security considerations clearly documented (read-only access, explicit sharing required)
  • Known limitations transparently communicated

The implementation is production-tested and ready to merge. One minor suggestion for ID validation has been noted as a best practice improvement.

Confidence Score: 4/5

  • Safe to merge - read-only integration following established patterns with comprehensive documentation
  • High confidence due to: (1) read-only operations only, (2) follows existing extension patterns, (3) production-tested, (4) comprehensive documentation, (5) no new dependencies. Minor deduction for lack of automated tests and ID validation, though these match existing codebase patterns
  • pi/extensions/notion.ts - consider adding ID validation after sanitization

Important Files Changed

Filename Overview
pi/extensions/notion.ts New read-only Notion integration following established extension patterns, with proper error handling and markdown formatting
.env.schema Added NOTION_API_KEY configuration with proper documentation link
CONFIGURATION.md Added comprehensive Notion Integration section with setup instructions and capability descriptions

Sequence Diagram

sequenceDiagram
    participant Agent as Baudbot Agent
    participant Ext as notion.ts Extension
    participant API as Notion REST API
    participant Page as Notion Workspace

    Note over Agent,Page: Search Action
    Agent->>Ext: notion({action: "search", query: "..."})
    Ext->>API: POST /search
    API->>Page: Query pages/databases
    Page-->>API: Results
    API-->>Ext: JSON response
    Ext-->>Agent: Formatted list with titles, URLs

    Note over Agent,Page: Get Page Content Action
    Agent->>Ext: notion({action: "get", page_id: "..."})
    Ext->>API: GET /pages/{id}
    API-->>Ext: Page metadata
    Ext->>API: GET /blocks/{id}/children
    API->>Page: Fetch blocks
    Page-->>API: Block content
    API-->>Ext: Blocks JSON
    loop For blocks with children
        Ext->>API: GET /blocks/{child_id}/children
        API-->>Ext: Nested blocks
    end
    Ext-->>Agent: Markdown formatted content

    Note over Agent,Page: Database Query Action
    Agent->>Ext: notion({action: "list", database_id: "...", filter: "..."})
    Ext->>API: POST /databases/{id}/query
    API->>Page: Query with filters
    Page-->>API: Database rows
    API-->>Ext: JSON entries
    Ext-->>Agent: Formatted entries with properties

    Note over Agent,Page: Database Schema Action
    Agent->>Ext: notion({action: "database", database_id: "..."})
    Ext->>API: GET /databases/{id}
    API->>Page: Get schema
    Page-->>API: Database structure
    API-->>Ext: Properties metadata
    Ext-->>Agent: Property list with types
Loading

Last reviewed commit: d45915b

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +180 to +181
// Remove hyphens from page_id if present (Notion accepts both formats)
const pageId = params.page_id.replace(/-/g, "");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ID sanitization without validation - consider validating that the result is a 32-character hex string after removing hyphens to prevent potential API errors from malformed inputs

Suggested change
// Remove hyphens from page_id if present (Notion accepts both formats)
const pageId = params.page_id.replace(/-/g, "");
const pageId = params.page_id.replace(/-/g, "");
if (!/^[a-f0-9]{32}$/i.test(pageId)) {
return "❌ page_id must be a valid Notion page ID (32 hex characters)";
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: pi/extensions/notion.ts
Line: 180-181

Comment:
ID sanitization without validation - consider validating that the result is a 32-character hex string after removing hyphens to prevent potential API errors from malformed inputs

```suggestion
	const pageId = params.page_id.replace(/-/g, "");
	if (!/^[a-f0-9]{32}$/i.test(pageId)) {
		return "❌ page_id must be a valid Notion page ID (32 hex characters)";
	}
```

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants