@west10tech/notion-mcp 1.4.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 CHANGED
@@ -3,38 +3,51 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@west10tech/notion-mcp.svg)](https://www.npmjs.com/package/@west10tech/notion-mcp)
4
4
  [![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/gcaliene/85580419c40e9163928d29b56df2e00c/raw/coverage.json)]()
5
5
 
6
- MCP server with full Notion capabilities (20 endpoints)
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 20 tools across 1 integrations:
12
+ This MCP server provides 23 tools:
15
13
 
16
- ### Notion Tools
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. Note: Creating pages directly in workspace root requires special permissions - use database or page parents instead.
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
- ## Instructions for Fetching API Keys/Tokens
83
- - **COMING SOON**
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
- - File uploads/downloads
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":"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"}
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': 'notion-mcp/1.0.0',
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 || this.config['nOTIONACCESSTOKEN'] || process.env[envVarName];
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