@n24q02m/better-notion-mcp 1.0.0

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 (69) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +240 -0
  3. package/bin/cli.mjs +117 -0
  4. package/build/scripts/start-server.d.ts +6 -0
  5. package/build/scripts/start-server.d.ts.map +1 -0
  6. package/build/scripts/start-server.js +21 -0
  7. package/build/scripts/start-server.js.map +1 -0
  8. package/build/src/init-server.d.ts +29 -0
  9. package/build/src/init-server.d.ts.map +1 -0
  10. package/build/src/init-server.js +32 -0
  11. package/build/src/init-server.js.map +1 -0
  12. package/build/src/tools/composite/blocks.d.ts +16 -0
  13. package/build/src/tools/composite/blocks.d.ts.map +1 -0
  14. package/build/src/tools/composite/blocks.js +104 -0
  15. package/build/src/tools/composite/blocks.js.map +1 -0
  16. package/build/src/tools/composite/comments.d.ts +16 -0
  17. package/build/src/tools/composite/comments.d.ts.map +1 -0
  18. package/build/src/tools/composite/comments.js +69 -0
  19. package/build/src/tools/composite/comments.js.map +1 -0
  20. package/build/src/tools/composite/content.d.ts +13 -0
  21. package/build/src/tools/composite/content.d.ts.map +1 -0
  22. package/build/src/tools/composite/content.js +50 -0
  23. package/build/src/tools/composite/content.js.map +1 -0
  24. package/build/src/tools/composite/databases.d.ts +33 -0
  25. package/build/src/tools/composite/databases.d.ts.map +1 -0
  26. package/build/src/tools/composite/databases.js +419 -0
  27. package/build/src/tools/composite/databases.js.map +1 -0
  28. package/build/src/tools/composite/pages.d.ts +24 -0
  29. package/build/src/tools/composite/pages.d.ts.map +1 -0
  30. package/build/src/tools/composite/pages.js +316 -0
  31. package/build/src/tools/composite/pages.js.map +1 -0
  32. package/build/src/tools/composite/search.d.ts +23 -0
  33. package/build/src/tools/composite/search.d.ts.map +1 -0
  34. package/build/src/tools/composite/search.js +94 -0
  35. package/build/src/tools/composite/search.js.map +1 -0
  36. package/build/src/tools/composite/users.d.ts +15 -0
  37. package/build/src/tools/composite/users.d.ts.map +1 -0
  38. package/build/src/tools/composite/users.js +93 -0
  39. package/build/src/tools/composite/users.js.map +1 -0
  40. package/build/src/tools/composite/workspace.d.ts +25 -0
  41. package/build/src/tools/composite/workspace.d.ts.map +1 -0
  42. package/build/src/tools/composite/workspace.js +72 -0
  43. package/build/src/tools/composite/workspace.js.map +1 -0
  44. package/build/src/tools/helpers/errors.d.ts +43 -0
  45. package/build/src/tools/helpers/errors.d.ts.map +1 -0
  46. package/build/src/tools/helpers/errors.js +162 -0
  47. package/build/src/tools/helpers/errors.js.map +1 -0
  48. package/build/src/tools/helpers/markdown.d.ts +45 -0
  49. package/build/src/tools/helpers/markdown.d.ts.map +1 -0
  50. package/build/src/tools/helpers/markdown.js +320 -0
  51. package/build/src/tools/helpers/markdown.js.map +1 -0
  52. package/build/src/tools/helpers/pagination.d.ts +42 -0
  53. package/build/src/tools/helpers/pagination.d.ts.map +1 -0
  54. package/build/src/tools/helpers/pagination.js +72 -0
  55. package/build/src/tools/helpers/pagination.js.map +1 -0
  56. package/build/src/tools/helpers/properties.d.ts +10 -0
  57. package/build/src/tools/helpers/properties.d.ts.map +1 -0
  58. package/build/src/tools/helpers/properties.js +57 -0
  59. package/build/src/tools/helpers/properties.js.map +1 -0
  60. package/build/src/tools/helpers/richtext.d.ts +85 -0
  61. package/build/src/tools/helpers/richtext.d.ts.map +1 -0
  62. package/build/src/tools/helpers/richtext.js +146 -0
  63. package/build/src/tools/helpers/richtext.js.map +1 -0
  64. package/build/src/tools/registry.d.ts +10 -0
  65. package/build/src/tools/registry.d.ts.map +1 -0
  66. package/build/src/tools/registry.js +342 -0
  67. package/build/src/tools/registry.js.map +1 -0
  68. package/build/tsconfig.tsbuildinfo +1 -0
  69. package/package.json +71 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 n24q02m
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,240 @@
1
+ # Better Notion MCP
2
+
3
+ **Composite MCP Server for Notion - Human-Like Workflows for AI Agents**
4
+
5
+ [![npm version](https://badge.fury.io/js/%40n24q02m%2Fbetter-notion-mcp.svg)](https://www.npmjs.com/package/@n24q02m/better-notion-mcp)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![Docker](https://img.shields.io/docker/v/n24q02m/better-notion-mcp?label=docker)](https://hub.docker.com/r/n24q02m/better-notion-mcp)
8
+ [![GitHub stars](https://img.shields.io/github/stars/n24q02m/better-notion-mcp)](https://github.com/n24q02m/better-notion-mcp/stargazers)
9
+
10
+ ## šŸŽÆ Design Philosophy
11
+
12
+ **Mission**: Enable AI agents to work with Notion using **human-like workflows** while maintaining **near-complete API coverage** with **minimal tools and token usage**.
13
+
14
+ This MCP server transforms Notion's 28+ atomic REST API endpoints into **7 mega action-based tools** that mirror how humans actually work with Notion:
15
+
16
+ 1. **Action-Based Design**: Each tool supports multiple related actions (e.g., pages tool: create, get, update, archive)
17
+ 2. **Markdown-First**: Natural language content format optimized for AI understanding
18
+ 3. **Auto-Pagination**: Transparent handling of large datasets
19
+ 4. **Bulk Operations**: Process multiple items efficiently in one request
20
+ 5. **Safe-by-Default**: Only safe operations exposed (no risky schema updates)
21
+
22
+ ## ✨ Key Features
23
+
24
+ - **7 Mega Tools**: 75% Official API coverage (21/28 endpoints) with safe operations only
25
+ - **25+ Actions**: Multiple actions per tool for comprehensive functionality
26
+ - **Markdown Support**: Write and read Notion content in markdown format
27
+ - **Auto-Pagination**: Automatically fetch all results without cursor management
28
+ - **Bulk Operations**: Create/update/delete multiple items in single requests
29
+ - **Simple Deployment**: npx one-liner or Docker container
30
+ - **Safe-by-Default**: Risky operations (database schema updates) intentionally excluded
31
+
32
+ ## šŸš€ Quick Start
33
+
34
+ ### NPX
35
+
36
+ ```json
37
+ {
38
+ "mcpServers": {
39
+ "notion": {
40
+ "command": "npx",
41
+ "args": ["@n24q02m/better-notion-mcp"],
42
+ "env": {
43
+ "NOTION_TOKEN": "your-notion-token-here"
44
+ }
45
+ }
46
+ }
47
+ }
48
+ ```
49
+
50
+ ### Docker
51
+
52
+ ```json
53
+ {
54
+ "mcpServers": {
55
+ "notion": {
56
+ "command": "docker",
57
+ "args": [
58
+ "run", "--rm", "-i",
59
+ "-e", "NOTION_TOKEN=your-notion-token-here",
60
+ "n24q02m/better-notion-mcp:latest"
61
+ ]
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ ## šŸ“š Documentation
68
+
69
+ - **[Changelog](CHANGELOG.md)** - Version history and changes
70
+ - **[Security Policy](SECURITY.md)** - How to report security vulnerabilities
71
+
72
+ ## šŸ”‘ Get Notion Token
73
+
74
+ 1. Visit <https://www.notion.so/my-integrations>
75
+ 2. Click "New integration"
76
+ 3. Name it and select your workspace
77
+ 4. Copy the **Internal Integration Token**
78
+ 5. Share pages/databases with your integration
79
+
80
+ ## šŸ› ļø 7 Mega Action-Based Tools
81
+
82
+ Each tool supports multiple actions, mapping to 21+ Official Notion API endpoints.
83
+
84
+ ### 1. **`pages`** - Complete page lifecycle (6 actions → 5 API endpoints)
85
+
86
+ **Actions**: `create`, `get`, `update`, `archive`, `restore`, `duplicate`
87
+
88
+ - **create**: Create page with title + markdown content + properties in one call
89
+ - Maps to: `POST /v1/pages` + `PATCH /v1/blocks/{id}/children`
90
+ - Example: `{action: "create", title: "My Page", parent_id: "xxx", content: "# Hello\nMarkdown here"}`
91
+
92
+ - **get**: Retrieve full page as markdown with all properties
93
+ - Maps to: `GET /v1/pages/{id}` + `GET /v1/blocks/{id}/children`
94
+ - Example: `{action: "get", page_id: "xxx"}`
95
+
96
+ - **update**: Update title, properties, and/or content (replace/append/prepend)
97
+ - Maps to: `PATCH /v1/pages/{id}` + `PATCH /v1/blocks/{id}/children`
98
+ - Example: `{action: "update", page_id: "xxx", title: "New Title", append_content: "\n## New section"}`
99
+
100
+ - **archive/restore**: Bulk archive or restore multiple pages
101
+ - Maps to: Multiple `PATCH /v1/pages/{id}` calls
102
+ - Example: `{action: "archive", page_ids: ["xxx", "yyy"]}`
103
+
104
+ - **duplicate**: Duplicate a page with all content
105
+ - Example: `{action: "duplicate", page_id: "xxx"}`
106
+
107
+ ### 2. **`databases`** - Database management (6 actions → 3 API endpoints)
108
+
109
+ **Actions**: `create`, `get`, `query`, `create_page`, `update_page`, `delete_page`
110
+
111
+ - **create**: Create database with full schema definition
112
+ - Maps to: `POST /v1/databases`
113
+ - Example: `{action: "create", parent_id: "xxx", title: "Tasks", properties: {Status: {select: {...}}}}`
114
+
115
+ - **get**: Retrieve database schema and structure
116
+ - Maps to: `GET /v1/databases/{id}`
117
+ - Example: `{action: "get", database_id: "xxx"}`
118
+
119
+ - **query**: Query database with filters/sorts + smart search
120
+ - Maps to: `POST /v1/databases/{id}/query`
121
+ - Example: `{action: "query", database_id: "xxx", search: "project", limit: 10}`
122
+
123
+ - **create_page/update_page/delete_page**: Bulk operations on database items
124
+ - Maps to: `POST/PATCH /v1/pages` (database items are pages)
125
+ - Example: `{action: "create_page", database_id: "xxx", pages: [{properties: {...}}]}`
126
+
127
+ ### 3. **`blocks`** - Granular block editing (5 actions → 5 API endpoints)
128
+
129
+ **Actions**: `get`, `children`, `append`, `update`, `delete`
130
+
131
+ - Maps to: `GET/PATCH/DELETE /v1/blocks/{id}` + `GET/PATCH /v1/blocks/{id}/children`
132
+ - Example: `{action: "append", block_id: "xxx", content: "## New section\nContent here"}`
133
+
134
+ ### 4. **`users`** - User management (3 actions → 3 API endpoints)
135
+
136
+ **Actions**: `list`, `get`, `me`
137
+
138
+ - Maps to: `GET /v1/users`, `GET /v1/users/{id}`, `GET /v1/users/me`
139
+ - Example: `{action: "list"}` or `{action: "get", user_id: "xxx"}` or `{action: "me"}`
140
+
141
+ ### 5. **`workspace`** - Workspace operations (2 actions → 2 API endpoints)
142
+
143
+ **Actions**: `info`, `search`
144
+
145
+ - **info**: Get bot and workspace information
146
+ - Maps to: `GET /v1/users/me`
147
+ - Example: `{action: "info"}`
148
+
149
+ - **search**: Smart workspace-wide search with filters
150
+ - Maps to: `POST /v1/search`
151
+ - Example: `{action: "search", query: "project", filter: {object: "page"}, limit: 10}`
152
+
153
+ ### 6. **`comments`** - Comment operations (2 actions → 2 API endpoints)
154
+
155
+ **Actions**: `list`, `create`
156
+
157
+ - Maps to: `GET /v1/comments`, `POST /v1/comments`
158
+ - Example: `{action: "list", page_id: "xxx"}` or `{action: "create", page_id: "xxx", content: "Great!"}`
159
+
160
+ ### 7. **`content_convert`** - Markdown ↔ Notion blocks utility
161
+
162
+ **Utility tool** for converting between formats (not a direct API call)
163
+
164
+ - Example: `{direction: "markdown-to-blocks", content: "# Hello\nWorld"}`
165
+
166
+ ## šŸ”§ Development
167
+
168
+ ### Prerequisites
169
+
170
+ - Node.js 22+ (uses native fetch)
171
+ - npm 7+
172
+ - Notion Integration with appropriate permissions
173
+
174
+ ### Build from Source
175
+
176
+ ```bash
177
+ git clone https://github.com/n24q02m/better-notion-mcp
178
+ cd better-notion-mcp
179
+ npm install
180
+ npm run build
181
+ ```
182
+
183
+ ### Local Development
184
+
185
+ ```bash
186
+ # Run with your Notion token
187
+ NOTION_TOKEN=secret_xxx npm run dev
188
+
189
+ # Or use environment variable
190
+ export NOTION_TOKEN=secret_xxx
191
+ npm run dev
192
+ ```
193
+
194
+ ### Run Tests
195
+
196
+ ```bash
197
+ # Run all tests
198
+ npm test
199
+
200
+ # Watch mode
201
+ npm run test:watch
202
+
203
+ # With coverage
204
+ npm run test:coverage
205
+ ```
206
+
207
+ ### Docker Development
208
+
209
+ ```bash
210
+ # Build image
211
+ npm run docker:build
212
+
213
+ # Run container
214
+ npm run docker:run
215
+ ```
216
+
217
+ ## šŸ“„ License
218
+
219
+ MIT License - See [LICENSE](LICENSE)
220
+
221
+ ## šŸ™ Acknowledgments
222
+
223
+ - **Built With**:
224
+ - [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/sdk) - MCP framework
225
+ - [@notionhq/client](https://github.com/makenotion/notion-sdk-js) - Official Notion SDK
226
+
227
+ ## šŸ“ž Support & Community
228
+
229
+ - šŸ› [Report Bugs](https://github.com/n24q02m/better-notion-mcp/issues/new?template=bug_report.yml)
230
+ - šŸ’” [Request Features](https://github.com/n24q02m/better-notion-mcp/issues/new?template=feature_request.yml)
231
+ - ⭐ [Star this repo](https://github.com/n24q02m/better-notion-mcp) if you find it useful!
232
+
233
+ ## šŸ”— Links
234
+
235
+ - [npm Package](https://www.npmjs.com/package/@n24q02m/better-notion-mcp)
236
+ - [Docker Hub](https://hub.docker.com/r/n24q02m/better-notion-mcp)
237
+ - [GitHub Repository](https://github.com/n24q02m/better-notion-mcp)
238
+ - [Notion API Documentation](https://developers.notion.com)
239
+ - [Model Context Protocol](https://modelcontextprotocol.io)
240
+
package/bin/cli.mjs ADDED
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'module';const require = createRequire(import.meta.url);
3
+ import{Server as pe}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as ue}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as ce,ListToolsRequestSchema as de}from"@modelcontextprotocol/sdk/types.js";import{Client as le}from"@notionhq/client";var c=class extends Error{constructor(t,r,o,i){super(t);this.code=r;this.suggestion=o;this.details=i;this.name="NotionMCPError"}toJSON(){return{error:this.name,code:this.code,message:this.message,suggestion:this.suggestion,details:this.details}}};function M(a){return a.code?B(a):a.message?.includes("ECONNREFUSED")||a.message?.includes("ENOTFOUND")?new c("Cannot connect to Notion API","NETWORK_ERROR","Check your internet connection and try again"):new c(a.message||"Unknown error occurred","UNKNOWN_ERROR","Please check your request and try again",a)}function B(a){let e=a.code,t=a.message||"Unknown Notion API error";switch(console.error("Notion API Error:",JSON.stringify({code:e,message:t,body:a.body,status:a.status},null,2)),e){case"unauthorized":return new c("Invalid or missing Notion API token","UNAUTHORIZED","Set NOTION_TOKEN environment variable with a valid integration token from https://www.notion.so/my-integrations");case"restricted_resource":return new c("Integration does not have access to this resource","RESTRICTED_RESOURCE","Share the page/database with your integration in Notion settings");case"object_not_found":return new c("Page or database not found","NOT_FOUND","Check that the ID is correct and the resource exists");case"validation_error":return new c("Invalid request parameters","VALIDATION_ERROR","Check the API documentation for valid parameter formats",a.details);case"rate_limited":return new c("Too many requests to Notion API","RATE_LIMITED","Wait a few seconds and try again. Consider batching operations.");case"conflict_error":return new c("Conflict with existing data","CONFLICT","The resource may have been modified. Refresh and try again.");case"service_unavailable":return new c("Notion API is temporarily unavailable","SERVICE_UNAVAILABLE","Wait a moment and try again. Check https://status.notion.so for updates.");default:return new c(t,e.toUpperCase(),"Check the Notion API documentation for this error code")}}function P(a){let e=`\u274C ${a.message}`;return a.suggestion&&(e+=`
4
+
5
+ \u{1F4A1} Suggestion: ${a.suggestion}`),a.details&&(e+=`
6
+
7
+ \u{1F4CB} Details: ${JSON.stringify(a.details,null,2)}`),e}function f(a){return async(...e)=>{try{return await a(...e)}catch(t){throw M(t)}}}function h(a){let e=a.split(`
8
+ `),t=[],r=[],o=null;for(let i=0;i<e.length;i++){let s=e[i];if(o&&!F(s)&&(t.push(...r),r=[],o=null),!!s.trim())if(s.startsWith("# "))t.push(v(1,s.slice(2)));else if(s.startsWith("## "))t.push(v(2,s.slice(3)));else if(s.startsWith("### "))t.push(v(3,s.slice(4)));else if(s.startsWith("```")){let d=s.slice(3).trim(),n=[];for(i++;i<e.length&&!e[i].startsWith("```");)n.push(e[i]),i++;t.push(z(n.join(`
9
+ `),d))}else if(s.match(/^[\-\*]\s/)){let d=s.slice(2);o="bulleted",r.push($(d))}else if(s.match(/^\d+\.\s/)){let d=s.replace(/^\d+\.\s/,"");o="numbered",r.push(H(d))}else s.startsWith("> ")?t.push(G(s.slice(2))):s.match(/^[\-\*]{3,}$/)?t.push(W()):t.push(V(s))}return r.length>0&&t.push(...r),t}function x(a){let e=[];for(let t of a)switch(t.type){case"heading_1":e.push(`# ${b(t.heading_1.rich_text)}`);break;case"heading_2":e.push(`## ${b(t.heading_2.rich_text)}`);break;case"heading_3":e.push(`### ${b(t.heading_3.rich_text)}`);break;case"paragraph":e.push(b(t.paragraph.rich_text));break;case"bulleted_list_item":e.push(`- ${b(t.bulleted_list_item.rich_text)}`);break;case"numbered_list_item":e.push(`1. ${b(t.numbered_list_item.rich_text)}`);break;case"code":e.push("```"+(t.code.language||"")),e.push(b(t.code.rich_text)),e.push("```");break;case"quote":e.push(`> ${b(t.quote.rich_text)}`);break;case"divider":e.push("---");break;default:break}return e.join(`
10
+ `)}function I(a){let e=[],t="",r=!1,o=!1,i=!1,s=!1;for(let d=0;d<a.length;d++){let n=a[d],u=a[d+1];if(n==="["){let p=a.indexOf("]",d),g=p!==-1?a.indexOf("(",p):-1,k=g!==-1?a.indexOf(")",g):-1;if(p!==-1&&g===p+1&&k!==-1){t&&(e.push(y(t,{bold:r,italic:o,code:i,strikethrough:s})),t="");let l=a.slice(d+1,p),w=a.slice(g+1,k);e.push({type:"text",text:{content:l,link:{url:w}},annotations:{bold:r,italic:o,strikethrough:s,underline:!1,code:i,color:"default"}}),d=k;continue}}if(n==="*"&&u==="*"){t&&(e.push(y(t,{bold:r,italic:o,code:i,strikethrough:s})),t=""),r=!r,d++;continue}else if(n==="*"&&u!=="*"){t&&(e.push(y(t,{bold:r,italic:o,code:i,strikethrough:s})),t=""),o=!o;continue}else if(n==="`"){t&&(e.push(y(t,{bold:r,italic:o,code:i,strikethrough:s})),t=""),i=!i;continue}else if(n==="~"&&u==="~"){t&&(e.push(y(t,{bold:r,italic:o,code:i,strikethrough:s})),t=""),s=!s,d++;continue}t+=n}return t&&e.push(y(t,{bold:r,italic:o,code:i,strikethrough:s})),e.length>0?e:[y(a)]}function b(a){return!a||!Array.isArray(a)?"":a.map(e=>{if(!e||!e.text)return"";let t=e.text.content||"",r=e.annotations||{};return r.bold&&(t=`**${t}**`),r.italic&&(t=`*${t}*`),r.code&&(t=`\`${t}\``),r.strikethrough&&(t=`~~${t}~~`),e.text.link&&(t=`[${t}](${e.text.link.url})`),t}).join("")}function y(a,e={}){return{type:"text",text:{content:a,link:null},annotations:{bold:e.bold||!1,italic:e.italic||!1,strikethrough:e.strikethrough||!1,underline:!1,code:e.code||!1,color:"default"}}}function v(a,e){let t=`heading_${a}`;return{object:"block",type:t,[t]:{rich_text:I(e),color:"default"}}}function V(a){return{object:"block",type:"paragraph",paragraph:{rich_text:I(a),color:"default"}}}function $(a){return{object:"block",type:"bulleted_list_item",bulleted_list_item:{rich_text:I(a),color:"default"}}}function H(a){return{object:"block",type:"numbered_list_item",numbered_list_item:{rich_text:I(a),color:"default"}}}function z(a,e){return{object:"block",type:"code",code:{rich_text:[y(a)],language:e||"plain text"}}}function G(a){return{object:"block",type:"quote",quote:{rich_text:I(a),color:"default"}}}function W(){return{object:"block",type:"divider",divider:{}}}function F(a){return a.match(/^[\-\*]\s/)!==null||a.match(/^\d+\.\s/)!==null}async function m(a,e={}){let{maxPages:t=0,pageSize:r=100}=e,o=[],i=null,s=0;do{let d=await a(i||void 0,r);if(o.push(...d.results),i=d.next_cursor,s++,t>0&&s>=t)break}while(i!==null);return o}async function O(a,e){return f(async()=>{if(!e.block_id)throw new c("block_id required","VALIDATION_ERROR","Provide block_id");switch(e.action){case"get":{let t=await a.blocks.retrieve({block_id:e.block_id});return{action:"get",block_id:t.id,type:t.type,has_children:t.has_children,archived:t.archived,block:t}}case"children":{let t=await m(o=>a.blocks.children.list({block_id:e.block_id,start_cursor:o,page_size:100})),r=x(t);return{action:"children",block_id:e.block_id,total_children:t.length,markdown:r,blocks:t}}case"append":{if(!e.content)throw new c("content required for append","VALIDATION_ERROR","Provide markdown content");let t=h(e.content);return await a.blocks.children.append({block_id:e.block_id,children:t}),{action:"append",block_id:e.block_id,appended_count:t.length}}case"update":{if(!e.content)throw new c("content required for update","VALIDATION_ERROR","Provide markdown content");let r=(await a.blocks.retrieve({block_id:e.block_id})).type,o=h(e.content);if(o.length===0)throw new c("Content must produce at least one block","VALIDATION_ERROR","Invalid markdown");let i=o[0],s={};if(["paragraph","heading_1","heading_2","heading_3","bulleted_list_item","numbered_list_item","quote"].includes(r))s[r]={rich_text:i[r]?.rich_text||[]};else throw new c(`Block type '${r}' cannot be updated`,"VALIDATION_ERROR","Only text blocks can be updated");return await a.blocks.update({block_id:e.block_id,...s}),{action:"update",block_id:e.block_id,type:r,updated:!0}}case"delete":return await a.blocks.delete({block_id:e.block_id}),{action:"delete",block_id:e.block_id,deleted:!0};default:throw new c(`Unknown action: ${e.action}`,"VALIDATION_ERROR","Supported actions: get, children, append, update, delete")}})()}function _(a){return{type:"text",text:{content:a,link:null},annotations:{bold:!1,italic:!1,strikethrough:!1,underline:!1,code:!1,color:"default"}}}function N(a){return a.map(e=>e.text.content).join("")}async function E(a,e){return f(async()=>{switch(e.action){case"list":{if(!e.page_id)throw new Error("page_id required for list action");let t=await m(async r=>await a.comments.list({block_id:e.page_id,start_cursor:r}));return{page_id:e.page_id,total_comments:t.length,comments:t.map(r=>({id:r.id,created_time:r.created_time,created_by:r.created_by,discussion_id:r.discussion_id,text:N(r.rich_text),parent:r.parent}))}}case"create":{if(!e.content)throw new Error("content required for create action");if(!e.page_id&&!e.discussion_id)throw new Error("Either page_id or discussion_id is required for create action");let t={rich_text:[_(e.content)]};e.discussion_id?t.discussion_id=e.discussion_id:t.parent={page_id:e.page_id};let r=await a.comments.create(t);return{comment_id:r.id,discussion_id:r.discussion_id,created:!0}}default:throw new Error(`Unsupported action: ${e.action}`)}})()}async function A(a){return f(async()=>{switch(a.direction){case"markdown-to-blocks":{if(typeof a.content!="string")throw new Error("Content must be a string for markdown-to-blocks");let e=h(a.content);return{direction:a.direction,block_count:e.length,blocks:e}}case"blocks-to-markdown":{let e=a.content;if(typeof e=="string")try{e=JSON.parse(e)}catch{throw new Error("Content must be a valid JSON array or array object for blocks-to-markdown")}if(!Array.isArray(e))throw new Error("Content must be an array for blocks-to-markdown");let t=x(e);return{direction:a.direction,char_count:t.length,markdown:t}}default:throw new Error(`Unsupported direction: ${a.direction}`)}})()}function R(a){let e={};for(let[t,r]of Object.entries(a)){if(r==null){e[t]=r;continue}typeof r=="string"?t==="Name"||t==="Title"||t.toLowerCase()==="title"?e[t]={title:[_(r)]}:e[t]={select:{name:r}}:typeof r=="number"?e[t]={number:r}:typeof r=="boolean"?e[t]={checkbox:r}:Array.isArray(r)&&r.length>0&&typeof r[0]=="string"?e[t]={multi_select:r.map(o=>({name:o}))}:e[t]=r}return e}async function D(a,e){return f(async()=>{switch(e.action){case"create":return await K(a,e);case"get":return await J(a,e);case"query":return await Q(a,e);case"create_page":return await Z(a,e);case"update_page":return await X(a,e);case"delete_page":return await Y(a,e);case"create_data_source":return await ee(a,e);case"update_data_source":return await te(a,e);case"update_database":return await ae(a,e);default:throw new c(`Unknown action: ${e.action}`,"VALIDATION_ERROR","Supported actions: create, get, query, create_page, update_page, delete_page, create_data_source, update_data_source, update_database")}})()}async function K(a,e){if(!e.parent_id||!e.title||!e.properties)throw new c("parent_id, title, and properties required for create action","VALIDATION_ERROR","Provide parent_id, title, and properties");let t={parent:{type:"page_id",page_id:e.parent_id},title:[_(e.title)],initial_data_source:{properties:e.properties}};e.description&&(t.description=[_(e.description)]),e.is_inline!==void 0&&(t.is_inline=e.is_inline);let r=await a.databases.create(t);return{action:"create",database_id:r.id,data_source_id:r.data_sources?.[0]?.id,url:r.url,created:!0}}async function J(a,e){if(!e.database_id)throw new c("database_id required for get action","VALIDATION_ERROR","Provide database_id");let t=await a.databases.retrieve({database_id:e.database_id}),r={},o=null;if(t.data_sources&&t.data_sources.length>0){let i=await a.dataSources.retrieve({data_source_id:t.data_sources[0].id});if(o={id:i.id,name:i.title?.[0]?.plain_text||t.data_sources[0].name},i.properties)for(let[s,d]of Object.entries(i.properties)){let n=d;r[s]={type:n.type,id:n.id},n.type==="select"&&n.select?.options?r[s].options=n.select.options.map(u=>u.name):n.type==="multi_select"&&n.multi_select?.options?r[s].options=n.multi_select.options.map(u=>u.name):n.type==="formula"&&n.formula&&(r[s].expression=n.formula.expression)}}return{action:"get",database_id:t.id,title:t.title?.[0]?.plain_text||"Untitled",description:t.description?.[0]?.plain_text||"",url:t.url,is_inline:t.is_inline,created_time:t.created_time,last_edited_time:t.last_edited_time,data_source:o,schema:r}}async function Q(a,e){if(!e.database_id)throw new c("database_id required for query action","VALIDATION_ERROR","Provide database_id");let t=await a.databases.retrieve({database_id:e.database_id});if(!t.data_sources||t.data_sources.length===0)throw new c("No data sources found in database","VALIDATION_ERROR","Database has no data sources");let r=t.data_sources[0].id,o=e.filters;if(e.search&&!o){let u=await a.dataSources.retrieve({data_source_id:r}),p=Object.entries(u.properties||{}).filter(([g,k])=>["title","rich_text"].includes(k.type)).map(([g])=>g);p.length>0&&(o={or:p.map(g=>({property:g,rich_text:{contains:e.search}}))})}let i={data_source_id:r};o&&(i.filter=o),e.sorts&&(i.sorts=e.sorts);let s=await m(async u=>{let p=await a.dataSources.query({...i,start_cursor:u,page_size:100});return{results:p.results,next_cursor:p.next_cursor,has_more:p.has_more}}),n=(e.limit?s.slice(0,e.limit):s).map(u=>{let p={page_id:u.id,url:u.url};for(let[g,k]of Object.entries(u.properties)){let l=k;l.type==="title"&&l.title?p[g]=l.title.map(w=>w.plain_text).join(""):l.type==="rich_text"&&l.rich_text?p[g]=l.rich_text.map(w=>w.plain_text).join(""):l.type==="select"&&l.select?p[g]=l.select.name:l.type==="multi_select"&&l.multi_select?p[g]=l.multi_select.map(w=>w.name):l.type==="number"?p[g]=l.number:l.type==="checkbox"?p[g]=l.checkbox:l.type==="url"?p[g]=l.url:l.type==="email"?p[g]=l.email:l.type==="phone_number"?p[g]=l.phone_number:l.type==="date"&&l.date&&(p[g]=l.date.start+(l.date.end?` to ${l.date.end}`:""))}return p});return{action:"query",database_id:e.database_id,data_source_id:r,total:n.length,results:n}}async function Z(a,e){if(!e.database_id)throw new c("database_id required","VALIDATION_ERROR","Provide database_id");let t=await a.databases.retrieve({database_id:e.database_id});if(!t.data_sources||t.data_sources.length===0)throw new c("No data sources found in database","VALIDATION_ERROR","Database has no data sources");let r=t.data_sources[0].id,o=e.pages||(e.page_properties?[{properties:e.page_properties}]:[]);if(o.length===0)throw new c("pages or page_properties required","VALIDATION_ERROR","Provide items to create");let i=[];for(let s of o){let d=R(s.properties),n=await a.pages.create({parent:{type:"data_source_id",data_source_id:r},properties:d});i.push({page_id:n.id,url:n.url,created:!0})}return{action:"create_page",database_id:e.database_id,data_source_id:r,processed:i.length,results:i}}async function X(a,e){let t=e.pages||(e.page_id&&e.page_properties?[{page_id:e.page_id,properties:e.page_properties}]:[]);if(t.length===0)throw new c("pages or page_id+page_properties required","VALIDATION_ERROR","Provide items to update");let r=[];for(let o of t){if(!o.page_id)throw new c("page_id required for each item","VALIDATION_ERROR","Provide page_id");let i=R(o.properties);await a.pages.update({page_id:o.page_id,properties:i}),r.push({page_id:o.page_id,updated:!0})}return{action:"update_page",processed:r.length,results:r}}async function Y(a,e){let t=e.page_ids||(e.page_id?[e.page_id]:[])||(e.pages?e.pages.map(o=>o.page_id).filter(Boolean):[]);if(t.length===0)throw new c("page_id or page_ids required","VALIDATION_ERROR","Provide page IDs to delete");let r=[];for(let o of t)await a.pages.update({page_id:o,archived:!0}),r.push({page_id:o,deleted:!0});return{action:"delete_page",processed:r.length,results:r}}async function ee(a,e){if(!e.database_id||!e.title||!e.properties)throw new c("database_id, title, and properties required","VALIDATION_ERROR","Provide database_id, title, and properties for new data source");let t={parent:{type:"database_id",database_id:e.database_id},title:[_(e.title)],properties:e.properties};return e.description&&(t.description=[_(e.description)]),{action:"create_data_source",data_source_id:(await a.dataSources.create(t)).id,database_id:e.database_id,created:!0}}async function te(a,e){if(!e.data_source_id)throw new c("data_source_id required","VALIDATION_ERROR","Provide data_source_id");let t={};if(e.title&&(t.title=[_(e.title)]),e.description&&(t.description=[_(e.description)]),e.properties&&(t.properties=e.properties),Object.keys(t).length===0)throw new c("No updates provided","VALIDATION_ERROR","Provide title, description, or properties to update");return await a.dataSources.update({data_source_id:e.data_source_id,...t}),{action:"update_data_source",data_source_id:e.data_source_id,updated:!0}}async function ae(a,e){if(!e.database_id)throw new c("database_id required","VALIDATION_ERROR","Provide database_id");let t={};if(e.parent_id&&(t.parent={type:"page_id",page_id:e.parent_id}),e.title&&(t.title=[_(e.title)]),e.description&&(t.description=[_(e.description)]),e.is_inline!==void 0&&(t.is_inline=e.is_inline),e.icon&&(t.icon={type:"emoji",emoji:e.icon}),e.cover&&(t.cover={type:"external",external:{url:e.cover}}),Object.keys(t).length===0)throw new c("No updates provided","VALIDATION_ERROR","Provide parent_id, title, description, is_inline, icon, or cover");return await a.databases.update({database_id:e.database_id,...t}),{action:"update_database",database_id:e.database_id,updated:!0}}async function C(a,e){return f(async()=>{switch(e.action){case"create":return await re(a,e);case"get":return await ie(a,e);case"update":return await oe(a,e);case"archive":case"restore":return await se(a,e);case"duplicate":return await ne(a,e);default:throw new c(`Unknown action: ${e.action}`,"VALIDATION_ERROR","Supported actions: create, get, update, archive, restore, move, duplicate")}})()}async function re(a,e){if(!e.title)throw new c("title is required for create action","VALIDATION_ERROR","Provide page title");if(!e.parent_id)throw new c("parent_id is required for page creation","VALIDATION_ERROR","Integration tokens cannot create workspace-level pages. Provide parent_id (database or page ID).");let t=e.parent_id.replace(/-/g,""),r;e.properties&&Object.keys(e.properties).length>0?r={type:"database_id",database_id:t}:r={type:"page_id",page_id:t};let o={};r.database_id?(o=R(e.properties||{}),!o.title&&!o.Name&&!o.Title&&(o.Name={title:[_(e.title)]})):o={title:{title:[_(e.title)]}};let i={parent:r,properties:o};e.icon&&(i.icon={type:"emoji",emoji:e.icon}),e.cover&&(i.cover={type:"external",external:{url:e.cover}});let s=await a.pages.create(i);if(e.content){let d=h(e.content);d.length>0&&await a.blocks.children.append({block_id:s.id,children:d})}return{action:"create",page_id:s.id,url:s.url,created:!0}}async function ie(a,e){if(!e.page_id)throw new c("page_id is required for get action","VALIDATION_ERROR","Provide page_id");let t=await a.pages.retrieve({page_id:e.page_id}),r=await m(s=>a.blocks.children.list({block_id:e.page_id,start_cursor:s,page_size:100})),o=x(r),i={};for(let[s,d]of Object.entries(t.properties)){let n=d;n.type==="title"&&n.title?i[s]=n.title.map(u=>u.plain_text).join(""):n.type==="rich_text"&&n.rich_text?i[s]=n.rich_text.map(u=>u.plain_text).join(""):n.type==="select"&&n.select?i[s]=n.select.name:n.type==="multi_select"&&n.multi_select?i[s]=n.multi_select.map(u=>u.name):n.type==="number"?i[s]=n.number:n.type==="checkbox"?i[s]=n.checkbox:n.type==="url"?i[s]=n.url:n.type==="email"?i[s]=n.email:n.type==="phone_number"?i[s]=n.phone_number:n.type==="date"&&n.date&&(i[s]=n.date.start+(n.date.end?` to ${n.date.end}`:""))}return{action:"get",page_id:t.id,url:t.url,created_time:t.created_time,last_edited_time:t.last_edited_time,archived:t.archived,properties:i,content:o,block_count:r.length}}async function oe(a,e){if(!e.page_id)throw new c("page_id is required for update action","VALIDATION_ERROR","Provide page_id");let t={};if(e.icon&&(t.icon={type:"emoji",emoji:e.icon}),e.cover&&(t.cover={type:"external",external:{url:e.cover}}),e.archived!==void 0&&(t.archived=e.archived),(e.properties||e.title)&&(t.properties={},e.title&&(t.properties.title={title:[_(e.title)]}),e.properties)){let r=R(e.properties);t.properties={...t.properties,...r}}if(Object.keys(t).length>0&&await a.pages.update({page_id:e.page_id,...t}),e.content||e.append_content||e.prepend_content){if(e.content){let r=await m(i=>a.blocks.children.list({block_id:e.page_id,start_cursor:i,page_size:100}));for(let i of r)await a.blocks.delete({block_id:i.id});let o=h(e.content);o.length>0&&await a.blocks.children.append({block_id:e.page_id,children:o})}else if(e.append_content){let r=h(e.append_content);r.length>0&&await a.blocks.children.append({block_id:e.page_id,children:r})}else if(e.prepend_content){let r=await m(i=>a.blocks.children.list({block_id:e.page_id,start_cursor:i,page_size:1})),o=h(e.prepend_content);o.length>0&&(r[0]?.id?await a.blocks.children.append({block_id:e.page_id,children:o,after:void 0}):await a.blocks.children.append({block_id:e.page_id,children:o}))}}return{action:"update",page_id:e.page_id,updated:!0}}async function se(a,e){let t=e.page_ids||(e.page_id?[e.page_id]:[]);if(t.length===0)throw new c("page_id or page_ids required","VALIDATION_ERROR","Provide at least one page ID");let r=e.action==="archive",o=[];for(let i of t)await a.pages.update({page_id:i,archived:r}),o.push({page_id:i,archived:r});return{action:e.action,processed:o.length,results:o}}async function ne(a,e){let t=e.page_ids||(e.page_id?[e.page_id]:[]);if(t.length===0)throw new c("page_id or page_ids required","VALIDATION_ERROR","Provide at least one page ID");let r=[];for(let o of t){let i=await a.pages.retrieve({page_id:o}),s=await m(n=>a.blocks.children.list({block_id:o,start_cursor:n,page_size:100})),d=await a.pages.create({parent:i.parent,properties:i.properties,icon:i.icon,cover:i.cover});s.length>0&&await a.blocks.children.append({block_id:d.id,children:s}),r.push({original_id:o,duplicate_id:d.id,url:d.url})}return{action:"duplicate",processed:r.length,results:r}}async function S(a,e){return f(async()=>{switch(e.action){case"list":{let t=await m(r=>a.users.list({start_cursor:r,page_size:100}));return{action:"list",total:t.length,users:t.map(r=>({id:r.id,type:r.type,name:r.name||"Unknown",avatar_url:r.avatar_url,email:r.type==="person"?r.person?.email:void 0}))}}case"get":{if(!e.user_id)throw new c("user_id required for get action","VALIDATION_ERROR","Provide user_id");let t=await a.users.retrieve({user_id:e.user_id});return{action:"get",id:t.id,type:t.type,name:t.name||"Unknown",avatar_url:t.avatar_url,email:t.type==="person"?t.person?.email:void 0}}case"me":{let t=await a.users.retrieve({user_id:"me"});return{action:"me",id:t.id,type:t.type,name:t.name||"Bot",bot:t.bot}}case"from_workspace":{let t=await a.search({filter:{property:"object",value:"page"},page_size:100}),r=new Map;for(let i of t.results)i.created_by&&r.set(i.created_by.id,{id:i.created_by.id,type:i.created_by.object,source:"page_metadata"}),i.last_edited_by&&r.set(i.last_edited_by.id,{id:i.last_edited_by.id,type:i.last_edited_by.object,source:"page_metadata"});let o=Array.from(r.values());return{action:"from_workspace",total:o.length,users:o,note:'Users extracted from accessible pages. Use "me" action for bot info, or share more pages for more users.'}}default:throw new c(`Unknown action: ${e.action}`,"VALIDATION_ERROR","Supported actions: list, get, me, from_workspace")}})()}async function q(a,e){return f(async()=>{switch(e.action){case"info":{let t=await a.users.retrieve({user_id:"me"});return{action:"info",bot:{id:t.id,name:t.name||"Bot",type:t.type,owner:t.bot?.owner}}}case"search":{if(!e.query)throw new c("query required for search action","VALIDATION_ERROR","Provide search query");let t={query:e.query};e.filter?.object&&(t.filter={value:e.filter.object,property:"object"}),e.sort&&(t.sort={direction:e.sort.direction||"descending",timestamp:e.sort.timestamp||"last_edited_time"});let r=await m(i=>a.search({...t,start_cursor:i,page_size:100})),o=e.limit?r.slice(0,e.limit):r;return{action:"search",query:e.query,total:o.length,results:o.map(i=>({id:i.id,object:i.object,title:i.object==="page"?i.properties?.title?.title?.[0]?.plain_text||i.properties?.Name?.title?.[0]?.plain_text||"Untitled":i.title?.[0]?.plain_text||"Untitled",url:i.url,last_edited_time:i.last_edited_time}))}}default:throw new c(`Unknown action: ${e.action}`,"VALIDATION_ERROR","Supported actions: info, search")}})()}var j=[{name:"pages",description:`Complete page lifecycle management. Handles creation, reading, updating, archiving, and duplication.
11
+
12
+ **IMPORTANT:** Integration tokens cannot create workspace-level pages. Always provide parent_id (page or database ID).
13
+
14
+ Actions: create, get, update, archive, restore, duplicate.
15
+
16
+ Maps to: POST/GET/PATCH /v1/pages + /v1/blocks/{id}/children
17
+
18
+ Examples:
19
+ - Create page: {action: "create", title: "Meeting Notes", parent_id: "xxx", content: "# Agenda
20
+ - Item 1
21
+ - Item 2"}
22
+ - Create in database: {action: "create", title: "Task", parent_id: "db-id", properties: {Status: "Todo", Priority: "High"}}
23
+ - Get with content: {action: "get", page_id: "xxx"} \u2192 Returns markdown content
24
+ - Update content: {action: "update", page_id: "xxx", append_content: "
25
+ ## New Section"}
26
+ - Update metadata: {action: "update", page_id: "xxx", icon: "\u{1F4DD}", cover: "https://..."}
27
+ - Archive: {action: "archive", page_ids: ["xxx", "yyy"]}
28
+ - Restore: {action: "restore", page_id: "xxx"}
29
+ - Duplicate: {action: "duplicate", page_id: "xxx"}`,inputSchema:{type:"object",properties:{action:{type:"string",enum:["create","get","update","archive","restore","duplicate"],description:"Action to perform"},page_id:{type:"string",description:"Page ID (required for most actions)"},page_ids:{type:"array",items:{type:"string"},description:"Multiple page IDs for batch operations"},title:{type:"string",description:"Page title"},content:{type:"string",description:"Markdown content"},append_content:{type:"string",description:"Markdown to append"},prepend_content:{type:"string",description:"Markdown to prepend"},parent_id:{type:"string",description:"Parent page or database ID"},properties:{type:"object",description:"Page properties (for database pages)"},icon:{type:"string",description:"Emoji icon"},cover:{type:"string",description:"Cover image URL"},archived:{type:"boolean",description:"Archive status"}},required:["action"]}},{name:"databases",description:`Complete database & data source operations (Notion API 2025-09-03).
30
+
31
+ **ARCHITECTURE NOTE:**
32
+ Notion databases now support multiple data sources. A database is a container that holds one or more data sources. Each data source has its own schema (properties) and rows (pages).
33
+
34
+ **WORKFLOW:**
35
+ 1. Create database \u2192 Creates database container + initial data source
36
+ 2. Get database \u2192 Retrieves data_source_id for querying
37
+ 3. Query/Create/Update pages \u2192 Use data_source_id (auto-fetched from database_id)
38
+
39
+ Actions: create, get, query, create_page, update_page, delete_page, create_data_source, update_data_source, update_database.
40
+
41
+ Maps to: /v1/databases + /v1/data_sources + /v1/pages
42
+
43
+ Examples:
44
+ - Create DB+datasource: {action: "create", parent_id: "xxx", title: "Tasks", properties: {Status: {select: {options: [{name: "Todo"}, {name: "Done"}]}}}}
45
+ - Get schema: {action: "get", database_id: "xxx"} \u2192 Returns data_source info
46
+ - Query data: {action: "query", database_id: "xxx", filters: {property: "Status", select: {equals: "Done"}}}
47
+ - Smart search: {action: "query", database_id: "xxx", search: "project"}
48
+ - Create rows: {action: "create_page", database_id: "xxx", pages: [{properties: {Name: "Task 1", Status: "Todo"}}]}
49
+ - Update rows: {action: "update_page", page_id: "yyy", page_properties: {Status: "Done"}}
50
+ - Delete rows: {action: "delete_page", page_ids: ["yyy", "zzz"]}
51
+ - Add 2nd datasource: {action: "create_data_source", database_id: "xxx", title: "Archive", properties: {...}}
52
+ - Update datasource schema: {action: "update_data_source", data_source_id: "yyy", properties: {NewField: {checkbox: {}}}}
53
+ - Move database: {action: "update_database", database_id: "xxx", parent_id: "new-page-id"}`,inputSchema:{type:"object",properties:{action:{type:"string",enum:["create","get","query","create_page","update_page","delete_page","create_data_source","update_data_source","update_database"],description:"Action to perform"},database_id:{type:"string",description:"Database ID (container)"},data_source_id:{type:"string",description:"Data source ID (for update_data_source action)"},parent_id:{type:"string",description:"Parent page ID (for create/update_database)"},title:{type:"string",description:"Title (for database or data source)"},description:{type:"string",description:"Description"},properties:{type:"object",description:"Schema properties (for create/update data source)"},is_inline:{type:"boolean",description:"Display as inline (for create/update_database)"},icon:{type:"string",description:"Emoji icon (for update_database)"},cover:{type:"string",description:"Cover image URL (for update_database)"},filters:{type:"object",description:"Query filters (for query action)"},sorts:{type:"array",items:{type:"object"},description:"Query sorts"},limit:{type:"number",description:"Max query results"},search:{type:"string",description:"Smart search across text fields (for query)"},page_id:{type:"string",description:"Single page ID (for update_page)"},page_ids:{type:"array",items:{type:"string"},description:"Multiple page IDs (for delete_page)"},page_properties:{type:"object",description:"Page properties to update (for update_page)"},pages:{type:"array",items:{type:"object"},description:"Array of pages for bulk create/update"}},required:["action"]}},{name:"blocks",description:`Fine-grained content manipulation at block level. Use for precise edits within pages.
54
+
55
+ **When to use:** Editing specific paragraphs, headings, or sections. For full page content, use pages tool.
56
+ **Block ID:** Page IDs are also valid block IDs (page is the root block).
57
+
58
+ Actions: get, children, append, update, delete.
59
+
60
+ Maps to: GET/PATCH/DELETE /v1/blocks/{id} + /v1/blocks/{id}/children
61
+
62
+ Examples:
63
+ - Get block info: {action: "get", block_id: "xxx"}
64
+ - Read content: {action: "children", block_id: "xxx"} \u2192 Returns markdown of child blocks
65
+ - Add content: {action: "append", block_id: "page-id", content: "## New Section
66
+ Paragraph text"}
67
+ - Edit paragraph: {action: "update", block_id: "block-id", content: "Updated text"}
68
+ - Remove block: {action: "delete", block_id: "block-id"}`,inputSchema:{type:"object",properties:{action:{type:"string",enum:["get","children","append","update","delete"],description:"Action to perform"},block_id:{type:"string",description:"Block ID"},content:{type:"string",description:"Markdown content (for append/update)"}},required:["action","block_id"]}},{name:"users",description:`User information retrieval. Get bot info, list users, or extract users from workspace.
69
+
70
+ **PERMISSION NOTE:** list action may fail if integration lacks user read permissions. Use from_workspace as fallback - it extracts user IDs from accessible pages' metadata.
71
+
72
+ Actions: list, get, me, from_workspace.
73
+
74
+ Maps to: GET /v1/users + GET /v1/users/{id} + GET /v1/users/me
75
+
76
+ Examples:
77
+ - Get bot details: {action: "me"} \u2192 Integration info
78
+ - List all users: {action: "list"} \u2192 Requires user:read permission
79
+ - Get specific user: {action: "get", user_id: "xxx"}
80
+ - Bypass permissions: {action: "from_workspace"} \u2192 Extracts users from page metadata (created_by, last_edited_by)`,inputSchema:{type:"object",properties:{action:{type:"string",enum:["list","get","me","from_workspace"],description:"Action to perform"},user_id:{type:"string",description:"User ID (for get action)"}},required:["action"]}},{name:"workspace",description:`Workspace-level operations: get integration info and search across pages/data sources.
81
+
82
+ **Search:** Searches page/database titles. In API 2025-09-03, use filter object "data_source" to find databases. Returns only accessible content (shared with integration).
83
+
84
+ Actions: info, search.
85
+
86
+ Maps to: GET /v1/users/me + POST /v1/search
87
+
88
+ Examples:
89
+ - Get workspace info: {action: "info"} \u2192 Bot owner, workspace details
90
+ - Search pages: {action: "search", query: "meeting notes", filter: {object: "page"}, limit: 10}
91
+ - Search databases: {action: "search", query: "tasks", filter: {object: "data_source"}}
92
+ - Sort results: {action: "search", query: "project", sort: {direction: "ascending", timestamp: "last_edited_time"}}`,inputSchema:{type:"object",properties:{action:{type:"string",enum:["info","search"],description:"Action to perform"},query:{type:"string",description:"Search query"},filter:{type:"object",properties:{object:{type:"string",enum:["page","data_source"]}}},sort:{type:"object",properties:{direction:{type:"string",enum:["ascending","descending"]},timestamp:{type:"string",enum:["last_edited_time","created_time"]}}},limit:{type:"number",description:"Max results"}},required:["action"]}},{name:"comments",description:`Page discussion management. List comments on pages and add new comments or replies.
93
+
94
+ **Threading:** Use page_id for new discussion. Use discussion_id (from list response) to reply to existing thread.
95
+
96
+ Actions: list, create.
97
+
98
+ Maps to: GET /v1/comments + POST /v1/comments
99
+
100
+ Examples:
101
+ - List page comments: {action: "list", page_id: "xxx"}
102
+ - Start discussion: {action: "create", page_id: "xxx", content: "Great work on this page!"}
103
+ - Reply to thread: {action: "create", discussion_id: "thread-id", content: "I agree with your points"}`,inputSchema:{type:"object",properties:{page_id:{type:"string",description:"Page ID"},discussion_id:{type:"string",description:"Discussion ID (for replies)"},action:{type:"string",enum:["list","create"],description:"Action to perform"},content:{type:"string",description:"Comment content (for create)"}},required:["action"]}},{name:"content_convert",description:`Format conversion utility. Convert between human-readable Markdown and Notion's block format.
104
+
105
+ **Use cases:**
106
+ - Preview Notion blocks as Markdown before appending
107
+ - Test block structure from Markdown input
108
+ - Debug block formatting issues
109
+
110
+ **Note:** Most operations (pages, blocks) handle Markdown automatically. Use this for advanced preview/validation.
111
+
112
+ Examples:
113
+ - Parse Markdown: {direction: "markdown-to-blocks", content: "# Heading
114
+ Paragraph text
115
+ - List item"}
116
+ - Read blocks: {direction: "blocks-to-markdown", content: [{"type": "paragraph", "paragraph": {...}}]}`,inputSchema:{type:"object",properties:{direction:{type:"string",enum:["markdown-to-blocks","blocks-to-markdown"],description:"Conversion direction"},content:{description:"Content to convert (string or array/JSON string)"}},required:["direction","content"]}}];function L(a,e){let t=new le({auth:e,notionVersion:"2025-09-03"});a.setRequestHandler(de,async()=>({tools:j})),a.setRequestHandler(ce,async r=>{let{name:o,arguments:i}=r.params;if(!i)return{content:[{type:"text",text:"Error: No arguments provided"}],isError:!0};try{let s;switch(o){case"pages":s=await C(t,i);break;case"databases":s=await D(t,i);break;case"blocks":s=await O(t,i);break;case"users":s=await S(t,i);break;case"workspace":s=await q(t,i);break;case"comments":s=await E(t,i);break;case"content_convert":s=await A(i);break;default:throw new c(`Unknown tool: ${o}`,"UNKNOWN_TOOL",`Available tools: ${j.map(d=>d.name).join(", ")}`)}return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}catch(s){let d=s instanceof c?s:new c(s.message,"TOOL_ERROR","Check the error details and try again");return{content:[{type:"text",text:P(d)}],isError:!0}}})}async function U(){let a=process.env.NOTION_TOKEN;a||(console.error("\u274C NOTION_TOKEN environment variable is required"),console.error("\u{1F4A1} Get your token from https://www.notion.so/my-integrations"),process.exit(1));let e=new pe({name:"@n24q02m/better-notion-mcp",version:"1.0.0"},{capabilities:{tools:{}}});L(e,a);let t=new ue;return await e.connect(t),e}async function ge(){try{await U(),process.on("SIGINT",()=>{console.error(`
117
+ \u{1F44B} Shutting down Better Notion MCP Server`),process.exit(0)})}catch(a){console.error("\u274C Failed to start server:",a),process.exit(1)}}ge();
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Better Notion MCP Server Starter
3
+ * Simplified to use composite tools only
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=start-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start-server.d.ts","sourceRoot":"","sources":["../../scripts/start-server.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Better Notion MCP Server Starter
3
+ * Simplified to use composite tools only
4
+ */
5
+ import { initServer } from '../src/init-server.js';
6
+ async function startServer() {
7
+ try {
8
+ await initServer();
9
+ // Keep process running
10
+ process.on('SIGINT', () => {
11
+ console.error('\nšŸ‘‹ Shutting down Better Notion MCP Server');
12
+ process.exit(0);
13
+ });
14
+ }
15
+ catch (error) {
16
+ console.error('āŒ Failed to start server:', error);
17
+ process.exit(1);
18
+ }
19
+ }
20
+ startServer();
21
+ //# sourceMappingURL=start-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start-server.js","sourceRoot":"","sources":["../../scripts/start-server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAElD,KAAK,UAAU,WAAW;IACxB,IAAI,CAAC;QACH,MAAM,UAAU,EAAE,CAAA;QAElB,uBAAuB;QACvB,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;YACxB,OAAO,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAA;YAC5D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAA;QACjD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;AACH,CAAC;AAED,WAAW,EAAE,CAAA"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Better Notion MCP Server
3
+ * Using composite tools for human-friendly AI agent interactions
4
+ */
5
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
6
+ export declare function initServer(): Promise<Server<{
7
+ method: string;
8
+ params?: {
9
+ [x: string]: unknown;
10
+ _meta?: {
11
+ [x: string]: unknown;
12
+ progressToken?: string | number | undefined;
13
+ } | undefined;
14
+ } | undefined;
15
+ }, {
16
+ method: string;
17
+ params?: {
18
+ [x: string]: unknown;
19
+ _meta?: {
20
+ [x: string]: unknown;
21
+ } | undefined;
22
+ } | undefined;
23
+ }, {
24
+ [x: string]: unknown;
25
+ _meta?: {
26
+ [x: string]: unknown;
27
+ } | undefined;
28
+ }>>;
29
+ //# sourceMappingURL=init-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-server.d.ts","sourceRoot":"","sources":["../../src/init-server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAA;AAIlE,wBAAsB,UAAU;;;;;;;;;;;;;;;;;;;;;;IA8B/B"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Better Notion MCP Server
3
+ * Using composite tools for human-friendly AI agent interactions
4
+ */
5
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
6
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
7
+ import { registerTools } from './tools/registry.js';
8
+ export async function initServer() {
9
+ // Get Notion token from environment
10
+ const notionToken = process.env.NOTION_TOKEN;
11
+ if (!notionToken) {
12
+ console.error('āŒ NOTION_TOKEN environment variable is required');
13
+ console.error('šŸ’” Get your token from https://www.notion.so/my-integrations');
14
+ process.exit(1);
15
+ }
16
+ // Create MCP server
17
+ const server = new Server({
18
+ name: '@n24q02m/better-notion-mcp',
19
+ version: '1.0.0'
20
+ }, {
21
+ capabilities: {
22
+ tools: {}
23
+ }
24
+ });
25
+ // Register composite tools
26
+ registerTools(server, notionToken);
27
+ // Connect stdio transport
28
+ const transport = new StdioServerTransport();
29
+ await server.connect(transport);
30
+ return server;
31
+ }
32
+ //# sourceMappingURL=init-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-server.js","sourceRoot":"","sources":["../../src/init-server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAA;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAEnD,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,oCAAoC;IACpC,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAA;IAE5C,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAA;QAChE,OAAO,CAAC,KAAK,CAAC,8DAA8D,CAAC,CAAA;QAC7E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,oBAAoB;IACpB,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB;QACE,IAAI,EAAE,4BAA4B;QAClC,OAAO,EAAE,OAAO;KACjB,EACD;QACE,YAAY,EAAE;YACZ,KAAK,EAAE,EAAE;SACV;KACF,CACF,CAAA;IAED,2BAA2B;IAC3B,aAAa,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;IAElC,0BAA0B;IAC1B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAA;IAC5C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IAC/B,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Blocks Mega Tool
3
+ * All block operations in one unified interface
4
+ */
5
+ import { Client } from '@notionhq/client';
6
+ export interface BlocksInput {
7
+ action: 'get' | 'children' | 'append' | 'update' | 'delete';
8
+ block_id: string;
9
+ content?: string;
10
+ }
11
+ /**
12
+ * Unified blocks tool
13
+ * Maps to: GET/PATCH/DELETE /v1/blocks/{id} and GET/PATCH /v1/blocks/{id}/children
14
+ */
15
+ export declare function blocks(notion: Client, input: BlocksInput): Promise<any>;
16
+ //# sourceMappingURL=blocks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../../../../src/tools/composite/blocks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAKzC,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,KAAK,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;IAC3D,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;GAGG;AACH,wBAAsB,MAAM,CAC1B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,GAAG,CAAC,CA+Gd"}