@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.
- package/LICENSE +21 -0
- package/README.md +240 -0
- package/bin/cli.mjs +117 -0
- package/build/scripts/start-server.d.ts +6 -0
- package/build/scripts/start-server.d.ts.map +1 -0
- package/build/scripts/start-server.js +21 -0
- package/build/scripts/start-server.js.map +1 -0
- package/build/src/init-server.d.ts +29 -0
- package/build/src/init-server.d.ts.map +1 -0
- package/build/src/init-server.js +32 -0
- package/build/src/init-server.js.map +1 -0
- package/build/src/tools/composite/blocks.d.ts +16 -0
- package/build/src/tools/composite/blocks.d.ts.map +1 -0
- package/build/src/tools/composite/blocks.js +104 -0
- package/build/src/tools/composite/blocks.js.map +1 -0
- package/build/src/tools/composite/comments.d.ts +16 -0
- package/build/src/tools/composite/comments.d.ts.map +1 -0
- package/build/src/tools/composite/comments.js +69 -0
- package/build/src/tools/composite/comments.js.map +1 -0
- package/build/src/tools/composite/content.d.ts +13 -0
- package/build/src/tools/composite/content.d.ts.map +1 -0
- package/build/src/tools/composite/content.js +50 -0
- package/build/src/tools/composite/content.js.map +1 -0
- package/build/src/tools/composite/databases.d.ts +33 -0
- package/build/src/tools/composite/databases.d.ts.map +1 -0
- package/build/src/tools/composite/databases.js +419 -0
- package/build/src/tools/composite/databases.js.map +1 -0
- package/build/src/tools/composite/pages.d.ts +24 -0
- package/build/src/tools/composite/pages.d.ts.map +1 -0
- package/build/src/tools/composite/pages.js +316 -0
- package/build/src/tools/composite/pages.js.map +1 -0
- package/build/src/tools/composite/search.d.ts +23 -0
- package/build/src/tools/composite/search.d.ts.map +1 -0
- package/build/src/tools/composite/search.js +94 -0
- package/build/src/tools/composite/search.js.map +1 -0
- package/build/src/tools/composite/users.d.ts +15 -0
- package/build/src/tools/composite/users.d.ts.map +1 -0
- package/build/src/tools/composite/users.js +93 -0
- package/build/src/tools/composite/users.js.map +1 -0
- package/build/src/tools/composite/workspace.d.ts +25 -0
- package/build/src/tools/composite/workspace.d.ts.map +1 -0
- package/build/src/tools/composite/workspace.js +72 -0
- package/build/src/tools/composite/workspace.js.map +1 -0
- package/build/src/tools/helpers/errors.d.ts +43 -0
- package/build/src/tools/helpers/errors.d.ts.map +1 -0
- package/build/src/tools/helpers/errors.js +162 -0
- package/build/src/tools/helpers/errors.js.map +1 -0
- package/build/src/tools/helpers/markdown.d.ts +45 -0
- package/build/src/tools/helpers/markdown.d.ts.map +1 -0
- package/build/src/tools/helpers/markdown.js +320 -0
- package/build/src/tools/helpers/markdown.js.map +1 -0
- package/build/src/tools/helpers/pagination.d.ts +42 -0
- package/build/src/tools/helpers/pagination.d.ts.map +1 -0
- package/build/src/tools/helpers/pagination.js +72 -0
- package/build/src/tools/helpers/pagination.js.map +1 -0
- package/build/src/tools/helpers/properties.d.ts +10 -0
- package/build/src/tools/helpers/properties.d.ts.map +1 -0
- package/build/src/tools/helpers/properties.js +57 -0
- package/build/src/tools/helpers/properties.js.map +1 -0
- package/build/src/tools/helpers/richtext.d.ts +85 -0
- package/build/src/tools/helpers/richtext.d.ts.map +1 -0
- package/build/src/tools/helpers/richtext.js +146 -0
- package/build/src/tools/helpers/richtext.js.map +1 -0
- package/build/src/tools/registry.d.ts +10 -0
- package/build/src/tools/registry.d.ts.map +1 -0
- package/build/src/tools/registry.js +342 -0
- package/build/src/tools/registry.js.map +1 -0
- package/build/tsconfig.tsbuildinfo +1 -0
- 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
|
+
[](https://www.npmjs.com/package/@n24q02m/better-notion-mcp)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://hub.docker.com/r/n24q02m/better-notion-mcp)
|
|
8
|
+
[](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 @@
|
|
|
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"}
|