notion-mcp-server 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +236 -0
  3. package/build/config/index.js +5 -0
  4. package/build/index.js +17 -0
  5. package/build/resources/imageList.js +62 -0
  6. package/build/resources/index.js +1 -0
  7. package/build/resources/predictionList.js +43 -0
  8. package/build/resources/svgList.js +69 -0
  9. package/build/schema/annotations.js +15 -0
  10. package/build/schema/blocks.js +209 -0
  11. package/build/schema/color.js +22 -0
  12. package/build/schema/file.js +11 -0
  13. package/build/schema/icon.js +10 -0
  14. package/build/schema/lang.js +77 -0
  15. package/build/schema/mention.js +102 -0
  16. package/build/schema/notion.js +57 -0
  17. package/build/schema/page.js +68 -0
  18. package/build/schema/preprocess.js +1 -0
  19. package/build/schema/rich-text.js +71 -0
  20. package/build/schema/richText.js +757 -0
  21. package/build/schema/timezone.js +599 -0
  22. package/build/schema/tools.js +17 -0
  23. package/build/server/index.js +27 -0
  24. package/build/services/notion.js +20 -0
  25. package/build/services/replicate.js +23 -0
  26. package/build/tools/appendBlockChildren.js +25 -0
  27. package/build/tools/batchAppendBlockChildren.js +33 -0
  28. package/build/tools/batchDeleteBlocks.js +32 -0
  29. package/build/tools/batchMixedOperations.js +58 -0
  30. package/build/tools/batchUpdateBlocks.js +33 -0
  31. package/build/tools/createPage.js +18 -0
  32. package/build/tools/createPrediction.js +28 -0
  33. package/build/tools/deleteBlock.js +24 -0
  34. package/build/tools/formatRichText.js +83 -0
  35. package/build/tools/generateImage.js +48 -0
  36. package/build/tools/generateImageVariants.js +105 -0
  37. package/build/tools/generateMultipleImages.js +60 -0
  38. package/build/tools/generateSVG.js +43 -0
  39. package/build/tools/getPrediction.js +22 -0
  40. package/build/tools/index.js +31 -0
  41. package/build/tools/predictionList.js +30 -0
  42. package/build/tools/retrieveBlock.js +24 -0
  43. package/build/tools/retrieveBlockChildren.js +32 -0
  44. package/build/tools/searchPage.js +28 -0
  45. package/build/tools/updateBlock.js +25 -0
  46. package/build/tools/updatePage.js +40 -0
  47. package/build/types/blocks.js +11 -0
  48. package/build/types/emoji.js +1 -0
  49. package/build/types/index.js +1 -0
  50. package/build/types/notion.js +1 -0
  51. package/build/types/page.js +6 -0
  52. package/build/types/richText.js +1 -0
  53. package/build/types/tools.js +1 -0
  54. package/build/utils/blob.js +5 -0
  55. package/build/utils/error.js +127 -0
  56. package/build/utils/image.js +34 -0
  57. package/build/utils/index.js +1 -0
  58. package/build/utils/richText.js +174 -0
  59. package/build/validation/blocks.js +568 -0
  60. package/build/validation/notion.js +51 -0
  61. package/build/validation/page.js +262 -0
  62. package/build/validation/richText.js +744 -0
  63. package/build/validation/tools.js +16 -0
  64. package/package.json +44 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Yaroslav Boiko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,236 @@
