@west10tech/notion-mcp 1.5.0 → 1.6.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/README.md +114 -12
- package/dist/clients/notion-client.d.ts +6 -3
- package/dist/clients/notion-client.d.ts.map +1 -1
- package/dist/clients/notion-client.js +218 -132
- package/dist/clients/notion-client.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +2 -2
- package/dist/index.js +73 -6
- package/dist/index.js.map +1 -1
- package/dist/tools/notion-tools.d.ts +2 -2
- package/dist/tools/notion-tools.d.ts.map +1 -1
- package/dist/tools/notion-tools.js +134 -46
- package/dist/tools/notion-tools.js.map +1 -1
- package/dist/types.d.ts +109 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -3,38 +3,51 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@west10tech/notion-mcp)
|
|
4
4
|
[]()
|
|
5
5
|
|
|
6
|
-
MCP server with full Notion capabilities (
|
|
6
|
+
MCP server with full Notion capabilities (23 tools)
|
|
7
7
|
|
|
8
8
|
**npm:** https://www.npmjs.com/package/@west10tech/notion-mcp
|
|
9
9
|
|
|
10
|
-
This MCP server includes the following integrations:
|
|
11
|
-
|
|
12
10
|
## Available Tools
|
|
13
11
|
|
|
14
|
-
This MCP server provides
|
|
12
|
+
This MCP server provides 23 tools:
|
|
15
13
|
|
|
16
|
-
###
|
|
17
|
-
- **notion_list_databases**: ⚠️ DEPRECATED: This endpoint is deprecated by Notion API. Use the search endpoint with database filter instead.
|
|
14
|
+
### Database Tools
|
|
18
15
|
- **notion_get_database**: Get database by ID
|
|
19
16
|
- **notion_query_database**: Query database pages
|
|
20
17
|
- **notion_create_database**: Create a new database
|
|
21
18
|
- **notion_update_database**: Update database properties
|
|
19
|
+
|
|
20
|
+
### Page Tools
|
|
22
21
|
- **notion_get_page**: Get page by ID
|
|
23
|
-
- **notion_create_page**: Create a new page
|
|
22
|
+
- **notion_create_page**: Create a new page
|
|
24
23
|
- **notion_update_page**: Update page properties
|
|
25
24
|
- **notion_get_page_property**: Get page property by ID
|
|
25
|
+
|
|
26
|
+
### Block Tools
|
|
26
27
|
- **notion_get_block_children**: Get block children
|
|
27
28
|
- **notion_append_block_children**: Append blocks to a parent block
|
|
28
29
|
- **notion_get_block**: Get block by ID
|
|
29
30
|
- **notion_update_block**: Update block content
|
|
30
31
|
- **notion_delete_block**: Delete a block
|
|
32
|
+
|
|
33
|
+
### User Tools
|
|
31
34
|
- **notion_list_users**: List all users
|
|
32
35
|
- **notion_get_user**: Get user by ID
|
|
33
36
|
- **notion_get_me**: Get current bot user
|
|
37
|
+
|
|
38
|
+
### Search & Comments
|
|
34
39
|
- **notion_search**: Search pages and databases
|
|
35
40
|
- **notion_create_comment**: Create a comment on a page or block
|
|
36
41
|
- **notion_get_comments**: Get comments for a page or block
|
|
37
42
|
|
|
43
|
+
### Template Tools
|
|
44
|
+
- **notion_create_page_from_template**: Create a new page by copying blocks from a template page
|
|
45
|
+
- **notion_create_database_from_template**: Create a new database by copying schema from a template database
|
|
46
|
+
|
|
47
|
+
### Duplication Tools
|
|
48
|
+
- **notion_duplicate_page**: Duplicate an existing page with its content blocks
|
|
49
|
+
- **notion_duplicate_database**: Duplicate an existing database with its schema
|
|
50
|
+
|
|
38
51
|
## Installation
|
|
39
52
|
|
|
40
53
|
```bash
|
|
@@ -49,9 +62,18 @@ Create a `.env` file with the following variables:
|
|
|
49
62
|
NOTION_ACCESS_TOKEN=your_notion_access_token_here
|
|
50
63
|
```
|
|
51
64
|
|
|
65
|
+
## Getting a Notion API Key
|
|
66
|
+
|
|
67
|
+
1. Go to [https://www.notion.so/my-integrations](https://www.notion.so/my-integrations)
|
|
68
|
+
2. Click **"New integration"**
|
|
69
|
+
3. Give it a name (e.g. "MCP Server") and select the workspace
|
|
70
|
+
4. Under **Capabilities**, enable the permissions your integration needs (read content, update content, etc.)
|
|
71
|
+
5. Click **Submit** and copy the **Internal Integration Secret** — this is your `NOTION_ACCESS_TOKEN`
|
|
72
|
+
6. **Important:** Share pages/databases with your integration by clicking the `...` menu on a page → **Connections** → select your integration
|
|
73
|
+
|
|
52
74
|
## Usage
|
|
53
75
|
|
|
54
|
-
### Running the server
|
|
76
|
+
### Running the server (stdio)
|
|
55
77
|
|
|
56
78
|
```bash
|
|
57
79
|
# Development mode
|
|
@@ -61,6 +83,25 @@ npm run dev
|
|
|
61
83
|
npm run build && npm start
|
|
62
84
|
```
|
|
63
85
|
|
|
86
|
+
### Running with HTTP/SSE transport
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# Start with HTTP transport
|
|
90
|
+
TRANSPORT_MODE=http PORT=3000 npm start
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Environment variables for HTTP mode:
|
|
94
|
+
|
|
95
|
+
| Variable | Default | Description |
|
|
96
|
+
|----------|---------|-------------|
|
|
97
|
+
| `TRANSPORT_MODE` | `stdio` | Set to `http` for HTTP/SSE transport |
|
|
98
|
+
| `PORT` | `3000` | HTTP server port |
|
|
99
|
+
| `CORS_ORIGIN` | `*` | Allowed CORS origin |
|
|
100
|
+
|
|
101
|
+
HTTP endpoints:
|
|
102
|
+
- `POST /` — MCP JSON-RPC messages
|
|
103
|
+
- `GET /health` — Health check (`{"status":"ok","server":"notion-mcp"}`)
|
|
104
|
+
|
|
64
105
|
### Using with Claude Desktop
|
|
65
106
|
|
|
66
107
|
Add this to your Claude Desktop configuration:
|
|
@@ -79,8 +120,57 @@ Add this to your Claude Desktop configuration:
|
|
|
79
120
|
}
|
|
80
121
|
```
|
|
81
122
|
|
|
82
|
-
##
|
|
83
|
-
|
|
123
|
+
## Template & Duplication Workflows
|
|
124
|
+
|
|
125
|
+
### Creating a page from a template
|
|
126
|
+
|
|
127
|
+
Use `notion_create_page_from_template` to copy the block structure of an existing page:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"template_page_id": "abc123...",
|
|
132
|
+
"parent": { "page_id": "def456..." },
|
|
133
|
+
"title": "My New Page"
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
This reads all blocks from the template page and creates a new page with the same content.
|
|
138
|
+
|
|
139
|
+
### Creating a database from a template
|
|
140
|
+
|
|
141
|
+
Use `notion_create_database_from_template` to copy the property schema of an existing database:
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{
|
|
145
|
+
"template_database_id": "abc123...",
|
|
146
|
+
"parent": { "page_id": "def456..." },
|
|
147
|
+
"title": "My New Database"
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Duplicating a page
|
|
152
|
+
|
|
153
|
+
Use `notion_duplicate_page` to create an exact copy of a page (properties + blocks):
|
|
154
|
+
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"page_id": "abc123...",
|
|
158
|
+
"title": "Custom Title"
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
If no title is provided, defaults to "Copy of {original title}". If no parent is provided, uses the same parent as the original.
|
|
163
|
+
|
|
164
|
+
### Duplicating a database
|
|
165
|
+
|
|
166
|
+
Use `notion_duplicate_database` to copy a database's schema:
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{
|
|
170
|
+
"database_id": "abc123...",
|
|
171
|
+
"title": "My Copy"
|
|
172
|
+
}
|
|
173
|
+
```
|
|
84
174
|
|
|
85
175
|
## Advanced Features
|
|
86
176
|
|
|
@@ -96,7 +186,7 @@ When a request is cancelled:
|
|
|
96
186
|
|
|
97
187
|
### Progress Notifications
|
|
98
188
|
|
|
99
|
-
The server supports progress notifications for long-running operations according to the [MCP progress specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/progress).
|
|
189
|
+
The server supports progress notifications for long-running operations according to the [MCP progress specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/progress).
|
|
100
190
|
|
|
101
191
|
To receive progress updates:
|
|
102
192
|
1. Include a `progressToken` in your request metadata
|
|
@@ -109,7 +199,7 @@ Progress is reported for:
|
|
|
109
199
|
- Multi-step operations
|
|
110
200
|
- Batch processing
|
|
111
201
|
- Long-running API calls
|
|
112
|
-
-
|
|
202
|
+
- Template and duplication operations
|
|
113
203
|
|
|
114
204
|
Example progress notification:
|
|
115
205
|
```json
|
|
@@ -124,3 +214,15 @@ Example progress notification:
|
|
|
124
214
|
}
|
|
125
215
|
```
|
|
126
216
|
|
|
217
|
+
### TypeScript Types
|
|
218
|
+
|
|
219
|
+
All tool parameters are fully typed. Import types from `@west10tech/notion-mcp/types`:
|
|
220
|
+
|
|
221
|
+
- `GetDatabaseParams`, `QueryDatabaseParams`, `CreateDatabaseParams`, `UpdateDatabaseParams`
|
|
222
|
+
- `GetPageParams`, `CreatePageParams`, `UpdatePageParams`, `GetPagePropertyParams`
|
|
223
|
+
- `GetBlockChildrenParams`, `AppendBlockChildrenParams`, `GetBlockParams`, `UpdateBlockParams`, `DeleteBlockParams`
|
|
224
|
+
- `ListUsersParams`, `GetUserParams`
|
|
225
|
+
- `SearchParams`, `CreateCommentParams`, `GetCommentsParams`
|
|
226
|
+
- `CreatePageFromTemplateParams`, `CreateDatabaseFromTemplateParams`
|
|
227
|
+
- `DuplicatePageParams`, `DuplicateDatabaseParams`
|
|
228
|
+
- `ToolResponse`
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Logger } from '../services/logger.js';
|
|
2
|
-
import { RequestOptions } from '../types.js';
|
|
2
|
+
import { RequestOptions, ToolResponse, CreatePageFromTemplateParams, CreateDatabaseFromTemplateParams, DuplicatePageParams, DuplicateDatabaseParams } from '../types.js';
|
|
3
3
|
export interface NotionClientConfig {
|
|
4
|
-
nOTIONACCESSTOKEN?: string;
|
|
5
4
|
timeout?: number;
|
|
6
5
|
rateLimit?: number;
|
|
7
6
|
authToken?: string;
|
|
7
|
+
version?: string;
|
|
8
8
|
logger?: Logger;
|
|
9
9
|
}
|
|
10
10
|
export declare class NotionClient {
|
|
@@ -29,7 +29,6 @@ export declare class NotionClient {
|
|
|
29
29
|
*/
|
|
30
30
|
private makeAuthenticatedRequest;
|
|
31
31
|
private buildPath;
|
|
32
|
-
listDatabases(params: any, options?: RequestOptions): Promise<any>;
|
|
33
32
|
getDatabase(params: any, options?: RequestOptions): Promise<any>;
|
|
34
33
|
queryDatabase(params: any, options?: RequestOptions): Promise<any>;
|
|
35
34
|
createDatabase(params: any, options?: RequestOptions): Promise<any>;
|
|
@@ -49,5 +48,9 @@ export declare class NotionClient {
|
|
|
49
48
|
search(params: any, options?: RequestOptions): Promise<any>;
|
|
50
49
|
createComment(params: any, options?: RequestOptions): Promise<any>;
|
|
51
50
|
getComments(params: any, options?: RequestOptions): Promise<any>;
|
|
51
|
+
createPageFromTemplate(params: CreatePageFromTemplateParams, options?: RequestOptions): Promise<ToolResponse>;
|
|
52
|
+
createDatabaseFromTemplate(params: CreateDatabaseFromTemplateParams, options?: RequestOptions): Promise<ToolResponse>;
|
|
53
|
+
duplicatePage(params: DuplicatePageParams, options?: RequestOptions): Promise<ToolResponse>;
|
|
54
|
+
duplicateDatabase(params: DuplicateDatabaseParams, options?: RequestOptions): Promise<ToolResponse>;
|
|
52
55
|
}
|
|
53
56
|
//# sourceMappingURL=notion-client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notion-client.d.ts","sourceRoot":"","sources":["../../src/clients/notion-client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,
|
|
1
|
+
{"version":3,"file":"notion-client.d.ts","sourceRoot":"","sources":["../../src/clients/notion-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -25,7 +25,7 @@ export class NotionClient {
|
|
|
25
25
|
headers: {
|
|
26
26
|
'Accept': 'application/json',
|
|
27
27
|
'Content-Type': 'application/json',
|
|
28
|
-
'User-Agent':
|
|
28
|
+
'User-Agent': `notion-mcp/${this.config.version || '0.0.0'}`,
|
|
29
29
|
'Notion-Version': '2022-06-28',
|
|
30
30
|
...this.getAuthHeaders()
|
|
31
31
|
},
|
|
@@ -145,7 +145,7 @@ export class NotionClient {
|
|
|
145
145
|
let envVarName;
|
|
146
146
|
// Use first required env var if specified
|
|
147
147
|
envVarName = 'NOTION_ACCESS_TOKEN';
|
|
148
|
-
const token = this.config.authToken ||
|
|
148
|
+
const token = this.config.authToken || process.env[envVarName];
|
|
149
149
|
if (token) {
|
|
150
150
|
this.logger.logAuthEvent('static_token_auth_setup', true, {
|
|
151
151
|
authType: 'bearer',
|
|
@@ -234,136 +234,6 @@ export class NotionClient {
|
|
|
234
234
|
});
|
|
235
235
|
return path;
|
|
236
236
|
}
|
|
237
|
-
/* DEBUG: endpoint={"name":"list_databases","method":"GET","path":"/databases","description":"⚠️ DEPRECATED: This endpoint is deprecated by Notion API. Use the search endpoint with database filter instead.","parameters":{"start_cursor":{"type":"string","required":false,"description":"Pagination cursor","location":"query"},"page_size":{"type":"number","required":false,"description":"Number of results per page (max 100)","location":"query","default":100}},"response_format":"json","category":"Database Operations"} */
|
|
238
|
-
async listDatabases(params, options) {
|
|
239
|
-
const startTime = Date.now();
|
|
240
|
-
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
241
|
-
endpoint: 'list_databases',
|
|
242
|
-
method: 'GET',
|
|
243
|
-
path: '/databases',
|
|
244
|
-
paramCount: Object.keys(params || {}).length,
|
|
245
|
-
paramKeys: Object.keys(params || {})
|
|
246
|
-
});
|
|
247
|
-
try {
|
|
248
|
-
// Extract and separate parameters by location: path, query, body
|
|
249
|
-
const pathTemplate = '/databases';
|
|
250
|
-
const pathParams = {};
|
|
251
|
-
const queryParams = {};
|
|
252
|
-
const bodyParams = {};
|
|
253
|
-
const extractedParams = [];
|
|
254
|
-
// Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
|
|
255
|
-
const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
|
|
256
|
-
let match;
|
|
257
|
-
while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
|
|
258
|
-
const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
|
|
259
|
-
if (paramName && params[paramName] !== undefined) {
|
|
260
|
-
pathParams[paramName] = params[paramName];
|
|
261
|
-
extractedParams.push(paramName);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
// Handle standard path templates: {resourceName}
|
|
265
|
-
const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
|
|
266
|
-
standardPathParams.forEach(paramTemplate => {
|
|
267
|
-
const paramName = paramTemplate.slice(1, -1); // Remove { }
|
|
268
|
-
// Only process if not already handled by Google template logic
|
|
269
|
-
if (!extractedParams.includes(paramName)) {
|
|
270
|
-
if (params[paramName] !== undefined) {
|
|
271
|
-
pathParams[paramName] = params[paramName];
|
|
272
|
-
extractedParams.push(paramName);
|
|
273
|
-
}
|
|
274
|
-
else {
|
|
275
|
-
// Provide default values for optional path parameters
|
|
276
|
-
if (paramName === 'userId') {
|
|
277
|
-
pathParams[paramName] = 'me'; // Default to authenticated user
|
|
278
|
-
extractedParams.push(paramName);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
// Check if any parameter has raw_array flag
|
|
284
|
-
let hasRawArrayBody = false;
|
|
285
|
-
let rawBodyData = undefined;
|
|
286
|
-
// Separate remaining parameters by location (query vs body)
|
|
287
|
-
if (params[""] !== undefined) {
|
|
288
|
-
queryParams[""] = params[""];
|
|
289
|
-
extractedParams.push("");
|
|
290
|
-
}
|
|
291
|
-
if (params[""] !== undefined) {
|
|
292
|
-
queryParams[""] = params[""];
|
|
293
|
-
extractedParams.push("");
|
|
294
|
-
}
|
|
295
|
-
// Any remaining unprocessed parameters default to body for backward compatibility
|
|
296
|
-
for (const [key, value] of Object.entries(params)) {
|
|
297
|
-
if (!extractedParams.includes(key)) {
|
|
298
|
-
bodyParams[key] = value;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
// Validate required parameters
|
|
302
|
-
const path = this.buildPath('/databases', pathParams);
|
|
303
|
-
// For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
|
|
304
|
-
const requestPath = path === '/' ? '' : path;
|
|
305
|
-
// Report initial progress if callback provided
|
|
306
|
-
if (options?.onProgress) {
|
|
307
|
-
await options.onProgress({
|
|
308
|
-
progress: 0,
|
|
309
|
-
total: 100,
|
|
310
|
-
message: `Starting list_databases request...`
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
// Use standard HTTP client for other auth types with abort signal
|
|
314
|
-
const requestConfig = {};
|
|
315
|
-
if (options?.signal) {
|
|
316
|
-
requestConfig.signal = options.signal;
|
|
317
|
-
}
|
|
318
|
-
const response = await this.httpClient.get(requestPath, { params: queryParams, ...requestConfig });
|
|
319
|
-
// Report completion progress if callback provided
|
|
320
|
-
if (options?.onProgress) {
|
|
321
|
-
await options.onProgress({
|
|
322
|
-
progress: 100,
|
|
323
|
-
total: 100,
|
|
324
|
-
message: `Completed list_databases request`
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
const duration = Date.now() - startTime;
|
|
328
|
-
this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
|
|
329
|
-
endpoint: 'list_databases',
|
|
330
|
-
method: 'GET',
|
|
331
|
-
path: '/databases',
|
|
332
|
-
duration_ms: duration,
|
|
333
|
-
responseDataSize: JSON.stringify(response.data).length
|
|
334
|
-
});
|
|
335
|
-
return {
|
|
336
|
-
content: [
|
|
337
|
-
{
|
|
338
|
-
type: 'text',
|
|
339
|
-
text: JSON.stringify(response.data, null, 2)
|
|
340
|
-
}
|
|
341
|
-
]
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
catch (error) {
|
|
345
|
-
const duration = Date.now() - startTime;
|
|
346
|
-
// Check if error is due to cancellation
|
|
347
|
-
if (axios.isCancel(error)) {
|
|
348
|
-
this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
|
|
349
|
-
endpoint: 'list_databases',
|
|
350
|
-
method: 'GET',
|
|
351
|
-
path: '/databases',
|
|
352
|
-
duration_ms: duration
|
|
353
|
-
});
|
|
354
|
-
throw new Error('Request was cancelled');
|
|
355
|
-
}
|
|
356
|
-
this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
|
|
357
|
-
endpoint: 'list_databases',
|
|
358
|
-
method: 'GET',
|
|
359
|
-
path: '/databases',
|
|
360
|
-
duration_ms: duration,
|
|
361
|
-
error: error instanceof Error ? error.message : String(error),
|
|
362
|
-
errorType: error instanceof Error ? error.constructor.name : 'unknown'
|
|
363
|
-
});
|
|
364
|
-
throw new Error(`Failed to execute list_databases: ${error instanceof Error ? error.message : String(error)}`);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
237
|
/* DEBUG: endpoint={"name":"get_database","method":"GET","path":"/databases/{database_id}","description":"Get database by ID","parameters":{"database_id":{"type":"string","required":true,"description":"Database ID to fetch","location":"path"}},"response_format":"json","category":"Database Operations"} */
|
|
368
238
|
async getDatabase(params, options) {
|
|
369
239
|
const startTime = Date.now();
|
|
@@ -2946,5 +2816,221 @@ export class NotionClient {
|
|
|
2946
2816
|
throw new Error(`Failed to execute get_comments: ${error instanceof Error ? error.message : String(error)}`);
|
|
2947
2817
|
}
|
|
2948
2818
|
}
|
|
2819
|
+
async createPageFromTemplate(params, options) {
|
|
2820
|
+
const startTime = Date.now();
|
|
2821
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
2822
|
+
endpoint: 'create_page_from_template',
|
|
2823
|
+
templatePageId: params.template_page_id
|
|
2824
|
+
});
|
|
2825
|
+
try {
|
|
2826
|
+
if (options?.onProgress) {
|
|
2827
|
+
await options.onProgress({ progress: 0, total: 100, message: 'Reading template page blocks...' });
|
|
2828
|
+
}
|
|
2829
|
+
// Get template page blocks
|
|
2830
|
+
const blocksResult = await this.getBlockChildren({ block_id: params.template_page_id }, options);
|
|
2831
|
+
const blocksData = JSON.parse(blocksResult.content[0].text);
|
|
2832
|
+
const templateBlocks = (blocksData.results || []).map((block) => {
|
|
2833
|
+
const { id, created_time, last_edited_time, created_by, last_edited_by, has_children, archived, in_trash, parent, ...rest } = block;
|
|
2834
|
+
return rest;
|
|
2835
|
+
});
|
|
2836
|
+
if (options?.onProgress) {
|
|
2837
|
+
await options.onProgress({ progress: 50, total: 100, message: 'Creating page from template...' });
|
|
2838
|
+
}
|
|
2839
|
+
// Build page creation params
|
|
2840
|
+
const createParams = {
|
|
2841
|
+
parent: params.parent,
|
|
2842
|
+
children: templateBlocks,
|
|
2843
|
+
};
|
|
2844
|
+
if (params.properties) {
|
|
2845
|
+
createParams.properties = params.properties;
|
|
2846
|
+
}
|
|
2847
|
+
if (params.title) {
|
|
2848
|
+
createParams.properties = {
|
|
2849
|
+
...createParams.properties,
|
|
2850
|
+
title: { title: [{ text: { content: params.title } }] },
|
|
2851
|
+
};
|
|
2852
|
+
}
|
|
2853
|
+
const result = await this.createPage(createParams, options);
|
|
2854
|
+
const duration = Date.now() - startTime;
|
|
2855
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Template page created successfully', {
|
|
2856
|
+
endpoint: 'create_page_from_template',
|
|
2857
|
+
duration_ms: duration
|
|
2858
|
+
});
|
|
2859
|
+
if (options?.onProgress) {
|
|
2860
|
+
await options.onProgress({ progress: 100, total: 100, message: 'Page created from template' });
|
|
2861
|
+
}
|
|
2862
|
+
return result;
|
|
2863
|
+
}
|
|
2864
|
+
catch (error) {
|
|
2865
|
+
const duration = Date.now() - startTime;
|
|
2866
|
+
this.logger.error('ENDPOINT_ERROR', 'Failed to create page from template', {
|
|
2867
|
+
endpoint: 'create_page_from_template',
|
|
2868
|
+
duration_ms: duration,
|
|
2869
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2870
|
+
});
|
|
2871
|
+
throw new Error(`Failed to create page from template: ${error instanceof Error ? error.message : String(error)}`);
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
async createDatabaseFromTemplate(params, options) {
|
|
2875
|
+
const startTime = Date.now();
|
|
2876
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
2877
|
+
endpoint: 'create_database_from_template',
|
|
2878
|
+
templateDatabaseId: params.template_database_id
|
|
2879
|
+
});
|
|
2880
|
+
try {
|
|
2881
|
+
if (options?.onProgress) {
|
|
2882
|
+
await options.onProgress({ progress: 0, total: 100, message: 'Reading template database schema...' });
|
|
2883
|
+
}
|
|
2884
|
+
// Get template database schema
|
|
2885
|
+
const dbResult = await this.getDatabase({ database_id: params.template_database_id }, options);
|
|
2886
|
+
const dbData = JSON.parse(dbResult.content[0].text);
|
|
2887
|
+
if (options?.onProgress) {
|
|
2888
|
+
await options.onProgress({ progress: 50, total: 100, message: 'Creating database from template...' });
|
|
2889
|
+
}
|
|
2890
|
+
// Extract schema properties (exclude internal ones)
|
|
2891
|
+
const properties = { ...dbData.properties };
|
|
2892
|
+
const createParams = {
|
|
2893
|
+
parent: params.parent,
|
|
2894
|
+
title: [{ text: { content: params.title } }],
|
|
2895
|
+
properties,
|
|
2896
|
+
};
|
|
2897
|
+
const result = await this.createDatabase(createParams, options);
|
|
2898
|
+
const duration = Date.now() - startTime;
|
|
2899
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Template database created successfully', {
|
|
2900
|
+
endpoint: 'create_database_from_template',
|
|
2901
|
+
duration_ms: duration
|
|
2902
|
+
});
|
|
2903
|
+
if (options?.onProgress) {
|
|
2904
|
+
await options.onProgress({ progress: 100, total: 100, message: 'Database created from template' });
|
|
2905
|
+
}
|
|
2906
|
+
return result;
|
|
2907
|
+
}
|
|
2908
|
+
catch (error) {
|
|
2909
|
+
const duration = Date.now() - startTime;
|
|
2910
|
+
this.logger.error('ENDPOINT_ERROR', 'Failed to create database from template', {
|
|
2911
|
+
endpoint: 'create_database_from_template',
|
|
2912
|
+
duration_ms: duration,
|
|
2913
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2914
|
+
});
|
|
2915
|
+
throw new Error(`Failed to create database from template: ${error instanceof Error ? error.message : String(error)}`);
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
async duplicatePage(params, options) {
|
|
2919
|
+
const startTime = Date.now();
|
|
2920
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
2921
|
+
endpoint: 'duplicate_page',
|
|
2922
|
+
pageId: params.page_id
|
|
2923
|
+
});
|
|
2924
|
+
try {
|
|
2925
|
+
if (options?.onProgress) {
|
|
2926
|
+
await options.onProgress({ progress: 0, total: 100, message: 'Reading source page...' });
|
|
2927
|
+
}
|
|
2928
|
+
// Get source page
|
|
2929
|
+
const pageResult = await this.getPage({ page_id: params.page_id }, options);
|
|
2930
|
+
const pageData = JSON.parse(pageResult.content[0].text);
|
|
2931
|
+
if (options?.onProgress) {
|
|
2932
|
+
await options.onProgress({ progress: 25, total: 100, message: 'Reading page blocks...' });
|
|
2933
|
+
}
|
|
2934
|
+
// Get source page blocks
|
|
2935
|
+
const blocksResult = await this.getBlockChildren({ block_id: params.page_id }, options);
|
|
2936
|
+
const blocksData = JSON.parse(blocksResult.content[0].text);
|
|
2937
|
+
const blocks = (blocksData.results || []).map((block) => {
|
|
2938
|
+
const { id, created_time, last_edited_time, created_by, last_edited_by, has_children, archived, in_trash, parent, ...rest } = block;
|
|
2939
|
+
return rest;
|
|
2940
|
+
});
|
|
2941
|
+
if (options?.onProgress) {
|
|
2942
|
+
await options.onProgress({ progress: 50, total: 100, message: 'Creating duplicate page...' });
|
|
2943
|
+
}
|
|
2944
|
+
// Determine parent
|
|
2945
|
+
const parent = params.parent || pageData.parent;
|
|
2946
|
+
// Determine title
|
|
2947
|
+
let properties = { ...pageData.properties };
|
|
2948
|
+
if (params.title) {
|
|
2949
|
+
properties.title = { title: [{ text: { content: params.title } }] };
|
|
2950
|
+
}
|
|
2951
|
+
else {
|
|
2952
|
+
// Try to prepend "Copy of " to existing title
|
|
2953
|
+
const titleProp = properties.title || properties.Name;
|
|
2954
|
+
if (titleProp?.title?.[0]?.text?.content) {
|
|
2955
|
+
const originalTitle = titleProp.title[0].text.content;
|
|
2956
|
+
properties.title = { title: [{ text: { content: `Copy of ${originalTitle}` } }] };
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
const createParams = {
|
|
2960
|
+
parent,
|
|
2961
|
+
properties,
|
|
2962
|
+
children: blocks,
|
|
2963
|
+
};
|
|
2964
|
+
const result = await this.createPage(createParams, options);
|
|
2965
|
+
const duration = Date.now() - startTime;
|
|
2966
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Page duplicated successfully', {
|
|
2967
|
+
endpoint: 'duplicate_page',
|
|
2968
|
+
duration_ms: duration
|
|
2969
|
+
});
|
|
2970
|
+
if (options?.onProgress) {
|
|
2971
|
+
await options.onProgress({ progress: 100, total: 100, message: 'Page duplicated' });
|
|
2972
|
+
}
|
|
2973
|
+
return result;
|
|
2974
|
+
}
|
|
2975
|
+
catch (error) {
|
|
2976
|
+
const duration = Date.now() - startTime;
|
|
2977
|
+
this.logger.error('ENDPOINT_ERROR', 'Failed to duplicate page', {
|
|
2978
|
+
endpoint: 'duplicate_page',
|
|
2979
|
+
duration_ms: duration,
|
|
2980
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2981
|
+
});
|
|
2982
|
+
throw new Error(`Failed to duplicate page: ${error instanceof Error ? error.message : String(error)}`);
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
async duplicateDatabase(params, options) {
|
|
2986
|
+
const startTime = Date.now();
|
|
2987
|
+
this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
|
|
2988
|
+
endpoint: 'duplicate_database',
|
|
2989
|
+
databaseId: params.database_id
|
|
2990
|
+
});
|
|
2991
|
+
try {
|
|
2992
|
+
if (options?.onProgress) {
|
|
2993
|
+
await options.onProgress({ progress: 0, total: 100, message: 'Reading source database...' });
|
|
2994
|
+
}
|
|
2995
|
+
// Get source database
|
|
2996
|
+
const dbResult = await this.getDatabase({ database_id: params.database_id }, options);
|
|
2997
|
+
const dbData = JSON.parse(dbResult.content[0].text);
|
|
2998
|
+
if (options?.onProgress) {
|
|
2999
|
+
await options.onProgress({ progress: 50, total: 100, message: 'Creating duplicate database...' });
|
|
3000
|
+
}
|
|
3001
|
+
// Determine parent
|
|
3002
|
+
const parent = params.parent || dbData.parent;
|
|
3003
|
+
// Determine title
|
|
3004
|
+
let title = params.title;
|
|
3005
|
+
if (!title) {
|
|
3006
|
+
const originalTitle = dbData.title?.[0]?.text?.content || 'Untitled';
|
|
3007
|
+
title = `Copy of ${originalTitle}`;
|
|
3008
|
+
}
|
|
3009
|
+
const createParams = {
|
|
3010
|
+
parent,
|
|
3011
|
+
title: [{ text: { content: title } }],
|
|
3012
|
+
properties: dbData.properties,
|
|
3013
|
+
};
|
|
3014
|
+
const result = await this.createDatabase(createParams, options);
|
|
3015
|
+
const duration = Date.now() - startTime;
|
|
3016
|
+
this.logger.info('ENDPOINT_SUCCESS', 'Database duplicated successfully', {
|
|
3017
|
+
endpoint: 'duplicate_database',
|
|
3018
|
+
duration_ms: duration
|
|
3019
|
+
});
|
|
3020
|
+
if (options?.onProgress) {
|
|
3021
|
+
await options.onProgress({ progress: 100, total: 100, message: 'Database duplicated' });
|
|
3022
|
+
}
|
|
3023
|
+
return result;
|
|
3024
|
+
}
|
|
3025
|
+
catch (error) {
|
|
3026
|
+
const duration = Date.now() - startTime;
|
|
3027
|
+
this.logger.error('ENDPOINT_ERROR', 'Failed to duplicate database', {
|
|
3028
|
+
endpoint: 'duplicate_database',
|
|
3029
|
+
duration_ms: duration,
|
|
3030
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3031
|
+
});
|
|
3032
|
+
throw new Error(`Failed to duplicate database: ${error instanceof Error ? error.message : String(error)}`);
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
2949
3035
|
}
|
|
2950
3036
|
//# sourceMappingURL=notion-client.js.map
|