Metadata-Version: 2.4
Name: notionary
Version: 0.6.3
Summary: Python library for programmatic Notion workspace management - databases, pages, and content with advanced Markdown support
Project-URL: Homepage, https://github.com/mathisarends/notionary
Author-email: Mathis Arends <mathisarends27@gmail.com>
License: MIT
License-File: LICENSE
Requires-Python: <4.0,>=3.12
Requires-Dist: aiofiles<25.0.0,>=24.1.0
Requires-Dist: httpx>=0.28.0
Requires-Dist: pydantic>=2.11.4
Provides-Extra: mcp
Requires-Dist: fastmcp>=3.1.1; extra == 'mcp'
Description-Content-Type: text/markdown

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="./static/notionary-dark.png">
  <source media="(prefers-color-scheme: light)" srcset="./static/notionary-light.png">
  <img alt="Notionary logo" src="./static/notionary-light.png" width="full">
</picture>

<h1 align="center">Notionary</h1>

<div align="center">

[![PyPI version](https://badge.fury.io/py/notionary.svg)](https://badge.fury.io/py/notionary)
[![Python Version](https://img.shields.io/badge/python-3.12%2B-3776AB?logo=python&logoColor=white)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Downloads](https://img.shields.io/pypi/dm/notionary)](https://pypi.org/project/notionary/)
[![Documentation](https://img.shields.io/badge/docs-notionary-blue?style=flat&logo=readthedocs)](https://mathisarends.github.io/notionary)
[![Notion API](https://img.shields.io/badge/Notion%20API-v1-black?logo=notion)](https://developers.notion.com/)

**The Modern Notion API for Python & AI Agents**

Transform complex Notion API interactions into simple, Pythonic code. Perfect for developers building AI agents, automation workflows, and dynamic content systems. Also ships with a built-in [MCP server](#mcp-server) for tool-use with any MCP-compatible AI agent.

</div>

---

## Why Notionary?

| | |
|---|---|
| **AI-friendly** | Composable APIs that drop cleanly into agent workflows |
| **Smart discovery** | Find pages/databases by title with fuzzy matching — no ID spelunking |
| **Markdown content** | Read & write page content as Markdown via the Notion Markdown API |
| **Async-first** | Modern Python with full `async` / `await` |
| **Round-trip editing** | Read a page as Markdown, transform it, write it back |
| **Full coverage** | Pages, databases, data sources, file uploads, users, workspace search |

---

## Installation

```bash
pip install notionary          # core library
pip install notionary[mcp]     # + MCP server
```

Set your Notion integration token:

```bash
export NOTION_API_KEY=your_integration_key
```

---

## Quick Start

All access goes through the `Notionary` client, which exposes namespace objects — each mapping to a Notion API area.

```python
import asyncio
from notionary import Notionary

async def main():
    async with Notionary() as notion:
        # Find a page by title (fuzzy matching)
        page = await notion.pages.find("Meeting Notes")
        print(page.title, page.url)

        # Read content as Markdown
        md = await page.get_markdown()
        print(md)

        # Append content
        await page.append("## Action Items\n- [ ] Review proposal")

        # Replace all content
        await page.replace("# Fresh Start\nThis page was rewritten.")

asyncio.run(main())
```

---

## Core API

### Pages

```python
async with Notionary() as notion:
    # Lookup
    page = await notion.pages.find("Sprint Board")
    page = await notion.pages.from_id(page_uuid)

    # List & search
    pages = await notion.pages.list(query="roadmap")
```

#### Content (Markdown API)

```python
    md = await page.get_markdown()       # read as Markdown
    await page.append("## New Section") # append blocks
    await page.replace("# Replaced")   # overwrite all content
    await page.clear()                  # remove all blocks
```

#### Metadata

```python
    await page.rename("New Title")
    await page.set_icon("🚀")
    await page.set_cover("https://example.com/cover.png")
    await page.random_cover()
```

#### Properties

```python
    await page.properties.set_property("Status", "Done")
    await page.properties.set_property("Due Date", "2025-12-31")
```

#### Comments & Lifecycle

```python
    await page.comment("Review completed")
    await page.lock()
    await page.trash()
```

> **Notion API Reference:** [Pages](https://developers.notion.com/reference/page) · [Markdown](https://developers.notion.com/reference/markdown)

---

### Databases

```python
async with Notionary() as notion:
    # Lookup
    db = await notion.databases.find("Tasks")
    db = await notion.databases.from_id(db_uuid)

    # Create
    db = await notion.databases.create(
        parent_page_id=page_uuid,
        title="New Database",
        icon_emoji="📊",
    )

    # Metadata
    await db.set_title("Project Tracker")
    await db.set_description("All current projects")
    await db.set_icon("📊")
    await db.lock()
```

> **Notion API Reference:** [Databases](https://developers.notion.com/reference/database)

---

### Data Sources

Data sources represent queryable Notion databases with schema awareness — useful for building structured content pipelines.

```python
async with Notionary() as notion:
    ds = await notion.data_sources.find("Engineering Backlog")

    # Schema introspection
    schema = await ds.get_schema()

    # Create a page inside the data source
    page = await ds.create_page(title="New Feature")

    # Query with filters
    results = await ds.query(filter={"property": "Status", "select": {"equals": "In Progress"}})

    # Metadata
    await ds.set_title("Sprint Board")
    await ds.set_icon("🧭")
```

> **Notion API Reference:** [Data Sources](https://developers.notion.com/reference/post-database-query)

---

### File Uploads

```python
from pathlib import Path

async with Notionary() as notion:
    # Upload from disk
    result = await notion.file_uploads.upload(Path("./report.pdf"))

    # Upload from bytes (e.g. generated images, in-memory content)
    result = await notion.file_uploads.upload_from_bytes(
        content=image_bytes,
        filename="chart.png",
    )

    # List previous uploads
    uploads = await notion.file_uploads.list()
```

---

### Users

```python
async with Notionary() as notion:
    all_users = await notion.users.list()
    people    = await notion.users.list(filter="person")
    bots      = await notion.users.list(filter="bot")
    me        = await notion.users.me()

    matches   = await notion.users.search("alex")
```

---

### Workspace Search

```python
async with Notionary() as notion:
    results = await notion.workspace.search(query="roadmap")
    for r in results:
        print(type(r).__name__, r.title)
```

---

## Key Features

### Smart Discovery
- Find pages and databases by human-readable name
- Fuzzy matching handles typos and partial titles
- No more hunting for opaque IDs or copying page URLs

### Markdown Content API
- Read any Notion page as clean Markdown
- Append or replace blocks using Markdown syntax
- Powered by the official [Notion Markdown API](https://developers.notion.com/reference/markdown)

### Round-Trip Editing

```python
# Read → transform → write back
md = await page.get_markdown()
updated = md.replace("Draft", "Final")
await page.replace(updated)
```

### Modern Python
- Full `async` / `await` support throughout
- Type hints on all public APIs
- Pydantic models for all API responses
- Context-manager client for clean resource handling

### AI-Ready Architecture
- Namespace-based API maps naturally to agent tool sets
- Predictable response models enable prompt chaining
- Works with LangChain, LlamaIndex, OpenAI Agents SDK, Claude, and any MCP-compatible host

---

## MCP Server

Notionary ships a built-in [Model Context Protocol](https://modelcontextprotocol.io/) server so any MCP-compatible AI agent can manage your Notion workspace out of the box.

```bash
pip install notionary[mcp]
```

### Claude Desktop / Claude Code

```json
{
  "mcpServers": {
    "notionary": {
      "command": "notionary-mcp",
      "env": {
        "NOTION_API_KEY": "your_integration_key"
      }
    }
  }
}
```

### OpenAI Agents SDK

```python
import asyncio, sys
from agents import Agent, Runner
from agents.mcp import MCPServerStdio

async def main():
    async with MCPServerStdio(
        name="Notionary",
        params={"command": sys.executable, "args": ["-m", "notionary.mcp.server"]},
    ) as server:
        agent = Agent(
            name="Notion Assistant",
            instructions="You help users manage their Notion workspace.",
            mcp_servers=[server],
        )
        result = await Runner.run(agent, "Search my workspace and list what you find.")
        print(result.final_output)

asyncio.run(main())
```

### Available MCP Tools

| Area | Tools |
|------|-------|
| **Workspace** | `search_workspace` |
| **Pages** | `list_pages`, `find_page`, `get_page_content`, `get_page_comments`, `update_page`, `append_to_page`, `replace_page_content`, `clear_page`, `comment_on_page`, `rename_page`, `set_page_property`, `trash_page`, `restore_page`, `lock_page`, `unlock_page` |
| **Data Sources** | `list_data_sources`, `find_data_source`, `get_data_source_schema`, `create_page_in_data_source`, `update_data_source`, `list_data_source_templates`, `trash_data_source`, `restore_data_source` |
| **Databases** | `list_databases`, `find_database`, `create_database`, `update_database`, `trash_database`, `restore_database` |
| **Users** | `list_users`, `search_users`, `get_me` |

---

## Contributing

Contributions are welcome — whether you're fixing bugs, adding features, improving docs, or sharing examples.
Check the [Contributing Guide](CONTRIBUTING.md) to get started.

---

## Documentation

[**mathisarends.github.io/notionary**](https://mathisarends.github.io/notionary/) — Complete API reference auto-generated from source.

---

<div align="center">
Built with ❤️ for Python developers and AI agents
</div>