1
+ # Notion MCP Server
2
+
3
+ ![MCP Compatible](https://img.shields.io/badge/MCP-Compatible-blue)
4
+ ![License](https://img.shields.io/badge/license-MIT-green)
5
+ ![TypeScript](https://img.shields.io/badge/TypeScript-4.9+-blue)
6
+ ![Model Context Protocol](https://img.shields.io/badge/MCP-Enabled-purple)
7
+
8
+ **Notion MCP Server** is a Model Context Protocol (MCP) server template for integrating with Notion's API. This boilerplate provides a starting point for building AI assistants that can interact with Notion.
9
+
10
+ ## 📑 Table of Contents
11
+
12
+ - [Getting Started & Integration](#-getting-started--integration)
13
+ - [Setup Process](#setup-process)
14
+ - [Cursor Integration](#cursor-integration)
15
+ - [Claude Desktop Integration](#claude-desktop-integration)
16
+ - [Features](#-features)
17
+ - [Documentation](#-documentation)
18
+ - [Available Tools](#available-tools)
19
+ - [Available Resources](#available-resources)
20
+ - [Development](#-development)
21
+ - [Technical Details](#-technical-details)
22
+ - [Troubleshooting](#-troubleshooting)
23
+ - [Contributing](#-contributing)
24
+ - [License](#-license)
25
+
26
+ ## 🚀 Getting Started & Integration
27
+
28
+ ### Setup Process
29
+
30
+ 1. **Obtain a Notion API Key**
31
+ - Create an integration at [Notion Developers](https://www.notion.so/my-integrations)
32
+ - Copy your API key
33
+
34
+ 2. **Enable Integration for Your Pages**
35
+ - Select an existing page or create a new one in Notion
36
+ - Click the "..." menu in the top right corner
37
+ - Go to "Connections"
38
+ - Find and enable your integration from the list
39
+
40
+ ![Notion Page Connection](page_connection.png)
41
+
42
+ 3. **Choose Your Integration Method**
43
+ - Follow one of the integration options below based on your preferred MCP client
44
+
45
+ 4. **Ask Your AI Assistant to Interact with Notion**
46
+ - "Create a new page with today's tasks"
47
+ - "Update my meeting notes in Notion"
48
+ - "Add bullet points to my meeting notes page"
49
+
50
+ ### Cursor Integration
51
+
52
+ #### Method 1: Using mcp.json
53
+
54
+ 1. Create or edit the `.cursor/mcp.json` file in your project directory:
55
+
56
+ ```json
57
+ {
58
+ "mcpServers": {
59
+ "notion-mcp-server": {
60
+ "command": "env NOTION_TOKEN=YOUR_KEY NOTION_PAGE_ID=YOUR_PAGE_ID npx",
61
+ "args": ["-y", "notion-mcp-server"]
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ 2. Replace `YOUR_KEY` and `YOUR_PAGE_ID` with your actual Notion API key and page ID
68
+ 3. Restart Cursor to apply the changes
69
+
70
+ #### Method 2: Manual Mode
71
+
72
+ 1. Open Cursor and go to Settings
73
+ 2. Navigate to the "MCP" or "Model Context Protocol" section
74
+ 3. Click "Add Server" or equivalent
75
+ 4. Enter the following command in the appropriate field:
76
+
77
+ ```
78
+ env NOTION_TOKEN=YOUR_KEY NOTION_PAGE_ID=YOUR_PAGE_ID npx -y notion-mcp-server
79
+ ```
80
+
81
+ 5. Replace `YOUR_KEY` and `YOUR_PAGE_ID` with your actual Notion API key and page ID
82
+ 6. Save the settings and restart Cursor if necessary
83
+
84
+ ### Claude Desktop Integration
85
+
86
+ 1. Create or edit the `mcp.json` file in your configuration directory:
87
+
88
+ ```json
89
+ {
90
+ "mcpServers": {
91
+ "notion-mcp-server": {
92
+ "command": "npx",
93
+ "args": ["-y", "notion-mcp-server"],
94
+ "env": {
95
+ "NOTION_TOKEN": "YOUR_KEY",
96
+ "NOTION_PAGE_ID": "YOUR_PAGE_ID"
97
+ }
98
+ }
99
+ }
100
+ }
101
+ ```
102
+
103
+ 2. Replace `YOUR_KEY` and `YOUR_PAGE_ID` with your actual Notion API key and page ID
104
+ 3. Restart Claude Desktop to apply the changes
105
+
106
+ ## 🌟 Features
107
+
108
+ - **📝 Notion Integration** - Interact with Notion databases, pages, and blocks
109
+ - **🔌 Universal MCP Compatibility** - Works with all MCP clients including Cursor, Claude Desktop, Cline, and Zed
110
+ - **🔍 Data Retrieval** - Fetch information from Notion pages, blocks, and databases
111
+ - **✏️ Content Creation** - Create and update Notion pages and blocks
112
+ - **📊 Block Management** - Append, update, and delete blocks within Notion pages
113
+ - **🔄 Batch Operations** - Perform multiple operations in a single request
114
+ - **🗑️ Archive & Restore** - Archive and restore Notion pages
115
+ - **🔎 Search Functionality** - Search Notion pages and databases by title
116
+
117
+ ## 📚 Documentation
118
+
119
+ ### Available Tools
120
+
121
+ The server provides the following tools for interacting with Notion:
122
+
123
+ #### Page Operations
124
+
125
+ ##### `create_page`
126
+ Create a new page in Notion with specified content
127
+
128
+ ##### `archive_page`
129
+ Archive (move to trash) a Notion page by ID
130
+
131
+ ##### `restore_page`
132
+ Restore a previously archived Notion page by ID
133
+
134
+ ##### `search_pages`
135
+ Search for pages and databases in Notion by title
136
+
137
+ #### Block Operations
138
+
139
+ ##### `retrieve_block`
140
+ Retrieve a block from Notion by ID
141
+
142
+ ##### `retrieve_block_children`
143
+ Retrieve the children of a block from Notion
144
+
145
+ ##### `append_block_children`
146
+ Append child blocks to a parent block in Notion
147
+
148
+ ##### `update_block`
149
+ Update a block's content in Notion
150
+
151
+ ##### `delete_block`
152
+ Delete (move to trash) a block in Notion
153
+
154
+ #### Batch Operations
155
+
156
+ ##### `batch_append_block_children`
157
+ Append children to multiple blocks in a single operation
158
+
159
+ ##### `batch_update_blocks`
160
+ Update multiple blocks in a single operation
161
+
162
+ ##### `batch_delete_blocks`
163
+ Delete multiple blocks in a single operation
164
+
165
+ ##### `batch_mixed_operations`
166
+ Perform a mix of append, update, and delete operations in a single request
167
+
168
+ ### Available Resources
169
+
170
+ The server currently does not expose any resources, focusing instead on tool-based operations.
171
+
172
+ ## 🛠 Development
173
+
174
+ 1. **Clone the Repository**
175
+ ```
176
+ git clone https://github.com/awkoy/notion-mcp-server.git
177
+ cd notion-mcp-server
178
+ ```
179
+
180
+ 2. **Install Dependencies**
181
+ ```
182
+ npm install
183
+ ```
184
+
185
+ 3. **Set Up Environment Variables**
186
+ - Create a `.env` file with:
187
+ ```
188
+ NOTION_TOKEN=your_notion_api_key
189
+ NOTION_PAGE_ID=your_notion_page_id
190
+ ```
191
+
192
+ 4. **Build the Project**
193
+ ```
194
+ npm run build
195
+ ```
196
+
197
+ 5. **Run the Inspector**
198
+ ```
199
+ npm run inspector
200
+ ```
201
+
202
+ ## 🔧 Technical Details
203
+
204
+ - Built using TypeScript and the MCP SDK (version 1.7.0+)
205
+ - Uses the official Notion API client (@notionhq/client v2.3.0+)
206
+ - Follows the Model Context Protocol specification
207
+ - Implements tools for CRUD operations on Notion pages and blocks
208
+ - Supports efficient batch operations for performance optimization
209
+ - Validates input/output with Zod schemas
210
+
211
+ ## ❓ Troubleshooting
212
+
213
+ - **Common Issues**
214
+ - **Authentication Errors**: Ensure your Notion token has the correct permissions and integration is enabled for your pages/databases
215
+ - **Page Access Issues**: Make sure your integration has been added to the pages you're attempting to access
216
+ - **Rate Limiting**: Notion API has rate limits - use batch operations to optimize requests
217
+
218
+ - **Getting Help**
219
+ - Create an issue on the [GitHub repository](https://github.com/awkoy/notion-mcp-server/issues)
220
+ - Check the [Notion API documentation](https://developers.notion.com/reference/intro)
221
+ - Visit the MCP community channels for assistance
222
+
223
+ ## 🤝 Contributing
224
+
225
+ Contributions are welcome! Please feel free to submit a Pull Request.
226
+
227
+ 1. Fork the repository
228
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
229
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
230
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
231
+ 5. Open a Pull Request
232
+
233
+ ## 📄 License
234
+
235
+ This project is licensed under the MIT License - see the LICENSE file for details.
236
+
@@ -0,0 +1,5 @@
1
+ // Configuration
2
+ export const CONFIG = {
3
+ serverName: "notion-mcp-server",
4
+ serverVersion: "0.0.1",
5
+ };
package/build/index.js ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ import { startServer } from "./server/index.js";
3
+ import { registerAllTools } from "./tools/index.js";
4
+ registerAllTools();
5
+ async function main() {
6
+ try {
7
+ await startServer();
8
+ }
9
+ catch (error) {
10
+ console.error("Unhandled server error:", error instanceof Error ? error.message : String(error));
11
+ process.exit(1);
12
+ }
13
+ }
14
+ main().catch((error) => {
15
+ console.error("Unhandled server error:", error instanceof Error ? error.message : String(error));
16
+ process.exit(1);
17
+ });
@@ -0,0 +1,62 @@
1
+ import { server } from "../server/index.js";
2
+ import { ResourceTemplate, } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { replicate } from "../services/replicate.js";
4
+ import { urlToBase64 } from "../utils/image.js";
5
+ import { CONFIG } from "../config/index.js";
6
+ export const registerImageListResource = () => {
7
+ const list = async () => {
8
+ try {
9
+ const predictions = [];
10
+ for await (const page of replicate.paginate(replicate.predictions.list)) {
11
+ predictions.push(...page);
12
+ }
13
+ return {
14
+ resources: predictions
15
+ .filter((prediction) => prediction.output?.length &&
16
+ prediction.model === CONFIG.imageModelId)
17
+ .map((prediction) => ({
18
+ uri: `images://${prediction.id}`,
19
+ name: `Image ${prediction.id}`,
20
+ mimeType: "application/json",
21
+ description: `Generated image by ${prediction.model} with id ${prediction.id}`,
22
+ })),
23
+ nextCursor: undefined,
24
+ };
25
+ }
26
+ catch (error) {
27
+ console.error("Error listing predictions:", error);
28
+ return {
29
+ resources: [],
30
+ nextCursor: undefined,
31
+ };
32
+ }
33
+ };
34
+ server.resource("images", new ResourceTemplate("images://{id}", {
35
+ list,
36
+ }), async (uri, { id }) => {
37
+ const prediction = await replicate.predictions.get(id);
38
+ if (!prediction.output?.length) {
39
+ return {
40
+ contents: [
41
+ {
42
+ name: "Not Found!",
43
+ uri: uri.href,
44
+ text: `Data has been removed by Replicate automatically after an hour, by default. You have to save your own copies before it is removed.`,
45
+ mimeType: "text/plain",
46
+ },
47
+ ],
48
+ };
49
+ }
50
+ const imageBase64 = await urlToBase64(prediction.output[0]);
51
+ return {
52
+ contents: [
53
+ {
54
+ name: "image",
55
+ uri: uri.href,
56
+ blob: imageBase64,
57
+ mimeType: "image/png",
58
+ },
59
+ ],
60
+ };
61
+ });
62
+ };
@@ -0,0 +1 @@
1
+ export const registerAllResources = () => { };
@@ -0,0 +1,43 @@
1
+ import { server } from "../server/index.js";
2
+ import { ResourceTemplate, } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { replicate } from "../services/replicate.js";
4
+ export const registerPreditionListResource = () => {
5
+ const list = async () => {
6
+ try {
7
+ const predictions = [];
8
+ for await (const page of replicate.paginate(replicate.predictions.list)) {
9
+ predictions.push(...page);
10
+ }
11
+ return {
12
+ resources: predictions.map((prediction) => ({
13
+ uri: `predictions://${prediction.id}`,
14
+ name: `Prediction ${prediction.id}`,
15
+ mimeType: "application/json",
16
+ })),
17
+ nextCursor: undefined,
18
+ };
19
+ }
20
+ catch (error) {
21
+ console.error("Error listing predictions:", error);
22
+ return {
23
+ resources: [],
24
+ nextCursor: undefined,
25
+ };
26
+ }
27
+ };
28
+ server.resource("predictions", new ResourceTemplate("predictions://{id}", {
29
+ list,
30
+ }), async (uri, { id }) => {
31
+ const prediction = await replicate.predictions.get(id);
32
+ return {
33
+ contents: [
34
+ {
35
+ name: "prediction",
36
+ uri: uri.href,
37
+ text: JSON.stringify(prediction),
38
+ mimeType: "application/json",
39
+ },
40
+ ],
41
+ };
42
+ });
43
+ };
@@ -0,0 +1,69 @@
1
+ // import { server } from "../server/index.js";
2
+ // import {
3
+ // ListResourcesCallback,
4
+ // ResourceTemplate,
5
+ // } from "@modelcontextprotocol/sdk/server/mcp.js";
6
+ // import { replicate } from "../services/notion.js";
7
+ // import { urlToSvg } from "../utils/image.js";
8
+ // import { Prediction } from "replicate";
9
+ // import { CONFIG } from "../config/index.js";
10
+ export {};
11
+ // export const registerSvgListResource = () => {
12
+ // const list: ListResourcesCallback = async () => {
13
+ // try {
14
+ // const predictions: Prediction[] = [];
15
+ // for await (const page of replicate.paginate(replicate.predictions.list)) {
16
+ // predictions.push(...page);
17
+ // }
18
+ // return {
19
+ // resources: predictions
20
+ // .filter((prediction) => prediction.model === CONFIG.svgModelId)
21
+ // .map((prediction) => ({
22
+ // uri: `svglist://${prediction.id}`,
23
+ // name: `SVG ${prediction.id}`,
24
+ // mimeType: "application/json",
25
+ // description: `Generated image by ${prediction.model} with id ${prediction.id}`,
26
+ // })),
27
+ // nextCursor: undefined,
28
+ // };
29
+ // } catch (error) {
30
+ // console.error("Error listing predictions:", error);
31
+ // return {
32
+ // resources: [],
33
+ // nextCursor: undefined,
34
+ // };
35
+ // }
36
+ // };
37
+ // server.resource(
38
+ // "svglist",
39
+ // new ResourceTemplate("svglist://{id}", {
40
+ // list,
41
+ // }),
42
+ // async (uri, { id }) => {
43
+ // const prediction = await replicate.predictions.get(id as string);
44
+ // if (!prediction.output) {
45
+ // return {
46
+ // contents: [
47
+ // {
48
+ // name: "Not Found!",
49
+ // uri: uri.href,
50
+ // text: `Data has been removed by Replicate automatically after an hour, by default. You have to save your own copies before it is removed.`,
51
+ // mimeType: "text/plain",
52
+ // },
53
+ // ],
54
+ // };
55
+ // }
56
+ // const svg = await urlToSvg(prediction.output);
57
+ // return {
58
+ // contents: [
59
+ // {
60
+ // name: "svglist",
61
+ // uri: uri.href,
62
+ // text: svg,
63
+ // mimeType: "image/svg+xml",
64
+ // },
65
+ // ],
66
+ // };
67
+ // }
68
+ // );
69
+ // };
@@ -0,0 +1,15 @@
1
+ import { z } from "zod";
2
+ import { COLOR_SCHEMA } from "./color.js";
3
+ export const ANNOTATIONS_SCHEMA = z
4
+ .object({
5
+ bold: z.boolean().optional().describe("Whether text is bold"),
6
+ italic: z.boolean().optional().describe("Whether text is italic"),
7
+ strikethrough: z
8
+ .boolean()
9
+ .optional()
10
+ .describe("Whether text has strikethrough"),
11
+ underline: z.boolean().optional().describe("Whether text is underlined"),
12
+ code: z.boolean().optional().describe("Whether text is code formatted"),
13
+ color: COLOR_SCHEMA.optional().describe("Color of the text"),
14
+ })
15
+ .describe("Text formatting annotations");
@@ -0,0 +1,209 @@
1
+ import { z } from "zod";
2
+ import { COLOR_SCHEMA } from "./color.js";
3
+ import { ICON_SCHEMA } from "./icon.js";
4
+ import { RICH_TEXT_ITEM_REQUEST_SCHEMA } from "./rich-text.js";
5
+ import { preprocessJson } from "./preprocess.js";
6
+ import { LANGUAGE_SCHEMA } from "./lang.js";
7
+ import { FILE_SCHEMA } from "./file.js";
8
+ export const BASE_BLOCK_REQUEST_SCHEMA = z.object({
9
+ type: z.string().describe("Type of block"),
10
+ object: z.literal("block").optional().describe("Object type identifier"),
11
+ created_time: z
12
+ .string()
13
+ .optional()
14
+ .describe("ISO timestamp of block creation"),
15
+ last_edited_time: z
16
+ .string()
17
+ .optional()
18
+ .describe("ISO timestamp of last edit"),
19
+ has_children: z
20
+ .boolean()
21
+ .optional()
22
+ .describe("Whether block has child blocks"),
23
+ archived: z.boolean().optional().describe("Whether block is archived"),
24
+ });
25
+ export const TEXT_BLOCK_BASE_REQUEST_SCHEMA = z.object({
26
+ rich_text: z
27
+ .array(RICH_TEXT_ITEM_REQUEST_SCHEMA)
28
+ .describe("Array of rich text content"),
29
+ color: COLOR_SCHEMA.optional().describe("Color of the block"),
30
+ });
31
+ export const PARAGRAPH_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({
32
+ type: z.literal("paragraph").describe("Paragraph block type"),
33
+ paragraph: TEXT_BLOCK_BASE_REQUEST_SCHEMA.describe("Paragraph block content"),
34
+ });
35
+ export const HEADING1_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({
36
+ type: z.literal("heading_1").describe("Heading 1 block type"),
37
+ heading_1: TEXT_BLOCK_BASE_REQUEST_SCHEMA.extend({
38
+ is_toggleable: z
39
+ .boolean()
40
+ .optional()
41
+ .describe("Whether heading can be toggled"),
42
+ }).describe("Heading 1 block content"),
43
+ });
44
+ export const HEADING2_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({
45
+ type: z.literal("heading_2").describe("Heading 2 block type"),
46
+ heading_2: TEXT_BLOCK_BASE_REQUEST_SCHEMA.extend({
47
+ is_toggleable: z
48
+ .boolean()
49
+ .optional()
50
+ .describe("Whether heading can be toggled"),
51
+ }).describe("Heading 2 block content"),
52
+ });
53
+ export const HEADING3_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({
54
+ type: z.literal("heading_3").describe("Heading 3 block type"),
55
+ heading_3: TEXT_BLOCK_BASE_REQUEST_SCHEMA.extend({
56
+ is_toggleable: z
57
+ .boolean()
58
+ .optional()
59
+ .describe("Whether heading can be toggled"),
60
+ }).describe("Heading 3 block content"),
61
+ });
62
+ export const QUOTE_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({
63
+ type: z.literal("quote").describe("Quote block type"),
64
+ quote: TEXT_BLOCK_BASE_REQUEST_SCHEMA.describe("Quote block content"),
65
+ });
66
+ export const CALLOUT_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({
67
+ type: z.literal("callout").describe("Callout block type"),
68
+ callout: TEXT_BLOCK_BASE_REQUEST_SCHEMA.extend({
69
+ icon: ICON_SCHEMA.optional().describe("Icon for the callout"),
70
+ }).describe("Callout block content"),
71
+ });
72
+ export const TOGGLE_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({
73
+ type: z.literal("toggle").describe("Toggle block type"),
74
+ toggle: TEXT_BLOCK_BASE_REQUEST_SCHEMA.describe("Toggle block content"),
75
+ });
76
+ export const BULLETED_LIST_ITEM_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({
77
+ type: z
78
+ .literal("bulleted_list_item")
79
+ .describe("Bulleted list item block type"),
80
+ bulleted_list_item: TEXT_BLOCK_BASE_REQUEST_SCHEMA.describe("Bulleted list item block content"),
81
+ });
82
+ export const NUMBERED_LIST_ITEM_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({
83
+ type: z
84
+ .literal("numbered_list_item")
85
+ .describe("Numbered list item block type"),
86
+ numbered_list_item: TEXT_BLOCK_BASE_REQUEST_SCHEMA.describe("Numbered list item block content"),
87
+ });
88
+ export const TO_DO_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({
89
+ type: z.literal("to_do").describe("To-do block type"),
90
+ to_do: TEXT_BLOCK_BASE_REQUEST_SCHEMA.extend({
91
+ checked: z.boolean().optional().describe("Whether the to-do is checked"),
92
+ }).describe("To-do block content"),
93
+ });
94
+ export const CODE_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({
95
+ type: z.literal("code").describe("Code block type"),
96
+ code: TEXT_BLOCK_BASE_REQUEST_SCHEMA.extend({
97
+ language: LANGUAGE_SCHEMA,
98
+ }).describe("Code block content"),
99
+ });
100
+ export const DIVIDER_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({
101
+ type: z.literal("divider").describe("Divider block type"),
102
+ divider: z.object({}).describe("Divider block content"),
103
+ });
104
+ export const IMAGE_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({
105
+ type: z.literal("image").describe("Image block type"),
106
+ image: z
107
+ .object({
108
+ ...FILE_SCHEMA.shape,
109
+ caption: z
110
+ .array(RICH_TEXT_ITEM_REQUEST_SCHEMA)
111
+ .optional()
112
+ .describe("Image caption"),
113
+ })
114
+ .describe("Image block content"),
115
+ });
116
+ export const TEXT_BLOCK_REQUEST_SCHEMA = z.preprocess(preprocessJson, z
117
+ .discriminatedUnion("type", [
118
+ PARAGRAPH_BLOCK_REQUEST_SCHEMA,
119
+ HEADING1_BLOCK_REQUEST_SCHEMA,
120
+ HEADING2_BLOCK_REQUEST_SCHEMA,
121
+ HEADING3_BLOCK_REQUEST_SCHEMA,
122
+ QUOTE_BLOCK_REQUEST_SCHEMA,
123
+ CALLOUT_BLOCK_REQUEST_SCHEMA,
124
+ TOGGLE_BLOCK_REQUEST_SCHEMA,
125
+ BULLETED_LIST_ITEM_BLOCK_REQUEST_SCHEMA,
126
+ NUMBERED_LIST_ITEM_BLOCK_REQUEST_SCHEMA,
127
+ TO_DO_BLOCK_REQUEST_SCHEMA,
128
+ CODE_BLOCK_REQUEST_SCHEMA,
129
+ DIVIDER_BLOCK_REQUEST_SCHEMA,
130
+ IMAGE_BLOCK_REQUEST_SCHEMA,
131
+ ])
132
+ .describe("Union of all possible text block request types"));
133
+ export const APPEND_BLOCK_CHILDREN_SCHEMA = {
134
+ blockId: z.string().describe("The ID of the block to append children to"),
135
+ children: z
136
+ .array(TEXT_BLOCK_REQUEST_SCHEMA)
137
+ .describe("Array of blocks to append as children"),
138
+ };
139
+ export const RETRIEVE_BLOCK_SCHEMA = {
140
+ blockId: z.string().describe("The ID of the block to retrieve"),
141
+ };
142
+ export const RETRIEVE_BLOCK_CHILDREN_SCHEMA = {
143
+ blockId: z.string().describe("The ID of the block to retrieve children for"),
144
+ start_cursor: z.string().optional().describe("Cursor for pagination"),
145
+ page_size: z
146
+ .number()
147
+ .min(1)
148
+ .max(100)
149
+ .optional()
150
+ .describe("Number of results to return (1-100)"),
151
+ };
152
+ export const UPDATE_BLOCK_SCHEMA = {
153
+ blockId: z.string().describe("The ID of the block to update"),
154
+ data: TEXT_BLOCK_REQUEST_SCHEMA.describe("The block data to update"),
155
+ };
156
+ export const DELETE_BLOCK_SCHEMA = {
157
+ blockId: z.string().describe("The ID of the block to delete/archive"),
158
+ };
159
+ // Batch operation schemas for multiple blocks
160
+ export const BATCH_APPEND_BLOCK_CHILDREN_SCHEMA = {
161
+ operations: z
162
+ .array(z.object({
163
+ blockId: z
164
+ .string()
165
+ .describe("The ID of the block to append children to"),
166
+ children: z
167
+ .array(TEXT_BLOCK_REQUEST_SCHEMA)
168
+ .describe("Array of blocks to append as children"),
169
+ }))
170
+ .describe("Array of append operations to perform in a single batch"),
171
+ };
172
+ export const BATCH_UPDATE_BLOCKS_SCHEMA = {
173
+ operations: z
174
+ .array(z.object({
175
+ blockId: z.string().describe("The ID of the block to update"),
176
+ data: TEXT_BLOCK_REQUEST_SCHEMA.describe("The block data to update"),
177
+ }))
178
+ .describe("Array of update operations to perform in a single batch"),
179
+ };
180
+ export const BATCH_DELETE_BLOCKS_SCHEMA = {
181
+ blockIds: z
182
+ .array(z.string().describe("The ID of a block to delete/archive"))
183
+ .describe("Array of block IDs to delete in a single batch"),
184
+ };
185
+ // Schema for multi-operation batches (mixed operations)
186
+ export const BATCH_MIXED_OPERATIONS_SCHEMA = {
187
+ operations: z
188
+ .array(z.discriminatedUnion("operation", [
189
+ z.object({
190
+ operation: z.literal("append"),
191
+ blockId: z
192
+ .string()
193
+ .describe("The ID of the block to append children to"),
194
+ children: z
195
+ .array(TEXT_BLOCK_REQUEST_SCHEMA)
196
+ .describe("Array of blocks to append as children"),
197
+ }),
198
+ z.object({
199
+ operation: z.literal("update"),
200
+ blockId: z.string().describe("The ID of the block to update"),
201
+ data: TEXT_BLOCK_REQUEST_SCHEMA.describe("The block data to update"),
202
+ }),
203
+ z.object({
204
+ operation: z.literal("delete"),
205
+ blockId: z.string().describe("The ID of the block to delete/archive"),
206
+ }),
207
+ ]))
208
+ .describe("Array of mixed operations to perform in a single batch"),
209
+ };