@west10tech/notion-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +120 -0
  2. package/dist/clients/notion-client.d.ts +53 -0
  3. package/dist/clients/notion-client.d.ts.map +1 -0
  4. package/dist/clients/notion-client.js +2950 -0
  5. package/dist/clients/notion-client.js.map +1 -0
  6. package/dist/config.d.ts +21 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +58 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/index.d.ts +3 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +163 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/services/log-batcher.d.ts +44 -0
  15. package/dist/services/log-batcher.d.ts.map +1 -0
  16. package/dist/services/log-batcher.js +81 -0
  17. package/dist/services/log-batcher.js.map +1 -0
  18. package/dist/services/log-shipper.d.ts +104 -0
  19. package/dist/services/log-shipper.d.ts.map +1 -0
  20. package/dist/services/log-shipper.js +384 -0
  21. package/dist/services/log-shipper.js.map +1 -0
  22. package/dist/services/logger.d.ts +92 -0
  23. package/dist/services/logger.d.ts.map +1 -0
  24. package/dist/services/logger.js +224 -0
  25. package/dist/services/logger.js.map +1 -0
  26. package/dist/services/progress-reporter.d.ts +64 -0
  27. package/dist/services/progress-reporter.d.ts.map +1 -0
  28. package/dist/services/progress-reporter.js +192 -0
  29. package/dist/services/progress-reporter.js.map +1 -0
  30. package/dist/services/request-tracker.d.ts +55 -0
  31. package/dist/services/request-tracker.d.ts.map +1 -0
  32. package/dist/services/request-tracker.js +184 -0
  33. package/dist/services/request-tracker.js.map +1 -0
  34. package/dist/tools/notion-tools.d.ts +21 -0
  35. package/dist/tools/notion-tools.d.ts.map +1 -0
  36. package/dist/tools/notion-tools.js +1146 -0
  37. package/dist/tools/notion-tools.js.map +1 -0
  38. package/dist/types.d.ts +25 -0
  39. package/dist/types.d.ts.map +1 -0
  40. package/dist/types.js +2 -0
  41. package/dist/types.js.map +1 -0
  42. package/package.json +57 -0
@@ -0,0 +1,2950 @@
1
+ import axios from 'axios';
2
+ import { Logger } from '../services/logger.js';
3
+ export class NotionClient {
4
+ constructor(config) {
5
+ this.config = config;
6
+ // Generate unique session ID for this client instance
7
+ this.sessionId = `notion-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
8
+ // Initialize logger (fallback to console if not provided)
9
+ this.logger = config.logger || new Logger({
10
+ logLevel: 'ERROR',
11
+ component: 'client',
12
+ enableConsole: true,
13
+ enableShipping: false,
14
+ serverName: 'notion-mcp'
15
+ });
16
+ this.logger.info('CLIENT_INIT', 'Client instance created', {
17
+ baseUrl: this.resolveBaseUrl(),
18
+ timeout: this.config.timeout || 30000,
19
+ hasRateLimit: !!this.config.rateLimit,
20
+ configKeys: Object.keys(config)
21
+ });
22
+ this.httpClient = axios.create({
23
+ baseURL: this.resolveBaseUrl(),
24
+ timeout: this.config.timeout || 30000,
25
+ headers: {
26
+ 'Accept': 'application/json',
27
+ 'Content-Type': 'application/json',
28
+ 'User-Agent': 'notion-mcp/1.0.0',
29
+ 'Notion-Version': '2022-06-28',
30
+ ...this.getAuthHeaders()
31
+ },
32
+ });
33
+ // Add request interceptor for rate limiting
34
+ if (this.config.rateLimit) {
35
+ this.setupRateLimit(this.config.rateLimit);
36
+ }
37
+ // Add request interceptor for logging
38
+ this.httpClient.interceptors.request.use((config) => {
39
+ this.logger.logRequestStart(config.method?.toUpperCase() || 'GET', `${config.baseURL}${config.url}`, {
40
+ hasData: !!config.data,
41
+ hasParams: !!(config.params && Object.keys(config.params).length > 0),
42
+ headers: Object.keys(config.headers || {})
43
+ });
44
+ if (config.data) {
45
+ this.logger.debug('HTTP_REQUEST_BODY', 'Request body data', {
46
+ dataType: typeof config.data,
47
+ dataSize: JSON.stringify(config.data).length
48
+ });
49
+ }
50
+ if (config.params && Object.keys(config.params).length > 0) {
51
+ this.logger.debug('HTTP_REQUEST_PARAMS', 'Query parameters', {
52
+ paramCount: Object.keys(config.params).length,
53
+ paramKeys: Object.keys(config.params)
54
+ });
55
+ }
56
+ return config;
57
+ }, (error) => {
58
+ this.logger.error('HTTP_REQUEST_ERROR', 'Request interceptor error', {
59
+ error: error.message,
60
+ code: error.code
61
+ });
62
+ return Promise.reject(error);
63
+ });
64
+ // Add response interceptor for logging and error handling
65
+ this.httpClient.interceptors.response.use((response) => {
66
+ this.logger.logRequestSuccess(response.config?.method?.toUpperCase() || 'GET', `${response.config?.baseURL}${response.config?.url}`, response.status, 0, // Duration will be calculated in endpoint methods
67
+ {
68
+ statusText: response.statusText,
69
+ responseSize: JSON.stringify(response.data).length,
70
+ headers: Object.keys(response.headers || {})
71
+ });
72
+ return response;
73
+ }, (error) => {
74
+ this.logger.logRequestError(error.config?.method?.toUpperCase() || 'GET', `${error.config?.baseURL}${error.config?.url}`, error, 0, // Duration will be calculated in endpoint methods
75
+ {
76
+ hasResponseData: !!error.response?.data
77
+ });
78
+ throw error;
79
+ });
80
+ }
81
+ setupRateLimit(requestsPerMinute) {
82
+ const interval = 60000 / requestsPerMinute; // ms between requests
83
+ let lastRequestTime = 0;
84
+ this.logger.info('RATE_LIMIT_SETUP', 'Rate limiting configured', {
85
+ requestsPerMinute,
86
+ intervalMs: interval
87
+ });
88
+ this.httpClient.interceptors.request.use(async (config) => {
89
+ const now = Date.now();
90
+ const timeSinceLastRequest = now - lastRequestTime;
91
+ if (timeSinceLastRequest < interval) {
92
+ const delayMs = interval - timeSinceLastRequest;
93
+ this.logger.logRateLimit('HTTP_REQUEST', delayMs, {
94
+ timeSinceLastRequest,
95
+ requiredInterval: interval
96
+ });
97
+ await new Promise(resolve => setTimeout(resolve, delayMs));
98
+ }
99
+ lastRequestTime = Date.now();
100
+ return config;
101
+ });
102
+ }
103
+ resolveBaseUrl() {
104
+ // Debug logging for base_url resolution
105
+ // console.error('[NotionClient] Resolving base URL...');
106
+ // console.error('[NotionClient] Template base_url:', 'https://api.notion.com/v1');
107
+ // console.error('[NotionClient] CustomConfig baseUrl:', '');
108
+ let baseUrl = 'https://api.notion.com/v1';
109
+ // console.error('[NotionClient] Initial resolved baseUrl:', baseUrl);
110
+ // If no base URL was found, throw an error
111
+ if (!baseUrl) {
112
+ throw new Error(`No base URL configured for notion. Please provide base_url in template or customConfig.baseUrl.`);
113
+ }
114
+ // Handle dynamic domain replacement for patterns like CONFLUENCE_DOMAIN, JIRA_DOMAIN, etc.
115
+ const domainEnvVar = `NOTION_DOMAIN`;
116
+ const domain = process.env[domainEnvVar];
117
+ // console.error(`[NotionClient] Domain env var (${domainEnvVar}):`, domain);
118
+ // Check for SERVICE_DOMAIN pattern (e.g., CONFLUENCE_DOMAIN, JIRA_DOMAIN, SLACK_DOMAIN)
119
+ // This handles both YOUR_DOMAIN and {SERVICE}_DOMAIN patterns in base URLs
120
+ if (baseUrl.includes('YOUR_DOMAIN') || baseUrl.includes(`${domainEnvVar}`)) {
121
+ if (!domain) {
122
+ throw new Error(`Missing domain configuration. Please set ${domainEnvVar} environment variable.`);
123
+ }
124
+ // Replace the placeholder with the actual domain value
125
+ // This handles patterns like https://CONFLUENCE_DOMAIN.atlassian.net
126
+ if (baseUrl.includes('YOUR_DOMAIN')) {
127
+ baseUrl = baseUrl.replace(/YOUR_DOMAIN/g, domain);
128
+ }
129
+ if (baseUrl.includes(`${domainEnvVar}`)) {
130
+ // Replace all occurrences of the service-specific domain placeholder
131
+ const regex = new RegExp(domainEnvVar, 'g');
132
+ baseUrl = baseUrl.replace(regex, domain);
133
+ }
134
+ this.logger.info('DOMAIN_RESOLVED', `Resolved base URL with domain`, {
135
+ template: 'notion',
136
+ baseUrl: baseUrl
137
+ });
138
+ }
139
+ // console.error('[NotionClient] Final resolved baseUrl:', baseUrl);
140
+ return baseUrl;
141
+ }
142
+ getAuthHeaders() {
143
+ // Bearer/API key authentication (static tokens)
144
+ // Determine the correct environment variable name based on auth type and configuration
145
+ let envVarName;
146
+ // Use first required env var if specified
147
+ envVarName = 'NOTION_ACCESS_TOKEN';
148
+ const token = this.config.authToken || this.config['nOTIONACCESSTOKEN'] || process.env[envVarName];
149
+ if (token) {
150
+ this.logger.logAuthEvent('static_token_auth_setup', true, {
151
+ authType: 'bearer',
152
+ tokenPreview: token.substring(0, 8) + '...',
153
+ header: 'Authorization',
154
+ source: 'static_configuration',
155
+ envVar: envVarName
156
+ });
157
+ return {
158
+ 'Authorization': `Bearer ${token}`
159
+ };
160
+ }
161
+ this.logger.warn('AUTH_WARNING', 'No authentication token found', {
162
+ authType: 'bearer',
163
+ warning: 'API calls may be rate limited',
164
+ checkedSources: ['config.authToken', 'environment variables'],
165
+ expectedEnvVar: envVarName
166
+ });
167
+ return {};
168
+ }
169
+ /**
170
+ * Initialize the client (for OAuth clients that need initialization)
171
+ */
172
+ async initialize() {
173
+ this.logger.debug('CLIENT_INITIALIZE', 'No initialization required for this auth type');
174
+ }
175
+ /**
176
+ * Get the session ID for this client instance
177
+ */
178
+ getSessionId() {
179
+ return this.sessionId;
180
+ }
181
+ /**
182
+ * Make an authenticated request with proper headers and cancellation support
183
+ */
184
+ async makeAuthenticatedRequest(config, options) {
185
+ // Add abort signal if provided
186
+ if (options?.signal) {
187
+ config.signal = options.signal;
188
+ }
189
+ // For non-OAuth requests, log what auth headers are being used
190
+ this.logger.info('REQUEST_AUTH', 'Using pre-configured authentication headers', {
191
+ authType: 'static',
192
+ requestUrl: config.url,
193
+ authHeaders: config.headers?.Authorization ? 'present' : 'missing',
194
+ headerKeys: Object.keys(config.headers || {})
195
+ });
196
+ return this.httpClient.request(config);
197
+ }
198
+ buildPath(template, params) {
199
+ let path = template;
200
+ // Custom encoding that preserves forward slashes for API paths
201
+ const encodePathComponent = (value) => {
202
+ // For Google API resource names like "people/c123", preserve the forward slash
203
+ return encodeURIComponent(value).replace(/%2F/g, '/');
204
+ };
205
+ // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
206
+ const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
207
+ let match;
208
+ const processedParams = [];
209
+ while ((match = googlePathTemplateRegex.exec(template)) !== null) {
210
+ const fullMatch = match[0]; // e.g., "{resourceName=people/*}"
211
+ const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
212
+ if (paramName && params[paramName] !== undefined) {
213
+ path = path.replace(fullMatch, encodePathComponent(String(params[paramName])));
214
+ processedParams.push(paramName);
215
+ }
216
+ }
217
+ // Handle standard path templates: {resourceName}
218
+ for (const [key, value] of Object.entries(params)) {
219
+ if (!processedParams.includes(key)) {
220
+ const standardTemplate = `{${key}}`;
221
+ if (path.includes(standardTemplate)) {
222
+ path = path.replace(standardTemplate, encodePathComponent(String(value)));
223
+ processedParams.push(key);
224
+ }
225
+ }
226
+ }
227
+ this.logger.debug('PATH_BUILD', 'Built API path from template', {
228
+ template,
229
+ resultPath: path,
230
+ paramCount: Object.keys(params).length,
231
+ paramKeys: Object.keys(params),
232
+ processedParams,
233
+ hasGoogleTemplates: googlePathTemplateRegex.test(template)
234
+ });
235
+ return path;
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
+ /* 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
+ async getDatabase(params, options) {
369
+ const startTime = Date.now();
370
+ this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
371
+ endpoint: 'get_database',
372
+ method: 'GET',
373
+ path: '/databases/{database_id}',
374
+ paramCount: Object.keys(params || {}).length,
375
+ paramKeys: Object.keys(params || {})
376
+ });
377
+ try {
378
+ // Extract and separate parameters by location: path, query, body
379
+ const pathTemplate = '/databases/{database_id}';
380
+ const pathParams = {};
381
+ const queryParams = {};
382
+ const bodyParams = {};
383
+ const extractedParams = [];
384
+ // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
385
+ const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
386
+ let match;
387
+ while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
388
+ const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
389
+ if (paramName && params[paramName] !== undefined) {
390
+ pathParams[paramName] = params[paramName];
391
+ extractedParams.push(paramName);
392
+ }
393
+ }
394
+ // Handle standard path templates: {resourceName}
395
+ const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
396
+ standardPathParams.forEach(paramTemplate => {
397
+ const paramName = paramTemplate.slice(1, -1); // Remove { }
398
+ // Only process if not already handled by Google template logic
399
+ if (!extractedParams.includes(paramName)) {
400
+ if (params[paramName] !== undefined) {
401
+ pathParams[paramName] = params[paramName];
402
+ extractedParams.push(paramName);
403
+ }
404
+ else {
405
+ // Provide default values for optional path parameters
406
+ if (paramName === 'userId') {
407
+ pathParams[paramName] = 'me'; // Default to authenticated user
408
+ extractedParams.push(paramName);
409
+ }
410
+ }
411
+ }
412
+ });
413
+ // Check if any parameter has raw_array flag
414
+ let hasRawArrayBody = false;
415
+ let rawBodyData = undefined;
416
+ // Separate remaining parameters by location (query vs body)
417
+ // Any remaining unprocessed parameters default to body for backward compatibility
418
+ for (const [key, value] of Object.entries(params)) {
419
+ if (!extractedParams.includes(key)) {
420
+ bodyParams[key] = value;
421
+ }
422
+ }
423
+ // Validate required parameters
424
+ const path = this.buildPath('/databases/{database_id}', pathParams);
425
+ // For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
426
+ const requestPath = path === '/' ? '' : path;
427
+ // Report initial progress if callback provided
428
+ if (options?.onProgress) {
429
+ await options.onProgress({
430
+ progress: 0,
431
+ total: 100,
432
+ message: `Starting get_database request...`
433
+ });
434
+ }
435
+ // Use standard HTTP client for other auth types with abort signal
436
+ const requestConfig = {};
437
+ if (options?.signal) {
438
+ requestConfig.signal = options.signal;
439
+ }
440
+ const response = await this.httpClient.get(requestPath, { params: queryParams, ...requestConfig });
441
+ // Report completion progress if callback provided
442
+ if (options?.onProgress) {
443
+ await options.onProgress({
444
+ progress: 100,
445
+ total: 100,
446
+ message: `Completed get_database request`
447
+ });
448
+ }
449
+ const duration = Date.now() - startTime;
450
+ this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
451
+ endpoint: 'get_database',
452
+ method: 'GET',
453
+ path: '/databases/{database_id}',
454
+ duration_ms: duration,
455
+ responseDataSize: JSON.stringify(response.data).length
456
+ });
457
+ return {
458
+ content: [
459
+ {
460
+ type: 'text',
461
+ text: JSON.stringify(response.data, null, 2)
462
+ }
463
+ ]
464
+ };
465
+ }
466
+ catch (error) {
467
+ const duration = Date.now() - startTime;
468
+ // Check if error is due to cancellation
469
+ if (axios.isCancel(error)) {
470
+ this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
471
+ endpoint: 'get_database',
472
+ method: 'GET',
473
+ path: '/databases/{database_id}',
474
+ duration_ms: duration
475
+ });
476
+ throw new Error('Request was cancelled');
477
+ }
478
+ this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
479
+ endpoint: 'get_database',
480
+ method: 'GET',
481
+ path: '/databases/{database_id}',
482
+ duration_ms: duration,
483
+ error: error instanceof Error ? error.message : String(error),
484
+ errorType: error instanceof Error ? error.constructor.name : 'unknown'
485
+ });
486
+ throw new Error(`Failed to execute get_database: ${error instanceof Error ? error.message : String(error)}`);
487
+ }
488
+ }
489
+ /* DEBUG: endpoint={"name":"query_database","method":"POST","path":"/databases/{database_id}/query","description":"Query database pages","parameters":{"database_id":{"type":"string","required":true,"description":"Database ID to query","location":"path"},"filter":{"type":"object","required":false,"description":"Filter object to apply","location":"body"},"sorts":{"type":"array","required":false,"description":"Array of sort objects","location":"body"},"start_cursor":{"type":"string","required":false,"description":"Pagination cursor","location":"body"},"page_size":{"type":"number","required":false,"description":"Number of results per page (max 100)","location":"body","default":100}},"response_format":"json","category":"Database Operations"} */
490
+ async queryDatabase(params, options) {
491
+ const startTime = Date.now();
492
+ this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
493
+ endpoint: 'query_database',
494
+ method: 'POST',
495
+ path: '/databases/{database_id}/query',
496
+ paramCount: Object.keys(params || {}).length,
497
+ paramKeys: Object.keys(params || {})
498
+ });
499
+ try {
500
+ // Extract and separate parameters by location: path, query, body
501
+ const pathTemplate = '/databases/{database_id}/query';
502
+ const pathParams = {};
503
+ const queryParams = {};
504
+ const bodyParams = {};
505
+ const extractedParams = [];
506
+ // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
507
+ const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
508
+ let match;
509
+ while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
510
+ const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
511
+ if (paramName && params[paramName] !== undefined) {
512
+ pathParams[paramName] = params[paramName];
513
+ extractedParams.push(paramName);
514
+ }
515
+ }
516
+ // Handle standard path templates: {resourceName}
517
+ const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
518
+ standardPathParams.forEach(paramTemplate => {
519
+ const paramName = paramTemplate.slice(1, -1); // Remove { }
520
+ // Only process if not already handled by Google template logic
521
+ if (!extractedParams.includes(paramName)) {
522
+ if (params[paramName] !== undefined) {
523
+ pathParams[paramName] = params[paramName];
524
+ extractedParams.push(paramName);
525
+ }
526
+ else {
527
+ // Provide default values for optional path parameters
528
+ if (paramName === 'userId') {
529
+ pathParams[paramName] = 'me'; // Default to authenticated user
530
+ extractedParams.push(paramName);
531
+ }
532
+ }
533
+ }
534
+ });
535
+ // Check if any parameter has raw_array flag
536
+ let hasRawArrayBody = false;
537
+ let rawBodyData = undefined;
538
+ // Separate remaining parameters by location (query vs body)
539
+ if (params[""] !== undefined) {
540
+ bodyParams[""] = params[""];
541
+ extractedParams.push("");
542
+ }
543
+ if (params[""] !== undefined) {
544
+ bodyParams[""] = params[""];
545
+ extractedParams.push("");
546
+ }
547
+ if (params[""] !== undefined) {
548
+ bodyParams[""] = params[""];
549
+ extractedParams.push("");
550
+ }
551
+ if (params[""] !== undefined) {
552
+ bodyParams[""] = params[""];
553
+ extractedParams.push("");
554
+ }
555
+ // Any remaining unprocessed parameters default to body for backward compatibility
556
+ for (const [key, value] of Object.entries(params)) {
557
+ if (!extractedParams.includes(key)) {
558
+ bodyParams[key] = value;
559
+ }
560
+ }
561
+ // Validate required parameters
562
+ const path = this.buildPath('/databases/{database_id}/query', pathParams);
563
+ // For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
564
+ const requestPath = path === '/' ? '' : path;
565
+ // Report initial progress if callback provided
566
+ if (options?.onProgress) {
567
+ await options.onProgress({
568
+ progress: 0,
569
+ total: 100,
570
+ message: `Starting query_database request...`
571
+ });
572
+ }
573
+ // Use standard HTTP client for other auth types with abort signal
574
+ const requestConfig = {};
575
+ if (options?.signal) {
576
+ requestConfig.signal = options.signal;
577
+ }
578
+ const response = await this.httpClient.post(requestPath, hasRawArrayBody ? rawBodyData : (Object.keys(bodyParams).length > 0 ? bodyParams : undefined), { params: queryParams, ...requestConfig });
579
+ // Report completion progress if callback provided
580
+ if (options?.onProgress) {
581
+ await options.onProgress({
582
+ progress: 100,
583
+ total: 100,
584
+ message: `Completed query_database request`
585
+ });
586
+ }
587
+ const duration = Date.now() - startTime;
588
+ this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
589
+ endpoint: 'query_database',
590
+ method: 'POST',
591
+ path: '/databases/{database_id}/query',
592
+ duration_ms: duration,
593
+ responseDataSize: JSON.stringify(response.data).length
594
+ });
595
+ return {
596
+ content: [
597
+ {
598
+ type: 'text',
599
+ text: JSON.stringify(response.data, null, 2)
600
+ }
601
+ ]
602
+ };
603
+ }
604
+ catch (error) {
605
+ const duration = Date.now() - startTime;
606
+ // Check if error is due to cancellation
607
+ if (axios.isCancel(error)) {
608
+ this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
609
+ endpoint: 'query_database',
610
+ method: 'POST',
611
+ path: '/databases/{database_id}/query',
612
+ duration_ms: duration
613
+ });
614
+ throw new Error('Request was cancelled');
615
+ }
616
+ this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
617
+ endpoint: 'query_database',
618
+ method: 'POST',
619
+ path: '/databases/{database_id}/query',
620
+ duration_ms: duration,
621
+ error: error instanceof Error ? error.message : String(error),
622
+ errorType: error instanceof Error ? error.constructor.name : 'unknown'
623
+ });
624
+ throw new Error(`Failed to execute query_database: ${error instanceof Error ? error.message : String(error)}`);
625
+ }
626
+ }
627
+ /* DEBUG: endpoint={"name":"create_database","method":"POST","path":"/databases","description":"Create a new database","parameters":{"parent":{"type":"object","required":true,"description":"Parent page object","location":"body"},"title":{"type":"array","required":true,"description":"Array of rich text objects for title","location":"body"},"properties":{"type":"object","required":true,"description":"Database properties schema","location":"body"},"icon":{"type":"object","required":false,"description":"Database icon object","location":"body"},"cover":{"type":"object","required":false,"description":"Database cover object","location":"body"}},"response_format":"json","category":"Page Creation & Editing"} */
628
+ async createDatabase(params, options) {
629
+ const startTime = Date.now();
630
+ this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
631
+ endpoint: 'create_database',
632
+ method: 'POST',
633
+ path: '/databases',
634
+ paramCount: Object.keys(params || {}).length,
635
+ paramKeys: Object.keys(params || {})
636
+ });
637
+ try {
638
+ // Extract and separate parameters by location: path, query, body
639
+ const pathTemplate = '/databases';
640
+ const pathParams = {};
641
+ const queryParams = {};
642
+ const bodyParams = {};
643
+ const extractedParams = [];
644
+ // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
645
+ const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
646
+ let match;
647
+ while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
648
+ const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
649
+ if (paramName && params[paramName] !== undefined) {
650
+ pathParams[paramName] = params[paramName];
651
+ extractedParams.push(paramName);
652
+ }
653
+ }
654
+ // Handle standard path templates: {resourceName}
655
+ const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
656
+ standardPathParams.forEach(paramTemplate => {
657
+ const paramName = paramTemplate.slice(1, -1); // Remove { }
658
+ // Only process if not already handled by Google template logic
659
+ if (!extractedParams.includes(paramName)) {
660
+ if (params[paramName] !== undefined) {
661
+ pathParams[paramName] = params[paramName];
662
+ extractedParams.push(paramName);
663
+ }
664
+ else {
665
+ // Provide default values for optional path parameters
666
+ if (paramName === 'userId') {
667
+ pathParams[paramName] = 'me'; // Default to authenticated user
668
+ extractedParams.push(paramName);
669
+ }
670
+ }
671
+ }
672
+ });
673
+ // Check if any parameter has raw_array flag
674
+ let hasRawArrayBody = false;
675
+ let rawBodyData = undefined;
676
+ // Separate remaining parameters by location (query vs body)
677
+ if (params[""] !== undefined) {
678
+ bodyParams[""] = params[""];
679
+ extractedParams.push("");
680
+ }
681
+ if (params[""] !== undefined) {
682
+ bodyParams[""] = params[""];
683
+ extractedParams.push("");
684
+ }
685
+ if (params[""] !== undefined) {
686
+ bodyParams[""] = params[""];
687
+ extractedParams.push("");
688
+ }
689
+ if (params[""] !== undefined) {
690
+ bodyParams[""] = params[""];
691
+ extractedParams.push("");
692
+ }
693
+ if (params[""] !== undefined) {
694
+ bodyParams[""] = params[""];
695
+ extractedParams.push("");
696
+ }
697
+ // Any remaining unprocessed parameters default to body for backward compatibility
698
+ for (const [key, value] of Object.entries(params)) {
699
+ if (!extractedParams.includes(key)) {
700
+ bodyParams[key] = value;
701
+ }
702
+ }
703
+ // Validate required parameters
704
+ const path = this.buildPath('/databases', pathParams);
705
+ // For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
706
+ const requestPath = path === '/' ? '' : path;
707
+ // Report initial progress if callback provided
708
+ if (options?.onProgress) {
709
+ await options.onProgress({
710
+ progress: 0,
711
+ total: 100,
712
+ message: `Starting create_database request...`
713
+ });
714
+ }
715
+ // Use standard HTTP client for other auth types with abort signal
716
+ const requestConfig = {};
717
+ if (options?.signal) {
718
+ requestConfig.signal = options.signal;
719
+ }
720
+ const response = await this.httpClient.post(requestPath, hasRawArrayBody ? rawBodyData : (Object.keys(bodyParams).length > 0 ? bodyParams : undefined), { params: queryParams, ...requestConfig });
721
+ // Report completion progress if callback provided
722
+ if (options?.onProgress) {
723
+ await options.onProgress({
724
+ progress: 100,
725
+ total: 100,
726
+ message: `Completed create_database request`
727
+ });
728
+ }
729
+ const duration = Date.now() - startTime;
730
+ this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
731
+ endpoint: 'create_database',
732
+ method: 'POST',
733
+ path: '/databases',
734
+ duration_ms: duration,
735
+ responseDataSize: JSON.stringify(response.data).length
736
+ });
737
+ return {
738
+ content: [
739
+ {
740
+ type: 'text',
741
+ text: JSON.stringify(response.data, null, 2)
742
+ }
743
+ ]
744
+ };
745
+ }
746
+ catch (error) {
747
+ const duration = Date.now() - startTime;
748
+ // Check if error is due to cancellation
749
+ if (axios.isCancel(error)) {
750
+ this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
751
+ endpoint: 'create_database',
752
+ method: 'POST',
753
+ path: '/databases',
754
+ duration_ms: duration
755
+ });
756
+ throw new Error('Request was cancelled');
757
+ }
758
+ this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
759
+ endpoint: 'create_database',
760
+ method: 'POST',
761
+ path: '/databases',
762
+ duration_ms: duration,
763
+ error: error instanceof Error ? error.message : String(error),
764
+ errorType: error instanceof Error ? error.constructor.name : 'unknown'
765
+ });
766
+ throw new Error(`Failed to execute create_database: ${error instanceof Error ? error.message : String(error)}`);
767
+ }
768
+ }
769
+ /* DEBUG: endpoint={"name":"update_database","method":"PATCH","path":"/databases/{database_id}","description":"Update database properties","parameters":{"database_id":{"type":"string","required":true,"description":"Database ID to update","location":"path"},"title":{"type":"array","required":false,"description":"Array of rich text objects for title","location":"body"},"properties":{"type":"object","required":false,"description":"Database properties to update","location":"body"},"icon":{"type":"object","required":false,"description":"Database icon object","location":"body"},"cover":{"type":"object","required":false,"description":"Database cover object","location":"body"}},"response_format":"json","category":"Page Creation & Editing"} */
770
+ async updateDatabase(params, options) {
771
+ const startTime = Date.now();
772
+ this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
773
+ endpoint: 'update_database',
774
+ method: 'PATCH',
775
+ path: '/databases/{database_id}',
776
+ paramCount: Object.keys(params || {}).length,
777
+ paramKeys: Object.keys(params || {})
778
+ });
779
+ try {
780
+ // Extract and separate parameters by location: path, query, body
781
+ const pathTemplate = '/databases/{database_id}';
782
+ const pathParams = {};
783
+ const queryParams = {};
784
+ const bodyParams = {};
785
+ const extractedParams = [];
786
+ // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
787
+ const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
788
+ let match;
789
+ while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
790
+ const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
791
+ if (paramName && params[paramName] !== undefined) {
792
+ pathParams[paramName] = params[paramName];
793
+ extractedParams.push(paramName);
794
+ }
795
+ }
796
+ // Handle standard path templates: {resourceName}
797
+ const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
798
+ standardPathParams.forEach(paramTemplate => {
799
+ const paramName = paramTemplate.slice(1, -1); // Remove { }
800
+ // Only process if not already handled by Google template logic
801
+ if (!extractedParams.includes(paramName)) {
802
+ if (params[paramName] !== undefined) {
803
+ pathParams[paramName] = params[paramName];
804
+ extractedParams.push(paramName);
805
+ }
806
+ else {
807
+ // Provide default values for optional path parameters
808
+ if (paramName === 'userId') {
809
+ pathParams[paramName] = 'me'; // Default to authenticated user
810
+ extractedParams.push(paramName);
811
+ }
812
+ }
813
+ }
814
+ });
815
+ // Check if any parameter has raw_array flag
816
+ let hasRawArrayBody = false;
817
+ let rawBodyData = undefined;
818
+ // Separate remaining parameters by location (query vs body)
819
+ if (params[""] !== undefined) {
820
+ bodyParams[""] = params[""];
821
+ extractedParams.push("");
822
+ }
823
+ if (params[""] !== undefined) {
824
+ bodyParams[""] = params[""];
825
+ extractedParams.push("");
826
+ }
827
+ if (params[""] !== undefined) {
828
+ bodyParams[""] = params[""];
829
+ extractedParams.push("");
830
+ }
831
+ if (params[""] !== undefined) {
832
+ bodyParams[""] = params[""];
833
+ extractedParams.push("");
834
+ }
835
+ // Any remaining unprocessed parameters default to body for backward compatibility
836
+ for (const [key, value] of Object.entries(params)) {
837
+ if (!extractedParams.includes(key)) {
838
+ bodyParams[key] = value;
839
+ }
840
+ }
841
+ // Validate required parameters
842
+ const path = this.buildPath('/databases/{database_id}', pathParams);
843
+ // For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
844
+ const requestPath = path === '/' ? '' : path;
845
+ // Report initial progress if callback provided
846
+ if (options?.onProgress) {
847
+ await options.onProgress({
848
+ progress: 0,
849
+ total: 100,
850
+ message: `Starting update_database request...`
851
+ });
852
+ }
853
+ // Use standard HTTP client for other auth types with abort signal
854
+ const requestConfig = {};
855
+ if (options?.signal) {
856
+ requestConfig.signal = options.signal;
857
+ }
858
+ const response = await this.httpClient.patch(requestPath, hasRawArrayBody ? rawBodyData : (Object.keys(bodyParams).length > 0 ? bodyParams : undefined), { params: queryParams, ...requestConfig });
859
+ // Report completion progress if callback provided
860
+ if (options?.onProgress) {
861
+ await options.onProgress({
862
+ progress: 100,
863
+ total: 100,
864
+ message: `Completed update_database request`
865
+ });
866
+ }
867
+ const duration = Date.now() - startTime;
868
+ this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
869
+ endpoint: 'update_database',
870
+ method: 'PATCH',
871
+ path: '/databases/{database_id}',
872
+ duration_ms: duration,
873
+ responseDataSize: JSON.stringify(response.data).length
874
+ });
875
+ return {
876
+ content: [
877
+ {
878
+ type: 'text',
879
+ text: JSON.stringify(response.data, null, 2)
880
+ }
881
+ ]
882
+ };
883
+ }
884
+ catch (error) {
885
+ const duration = Date.now() - startTime;
886
+ // Check if error is due to cancellation
887
+ if (axios.isCancel(error)) {
888
+ this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
889
+ endpoint: 'update_database',
890
+ method: 'PATCH',
891
+ path: '/databases/{database_id}',
892
+ duration_ms: duration
893
+ });
894
+ throw new Error('Request was cancelled');
895
+ }
896
+ this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
897
+ endpoint: 'update_database',
898
+ method: 'PATCH',
899
+ path: '/databases/{database_id}',
900
+ duration_ms: duration,
901
+ error: error instanceof Error ? error.message : String(error),
902
+ errorType: error instanceof Error ? error.constructor.name : 'unknown'
903
+ });
904
+ throw new Error(`Failed to execute update_database: ${error instanceof Error ? error.message : String(error)}`);
905
+ }
906
+ }
907
+ /* DEBUG: endpoint={"name":"get_page","method":"GET","path":"/pages/{page_id}","description":"Get page by ID","parameters":{"page_id":{"type":"string","required":true,"description":"Page ID to fetch","location":"path"}},"response_format":"json","category":"Page Creation & Editing"} */
908
+ async getPage(params, options) {
909
+ const startTime = Date.now();
910
+ this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
911
+ endpoint: 'get_page',
912
+ method: 'GET',
913
+ path: '/pages/{page_id}',
914
+ paramCount: Object.keys(params || {}).length,
915
+ paramKeys: Object.keys(params || {})
916
+ });
917
+ try {
918
+ // Extract and separate parameters by location: path, query, body
919
+ const pathTemplate = '/pages/{page_id}';
920
+ const pathParams = {};
921
+ const queryParams = {};
922
+ const bodyParams = {};
923
+ const extractedParams = [];
924
+ // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
925
+ const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
926
+ let match;
927
+ while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
928
+ const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
929
+ if (paramName && params[paramName] !== undefined) {
930
+ pathParams[paramName] = params[paramName];
931
+ extractedParams.push(paramName);
932
+ }
933
+ }
934
+ // Handle standard path templates: {resourceName}
935
+ const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
936
+ standardPathParams.forEach(paramTemplate => {
937
+ const paramName = paramTemplate.slice(1, -1); // Remove { }
938
+ // Only process if not already handled by Google template logic
939
+ if (!extractedParams.includes(paramName)) {
940
+ if (params[paramName] !== undefined) {
941
+ pathParams[paramName] = params[paramName];
942
+ extractedParams.push(paramName);
943
+ }
944
+ else {
945
+ // Provide default values for optional path parameters
946
+ if (paramName === 'userId') {
947
+ pathParams[paramName] = 'me'; // Default to authenticated user
948
+ extractedParams.push(paramName);
949
+ }
950
+ }
951
+ }
952
+ });
953
+ // Check if any parameter has raw_array flag
954
+ let hasRawArrayBody = false;
955
+ let rawBodyData = undefined;
956
+ // Separate remaining parameters by location (query vs body)
957
+ // Any remaining unprocessed parameters default to body for backward compatibility
958
+ for (const [key, value] of Object.entries(params)) {
959
+ if (!extractedParams.includes(key)) {
960
+ bodyParams[key] = value;
961
+ }
962
+ }
963
+ // Validate required parameters
964
+ const path = this.buildPath('/pages/{page_id}', pathParams);
965
+ // For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
966
+ const requestPath = path === '/' ? '' : path;
967
+ // Report initial progress if callback provided
968
+ if (options?.onProgress) {
969
+ await options.onProgress({
970
+ progress: 0,
971
+ total: 100,
972
+ message: `Starting get_page request...`
973
+ });
974
+ }
975
+ // Use standard HTTP client for other auth types with abort signal
976
+ const requestConfig = {};
977
+ if (options?.signal) {
978
+ requestConfig.signal = options.signal;
979
+ }
980
+ const response = await this.httpClient.get(requestPath, { params: queryParams, ...requestConfig });
981
+ // Report completion progress if callback provided
982
+ if (options?.onProgress) {
983
+ await options.onProgress({
984
+ progress: 100,
985
+ total: 100,
986
+ message: `Completed get_page request`
987
+ });
988
+ }
989
+ const duration = Date.now() - startTime;
990
+ this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
991
+ endpoint: 'get_page',
992
+ method: 'GET',
993
+ path: '/pages/{page_id}',
994
+ duration_ms: duration,
995
+ responseDataSize: JSON.stringify(response.data).length
996
+ });
997
+ return {
998
+ content: [
999
+ {
1000
+ type: 'text',
1001
+ text: JSON.stringify(response.data, null, 2)
1002
+ }
1003
+ ]
1004
+ };
1005
+ }
1006
+ catch (error) {
1007
+ const duration = Date.now() - startTime;
1008
+ // Check if error is due to cancellation
1009
+ if (axios.isCancel(error)) {
1010
+ this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
1011
+ endpoint: 'get_page',
1012
+ method: 'GET',
1013
+ path: '/pages/{page_id}',
1014
+ duration_ms: duration
1015
+ });
1016
+ throw new Error('Request was cancelled');
1017
+ }
1018
+ this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
1019
+ endpoint: 'get_page',
1020
+ method: 'GET',
1021
+ path: '/pages/{page_id}',
1022
+ duration_ms: duration,
1023
+ error: error instanceof Error ? error.message : String(error),
1024
+ errorType: error instanceof Error ? error.constructor.name : 'unknown'
1025
+ });
1026
+ throw new Error(`Failed to execute get_page: ${error instanceof Error ? error.message : String(error)}`);
1027
+ }
1028
+ }
1029
+ /* DEBUG: endpoint={"name":"create_page","method":"POST","path":"/pages","description":"Create a new page. Note: Creating pages directly in workspace root requires special permissions - use database or page parents instead.","parameters":{"parent":{"type":"object","required":true,"description":"Parent object (database or page)","location":"body"},"properties":{"type":"object","required":false,"description":"Page properties (required for database pages)","location":"body"},"children":{"type":"array","required":false,"description":"Array of block objects for page content","location":"body"},"icon":{"type":"object","required":false,"description":"Page icon object","location":"body"},"cover":{"type":"object","required":false,"description":"Page cover object","location":"body"}},"response_format":"json","category":"Page Creation & Editing"} */
1030
+ async createPage(params, options) {
1031
+ const startTime = Date.now();
1032
+ this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
1033
+ endpoint: 'create_page',
1034
+ method: 'POST',
1035
+ path: '/pages',
1036
+ paramCount: Object.keys(params || {}).length,
1037
+ paramKeys: Object.keys(params || {})
1038
+ });
1039
+ try {
1040
+ // Extract and separate parameters by location: path, query, body
1041
+ const pathTemplate = '/pages';
1042
+ const pathParams = {};
1043
+ const queryParams = {};
1044
+ const bodyParams = {};
1045
+ const extractedParams = [];
1046
+ // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
1047
+ const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
1048
+ let match;
1049
+ while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
1050
+ const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
1051
+ if (paramName && params[paramName] !== undefined) {
1052
+ pathParams[paramName] = params[paramName];
1053
+ extractedParams.push(paramName);
1054
+ }
1055
+ }
1056
+ // Handle standard path templates: {resourceName}
1057
+ const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
1058
+ standardPathParams.forEach(paramTemplate => {
1059
+ const paramName = paramTemplate.slice(1, -1); // Remove { }
1060
+ // Only process if not already handled by Google template logic
1061
+ if (!extractedParams.includes(paramName)) {
1062
+ if (params[paramName] !== undefined) {
1063
+ pathParams[paramName] = params[paramName];
1064
+ extractedParams.push(paramName);
1065
+ }
1066
+ else {
1067
+ // Provide default values for optional path parameters
1068
+ if (paramName === 'userId') {
1069
+ pathParams[paramName] = 'me'; // Default to authenticated user
1070
+ extractedParams.push(paramName);
1071
+ }
1072
+ }
1073
+ }
1074
+ });
1075
+ // Check if any parameter has raw_array flag
1076
+ let hasRawArrayBody = false;
1077
+ let rawBodyData = undefined;
1078
+ // Separate remaining parameters by location (query vs body)
1079
+ if (params[""] !== undefined) {
1080
+ bodyParams[""] = params[""];
1081
+ extractedParams.push("");
1082
+ }
1083
+ if (params[""] !== undefined) {
1084
+ bodyParams[""] = params[""];
1085
+ extractedParams.push("");
1086
+ }
1087
+ if (params[""] !== undefined) {
1088
+ bodyParams[""] = params[""];
1089
+ extractedParams.push("");
1090
+ }
1091
+ if (params[""] !== undefined) {
1092
+ bodyParams[""] = params[""];
1093
+ extractedParams.push("");
1094
+ }
1095
+ if (params[""] !== undefined) {
1096
+ bodyParams[""] = params[""];
1097
+ extractedParams.push("");
1098
+ }
1099
+ // Any remaining unprocessed parameters default to body for backward compatibility
1100
+ for (const [key, value] of Object.entries(params)) {
1101
+ if (!extractedParams.includes(key)) {
1102
+ bodyParams[key] = value;
1103
+ }
1104
+ }
1105
+ // Validate required parameters
1106
+ const path = this.buildPath('/pages', pathParams);
1107
+ // For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
1108
+ const requestPath = path === '/' ? '' : path;
1109
+ // Report initial progress if callback provided
1110
+ if (options?.onProgress) {
1111
+ await options.onProgress({
1112
+ progress: 0,
1113
+ total: 100,
1114
+ message: `Starting create_page request...`
1115
+ });
1116
+ }
1117
+ // Use standard HTTP client for other auth types with abort signal
1118
+ const requestConfig = {};
1119
+ if (options?.signal) {
1120
+ requestConfig.signal = options.signal;
1121
+ }
1122
+ const response = await this.httpClient.post(requestPath, hasRawArrayBody ? rawBodyData : (Object.keys(bodyParams).length > 0 ? bodyParams : undefined), { params: queryParams, ...requestConfig });
1123
+ // Report completion progress if callback provided
1124
+ if (options?.onProgress) {
1125
+ await options.onProgress({
1126
+ progress: 100,
1127
+ total: 100,
1128
+ message: `Completed create_page request`
1129
+ });
1130
+ }
1131
+ const duration = Date.now() - startTime;
1132
+ this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
1133
+ endpoint: 'create_page',
1134
+ method: 'POST',
1135
+ path: '/pages',
1136
+ duration_ms: duration,
1137
+ responseDataSize: JSON.stringify(response.data).length
1138
+ });
1139
+ return {
1140
+ content: [
1141
+ {
1142
+ type: 'text',
1143
+ text: JSON.stringify(response.data, null, 2)
1144
+ }
1145
+ ]
1146
+ };
1147
+ }
1148
+ catch (error) {
1149
+ const duration = Date.now() - startTime;
1150
+ // Check if error is due to cancellation
1151
+ if (axios.isCancel(error)) {
1152
+ this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
1153
+ endpoint: 'create_page',
1154
+ method: 'POST',
1155
+ path: '/pages',
1156
+ duration_ms: duration
1157
+ });
1158
+ throw new Error('Request was cancelled');
1159
+ }
1160
+ this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
1161
+ endpoint: 'create_page',
1162
+ method: 'POST',
1163
+ path: '/pages',
1164
+ duration_ms: duration,
1165
+ error: error instanceof Error ? error.message : String(error),
1166
+ errorType: error instanceof Error ? error.constructor.name : 'unknown'
1167
+ });
1168
+ throw new Error(`Failed to execute create_page: ${error instanceof Error ? error.message : String(error)}`);
1169
+ }
1170
+ }
1171
+ /* DEBUG: endpoint={"name":"update_page","method":"PATCH","path":"/pages/{page_id}","description":"Update page properties","parameters":{"page_id":{"type":"string","required":true,"description":"Page ID to update","location":"path"},"properties":{"type":"object","required":false,"description":"Page properties to update","location":"body"},"archived":{"type":"boolean","required":false,"description":"Archive or unarchive the page","location":"body"},"icon":{"type":"object","required":false,"description":"Page icon object","location":"body"},"cover":{"type":"object","required":false,"description":"Page cover object","location":"body"}},"response_format":"json","category":"Page Creation & Editing"} */
1172
+ async updatePage(params, options) {
1173
+ const startTime = Date.now();
1174
+ this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
1175
+ endpoint: 'update_page',
1176
+ method: 'PATCH',
1177
+ path: '/pages/{page_id}',
1178
+ paramCount: Object.keys(params || {}).length,
1179
+ paramKeys: Object.keys(params || {})
1180
+ });
1181
+ try {
1182
+ // Extract and separate parameters by location: path, query, body
1183
+ const pathTemplate = '/pages/{page_id}';
1184
+ const pathParams = {};
1185
+ const queryParams = {};
1186
+ const bodyParams = {};
1187
+ const extractedParams = [];
1188
+ // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
1189
+ const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
1190
+ let match;
1191
+ while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
1192
+ const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
1193
+ if (paramName && params[paramName] !== undefined) {
1194
+ pathParams[paramName] = params[paramName];
1195
+ extractedParams.push(paramName);
1196
+ }
1197
+ }
1198
+ // Handle standard path templates: {resourceName}
1199
+ const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
1200
+ standardPathParams.forEach(paramTemplate => {
1201
+ const paramName = paramTemplate.slice(1, -1); // Remove { }
1202
+ // Only process if not already handled by Google template logic
1203
+ if (!extractedParams.includes(paramName)) {
1204
+ if (params[paramName] !== undefined) {
1205
+ pathParams[paramName] = params[paramName];
1206
+ extractedParams.push(paramName);
1207
+ }
1208
+ else {
1209
+ // Provide default values for optional path parameters
1210
+ if (paramName === 'userId') {
1211
+ pathParams[paramName] = 'me'; // Default to authenticated user
1212
+ extractedParams.push(paramName);
1213
+ }
1214
+ }
1215
+ }
1216
+ });
1217
+ // Check if any parameter has raw_array flag
1218
+ let hasRawArrayBody = false;
1219
+ let rawBodyData = undefined;
1220
+ // Separate remaining parameters by location (query vs body)
1221
+ if (params[""] !== undefined) {
1222
+ bodyParams[""] = params[""];
1223
+ extractedParams.push("");
1224
+ }
1225
+ if (params[""] !== undefined) {
1226
+ bodyParams[""] = params[""];
1227
+ extractedParams.push("");
1228
+ }
1229
+ if (params[""] !== undefined) {
1230
+ bodyParams[""] = params[""];
1231
+ extractedParams.push("");
1232
+ }
1233
+ if (params[""] !== undefined) {
1234
+ bodyParams[""] = params[""];
1235
+ extractedParams.push("");
1236
+ }
1237
+ // Any remaining unprocessed parameters default to body for backward compatibility
1238
+ for (const [key, value] of Object.entries(params)) {
1239
+ if (!extractedParams.includes(key)) {
1240
+ bodyParams[key] = value;
1241
+ }
1242
+ }
1243
+ // Validate required parameters
1244
+ const path = this.buildPath('/pages/{page_id}', pathParams);
1245
+ // For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
1246
+ const requestPath = path === '/' ? '' : path;
1247
+ // Report initial progress if callback provided
1248
+ if (options?.onProgress) {
1249
+ await options.onProgress({
1250
+ progress: 0,
1251
+ total: 100,
1252
+ message: `Starting update_page request...`
1253
+ });
1254
+ }
1255
+ // Use standard HTTP client for other auth types with abort signal
1256
+ const requestConfig = {};
1257
+ if (options?.signal) {
1258
+ requestConfig.signal = options.signal;
1259
+ }
1260
+ const response = await this.httpClient.patch(requestPath, hasRawArrayBody ? rawBodyData : (Object.keys(bodyParams).length > 0 ? bodyParams : undefined), { params: queryParams, ...requestConfig });
1261
+ // Report completion progress if callback provided
1262
+ if (options?.onProgress) {
1263
+ await options.onProgress({
1264
+ progress: 100,
1265
+ total: 100,
1266
+ message: `Completed update_page request`
1267
+ });
1268
+ }
1269
+ const duration = Date.now() - startTime;
1270
+ this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
1271
+ endpoint: 'update_page',
1272
+ method: 'PATCH',
1273
+ path: '/pages/{page_id}',
1274
+ duration_ms: duration,
1275
+ responseDataSize: JSON.stringify(response.data).length
1276
+ });
1277
+ return {
1278
+ content: [
1279
+ {
1280
+ type: 'text',
1281
+ text: JSON.stringify(response.data, null, 2)
1282
+ }
1283
+ ]
1284
+ };
1285
+ }
1286
+ catch (error) {
1287
+ const duration = Date.now() - startTime;
1288
+ // Check if error is due to cancellation
1289
+ if (axios.isCancel(error)) {
1290
+ this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
1291
+ endpoint: 'update_page',
1292
+ method: 'PATCH',
1293
+ path: '/pages/{page_id}',
1294
+ duration_ms: duration
1295
+ });
1296
+ throw new Error('Request was cancelled');
1297
+ }
1298
+ this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
1299
+ endpoint: 'update_page',
1300
+ method: 'PATCH',
1301
+ path: '/pages/{page_id}',
1302
+ duration_ms: duration,
1303
+ error: error instanceof Error ? error.message : String(error),
1304
+ errorType: error instanceof Error ? error.constructor.name : 'unknown'
1305
+ });
1306
+ throw new Error(`Failed to execute update_page: ${error instanceof Error ? error.message : String(error)}`);
1307
+ }
1308
+ }
1309
+ /* DEBUG: endpoint={"name":"get_page_property","method":"GET","path":"/pages/{page_id}/properties/{property_id}","description":"Get page property by ID","parameters":{"page_id":{"type":"string","required":true,"description":"Page ID containing the property","location":"path"},"property_id":{"type":"string","required":true,"description":"Property ID to fetch","location":"path"},"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":"Page Creation & Editing"} */
1310
+ async getPageProperty(params, options) {
1311
+ const startTime = Date.now();
1312
+ this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
1313
+ endpoint: 'get_page_property',
1314
+ method: 'GET',
1315
+ path: '/pages/{page_id}/properties/{property_id}',
1316
+ paramCount: Object.keys(params || {}).length,
1317
+ paramKeys: Object.keys(params || {})
1318
+ });
1319
+ try {
1320
+ // Extract and separate parameters by location: path, query, body
1321
+ const pathTemplate = '/pages/{page_id}/properties/{property_id}';
1322
+ const pathParams = {};
1323
+ const queryParams = {};
1324
+ const bodyParams = {};
1325
+ const extractedParams = [];
1326
+ // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
1327
+ const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
1328
+ let match;
1329
+ while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
1330
+ const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
1331
+ if (paramName && params[paramName] !== undefined) {
1332
+ pathParams[paramName] = params[paramName];
1333
+ extractedParams.push(paramName);
1334
+ }
1335
+ }
1336
+ // Handle standard path templates: {resourceName}
1337
+ const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
1338
+ standardPathParams.forEach(paramTemplate => {
1339
+ const paramName = paramTemplate.slice(1, -1); // Remove { }
1340
+ // Only process if not already handled by Google template logic
1341
+ if (!extractedParams.includes(paramName)) {
1342
+ if (params[paramName] !== undefined) {
1343
+ pathParams[paramName] = params[paramName];
1344
+ extractedParams.push(paramName);
1345
+ }
1346
+ else {
1347
+ // Provide default values for optional path parameters
1348
+ if (paramName === 'userId') {
1349
+ pathParams[paramName] = 'me'; // Default to authenticated user
1350
+ extractedParams.push(paramName);
1351
+ }
1352
+ }
1353
+ }
1354
+ });
1355
+ // Check if any parameter has raw_array flag
1356
+ let hasRawArrayBody = false;
1357
+ let rawBodyData = undefined;
1358
+ // Separate remaining parameters by location (query vs body)
1359
+ if (params[""] !== undefined) {
1360
+ queryParams[""] = params[""];
1361
+ extractedParams.push("");
1362
+ }
1363
+ if (params[""] !== undefined) {
1364
+ queryParams[""] = params[""];
1365
+ extractedParams.push("");
1366
+ }
1367
+ // Any remaining unprocessed parameters default to body for backward compatibility
1368
+ for (const [key, value] of Object.entries(params)) {
1369
+ if (!extractedParams.includes(key)) {
1370
+ bodyParams[key] = value;
1371
+ }
1372
+ }
1373
+ // Validate required parameters
1374
+ const path = this.buildPath('/pages/{page_id}/properties/{property_id}', pathParams);
1375
+ // For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
1376
+ const requestPath = path === '/' ? '' : path;
1377
+ // Report initial progress if callback provided
1378
+ if (options?.onProgress) {
1379
+ await options.onProgress({
1380
+ progress: 0,
1381
+ total: 100,
1382
+ message: `Starting get_page_property request...`
1383
+ });
1384
+ }
1385
+ // Use standard HTTP client for other auth types with abort signal
1386
+ const requestConfig = {};
1387
+ if (options?.signal) {
1388
+ requestConfig.signal = options.signal;
1389
+ }
1390
+ const response = await this.httpClient.get(requestPath, { params: queryParams, ...requestConfig });
1391
+ // Report completion progress if callback provided
1392
+ if (options?.onProgress) {
1393
+ await options.onProgress({
1394
+ progress: 100,
1395
+ total: 100,
1396
+ message: `Completed get_page_property request`
1397
+ });
1398
+ }
1399
+ const duration = Date.now() - startTime;
1400
+ this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
1401
+ endpoint: 'get_page_property',
1402
+ method: 'GET',
1403
+ path: '/pages/{page_id}/properties/{property_id}',
1404
+ duration_ms: duration,
1405
+ responseDataSize: JSON.stringify(response.data).length
1406
+ });
1407
+ return {
1408
+ content: [
1409
+ {
1410
+ type: 'text',
1411
+ text: JSON.stringify(response.data, null, 2)
1412
+ }
1413
+ ]
1414
+ };
1415
+ }
1416
+ catch (error) {
1417
+ const duration = Date.now() - startTime;
1418
+ // Check if error is due to cancellation
1419
+ if (axios.isCancel(error)) {
1420
+ this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
1421
+ endpoint: 'get_page_property',
1422
+ method: 'GET',
1423
+ path: '/pages/{page_id}/properties/{property_id}',
1424
+ duration_ms: duration
1425
+ });
1426
+ throw new Error('Request was cancelled');
1427
+ }
1428
+ this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
1429
+ endpoint: 'get_page_property',
1430
+ method: 'GET',
1431
+ path: '/pages/{page_id}/properties/{property_id}',
1432
+ duration_ms: duration,
1433
+ error: error instanceof Error ? error.message : String(error),
1434
+ errorType: error instanceof Error ? error.constructor.name : 'unknown'
1435
+ });
1436
+ throw new Error(`Failed to execute get_page_property: ${error instanceof Error ? error.message : String(error)}`);
1437
+ }
1438
+ }
1439
+ /* DEBUG: endpoint={"name":"get_block_children","method":"GET","path":"/blocks/{block_id}/children","description":"Get block children","parameters":{"block_id":{"type":"string","required":true,"description":"Block ID to get children for","location":"path"},"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":"Block Content"} */
1440
+ async getBlockChildren(params, options) {
1441
+ const startTime = Date.now();
1442
+ this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
1443
+ endpoint: 'get_block_children',
1444
+ method: 'GET',
1445
+ path: '/blocks/{block_id}/children',
1446
+ paramCount: Object.keys(params || {}).length,
1447
+ paramKeys: Object.keys(params || {})
1448
+ });
1449
+ try {
1450
+ // Extract and separate parameters by location: path, query, body
1451
+ const pathTemplate = '/blocks/{block_id}/children';
1452
+ const pathParams = {};
1453
+ const queryParams = {};
1454
+ const bodyParams = {};
1455
+ const extractedParams = [];
1456
+ // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
1457
+ const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
1458
+ let match;
1459
+ while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
1460
+ const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
1461
+ if (paramName && params[paramName] !== undefined) {
1462
+ pathParams[paramName] = params[paramName];
1463
+ extractedParams.push(paramName);
1464
+ }
1465
+ }
1466
+ // Handle standard path templates: {resourceName}
1467
+ const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
1468
+ standardPathParams.forEach(paramTemplate => {
1469
+ const paramName = paramTemplate.slice(1, -1); // Remove { }
1470
+ // Only process if not already handled by Google template logic
1471
+ if (!extractedParams.includes(paramName)) {
1472
+ if (params[paramName] !== undefined) {
1473
+ pathParams[paramName] = params[paramName];
1474
+ extractedParams.push(paramName);
1475
+ }
1476
+ else {
1477
+ // Provide default values for optional path parameters
1478
+ if (paramName === 'userId') {
1479
+ pathParams[paramName] = 'me'; // Default to authenticated user
1480
+ extractedParams.push(paramName);
1481
+ }
1482
+ }
1483
+ }
1484
+ });
1485
+ // Check if any parameter has raw_array flag
1486
+ let hasRawArrayBody = false;
1487
+ let rawBodyData = undefined;
1488
+ // Separate remaining parameters by location (query vs body)
1489
+ if (params[""] !== undefined) {
1490
+ queryParams[""] = params[""];
1491
+ extractedParams.push("");
1492
+ }
1493
+ if (params[""] !== undefined) {
1494
+ queryParams[""] = params[""];
1495
+ extractedParams.push("");
1496
+ }
1497
+ // Any remaining unprocessed parameters default to body for backward compatibility
1498
+ for (const [key, value] of Object.entries(params)) {
1499
+ if (!extractedParams.includes(key)) {
1500
+ bodyParams[key] = value;
1501
+ }
1502
+ }
1503
+ // Validate required parameters
1504
+ const path = this.buildPath('/blocks/{block_id}/children', pathParams);
1505
+ // For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
1506
+ const requestPath = path === '/' ? '' : path;
1507
+ // Report initial progress if callback provided
1508
+ if (options?.onProgress) {
1509
+ await options.onProgress({
1510
+ progress: 0,
1511
+ total: 100,
1512
+ message: `Starting get_block_children request...`
1513
+ });
1514
+ }
1515
+ // Use standard HTTP client for other auth types with abort signal
1516
+ const requestConfig = {};
1517
+ if (options?.signal) {
1518
+ requestConfig.signal = options.signal;
1519
+ }
1520
+ const response = await this.httpClient.get(requestPath, { params: queryParams, ...requestConfig });
1521
+ // Report completion progress if callback provided
1522
+ if (options?.onProgress) {
1523
+ await options.onProgress({
1524
+ progress: 100,
1525
+ total: 100,
1526
+ message: `Completed get_block_children request`
1527
+ });
1528
+ }
1529
+ const duration = Date.now() - startTime;
1530
+ this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
1531
+ endpoint: 'get_block_children',
1532
+ method: 'GET',
1533
+ path: '/blocks/{block_id}/children',
1534
+ duration_ms: duration,
1535
+ responseDataSize: JSON.stringify(response.data).length
1536
+ });
1537
+ return {
1538
+ content: [
1539
+ {
1540
+ type: 'text',
1541
+ text: JSON.stringify(response.data, null, 2)
1542
+ }
1543
+ ]
1544
+ };
1545
+ }
1546
+ catch (error) {
1547
+ const duration = Date.now() - startTime;
1548
+ // Check if error is due to cancellation
1549
+ if (axios.isCancel(error)) {
1550
+ this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
1551
+ endpoint: 'get_block_children',
1552
+ method: 'GET',
1553
+ path: '/blocks/{block_id}/children',
1554
+ duration_ms: duration
1555
+ });
1556
+ throw new Error('Request was cancelled');
1557
+ }
1558
+ this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
1559
+ endpoint: 'get_block_children',
1560
+ method: 'GET',
1561
+ path: '/blocks/{block_id}/children',
1562
+ duration_ms: duration,
1563
+ error: error instanceof Error ? error.message : String(error),
1564
+ errorType: error instanceof Error ? error.constructor.name : 'unknown'
1565
+ });
1566
+ throw new Error(`Failed to execute get_block_children: ${error instanceof Error ? error.message : String(error)}`);
1567
+ }
1568
+ }
1569
+ /* DEBUG: endpoint={"name":"append_block_children","method":"PATCH","path":"/blocks/{block_id}/children","description":"Append blocks to a parent block","parameters":{"block_id":{"type":"string","required":true,"description":"Parent block ID","location":"path"},"children":{"type":"array","required":true,"description":"Array of block objects to append","location":"body"}},"response_format":"json","category":"Block Content"} */
1570
+ async appendBlockChildren(params, options) {
1571
+ const startTime = Date.now();
1572
+ this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
1573
+ endpoint: 'append_block_children',
1574
+ method: 'PATCH',
1575
+ path: '/blocks/{block_id}/children',
1576
+ paramCount: Object.keys(params || {}).length,
1577
+ paramKeys: Object.keys(params || {})
1578
+ });
1579
+ try {
1580
+ // Extract and separate parameters by location: path, query, body
1581
+ const pathTemplate = '/blocks/{block_id}/children';
1582
+ const pathParams = {};
1583
+ const queryParams = {};
1584
+ const bodyParams = {};
1585
+ const extractedParams = [];
1586
+ // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
1587
+ const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
1588
+ let match;
1589
+ while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
1590
+ const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
1591
+ if (paramName && params[paramName] !== undefined) {
1592
+ pathParams[paramName] = params[paramName];
1593
+ extractedParams.push(paramName);
1594
+ }
1595
+ }
1596
+ // Handle standard path templates: {resourceName}
1597
+ const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
1598
+ standardPathParams.forEach(paramTemplate => {
1599
+ const paramName = paramTemplate.slice(1, -1); // Remove { }
1600
+ // Only process if not already handled by Google template logic
1601
+ if (!extractedParams.includes(paramName)) {
1602
+ if (params[paramName] !== undefined) {
1603
+ pathParams[paramName] = params[paramName];
1604
+ extractedParams.push(paramName);
1605
+ }
1606
+ else {
1607
+ // Provide default values for optional path parameters
1608
+ if (paramName === 'userId') {
1609
+ pathParams[paramName] = 'me'; // Default to authenticated user
1610
+ extractedParams.push(paramName);
1611
+ }
1612
+ }
1613
+ }
1614
+ });
1615
+ // Check if any parameter has raw_array flag
1616
+ let hasRawArrayBody = false;
1617
+ let rawBodyData = undefined;
1618
+ // Separate remaining parameters by location (query vs body)
1619
+ if (params[""] !== undefined) {
1620
+ bodyParams[""] = params[""];
1621
+ extractedParams.push("");
1622
+ }
1623
+ // Any remaining unprocessed parameters default to body for backward compatibility
1624
+ for (const [key, value] of Object.entries(params)) {
1625
+ if (!extractedParams.includes(key)) {
1626
+ bodyParams[key] = value;
1627
+ }
1628
+ }
1629
+ // Validate required parameters
1630
+ const path = this.buildPath('/blocks/{block_id}/children', pathParams);
1631
+ // For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
1632
+ const requestPath = path === '/' ? '' : path;
1633
+ // Report initial progress if callback provided
1634
+ if (options?.onProgress) {
1635
+ await options.onProgress({
1636
+ progress: 0,
1637
+ total: 100,
1638
+ message: `Starting append_block_children request...`
1639
+ });
1640
+ }
1641
+ // Use standard HTTP client for other auth types with abort signal
1642
+ const requestConfig = {};
1643
+ if (options?.signal) {
1644
+ requestConfig.signal = options.signal;
1645
+ }
1646
+ const response = await this.httpClient.patch(requestPath, hasRawArrayBody ? rawBodyData : (Object.keys(bodyParams).length > 0 ? bodyParams : undefined), { params: queryParams, ...requestConfig });
1647
+ // Report completion progress if callback provided
1648
+ if (options?.onProgress) {
1649
+ await options.onProgress({
1650
+ progress: 100,
1651
+ total: 100,
1652
+ message: `Completed append_block_children request`
1653
+ });
1654
+ }
1655
+ const duration = Date.now() - startTime;
1656
+ this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
1657
+ endpoint: 'append_block_children',
1658
+ method: 'PATCH',
1659
+ path: '/blocks/{block_id}/children',
1660
+ duration_ms: duration,
1661
+ responseDataSize: JSON.stringify(response.data).length
1662
+ });
1663
+ return {
1664
+ content: [
1665
+ {
1666
+ type: 'text',
1667
+ text: JSON.stringify(response.data, null, 2)
1668
+ }
1669
+ ]
1670
+ };
1671
+ }
1672
+ catch (error) {
1673
+ const duration = Date.now() - startTime;
1674
+ // Check if error is due to cancellation
1675
+ if (axios.isCancel(error)) {
1676
+ this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
1677
+ endpoint: 'append_block_children',
1678
+ method: 'PATCH',
1679
+ path: '/blocks/{block_id}/children',
1680
+ duration_ms: duration
1681
+ });
1682
+ throw new Error('Request was cancelled');
1683
+ }
1684
+ this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
1685
+ endpoint: 'append_block_children',
1686
+ method: 'PATCH',
1687
+ path: '/blocks/{block_id}/children',
1688
+ duration_ms: duration,
1689
+ error: error instanceof Error ? error.message : String(error),
1690
+ errorType: error instanceof Error ? error.constructor.name : 'unknown'
1691
+ });
1692
+ throw new Error(`Failed to execute append_block_children: ${error instanceof Error ? error.message : String(error)}`);
1693
+ }
1694
+ }
1695
+ /* DEBUG: endpoint={"name":"get_block","method":"GET","path":"/blocks/{block_id}","description":"Get block by ID","parameters":{"block_id":{"type":"string","required":true,"description":"Block ID to fetch","location":"path"}},"response_format":"json","category":"Block Content"} */
1696
+ async getBlock(params, options) {
1697
+ const startTime = Date.now();
1698
+ this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
1699
+ endpoint: 'get_block',
1700
+ method: 'GET',
1701
+ path: '/blocks/{block_id}',
1702
+ paramCount: Object.keys(params || {}).length,
1703
+ paramKeys: Object.keys(params || {})
1704
+ });
1705
+ try {
1706
+ // Extract and separate parameters by location: path, query, body
1707
+ const pathTemplate = '/blocks/{block_id}';
1708
+ const pathParams = {};
1709
+ const queryParams = {};
1710
+ const bodyParams = {};
1711
+ const extractedParams = [];
1712
+ // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
1713
+ const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
1714
+ let match;
1715
+ while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
1716
+ const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
1717
+ if (paramName && params[paramName] !== undefined) {
1718
+ pathParams[paramName] = params[paramName];
1719
+ extractedParams.push(paramName);
1720
+ }
1721
+ }
1722
+ // Handle standard path templates: {resourceName}
1723
+ const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
1724
+ standardPathParams.forEach(paramTemplate => {
1725
+ const paramName = paramTemplate.slice(1, -1); // Remove { }
1726
+ // Only process if not already handled by Google template logic
1727
+ if (!extractedParams.includes(paramName)) {
1728
+ if (params[paramName] !== undefined) {
1729
+ pathParams[paramName] = params[paramName];
1730
+ extractedParams.push(paramName);
1731
+ }
1732
+ else {
1733
+ // Provide default values for optional path parameters
1734
+ if (paramName === 'userId') {
1735
+ pathParams[paramName] = 'me'; // Default to authenticated user
1736
+ extractedParams.push(paramName);
1737
+ }
1738
+ }
1739
+ }
1740
+ });
1741
+ // Check if any parameter has raw_array flag
1742
+ let hasRawArrayBody = false;
1743
+ let rawBodyData = undefined;
1744
+ // Separate remaining parameters by location (query vs body)
1745
+ // Any remaining unprocessed parameters default to body for backward compatibility
1746
+ for (const [key, value] of Object.entries(params)) {
1747
+ if (!extractedParams.includes(key)) {
1748
+ bodyParams[key] = value;
1749
+ }
1750
+ }
1751
+ // Validate required parameters
1752
+ const path = this.buildPath('/blocks/{block_id}', pathParams);
1753
+ // For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
1754
+ const requestPath = path === '/' ? '' : path;
1755
+ // Report initial progress if callback provided
1756
+ if (options?.onProgress) {
1757
+ await options.onProgress({
1758
+ progress: 0,
1759
+ total: 100,
1760
+ message: `Starting get_block request...`
1761
+ });
1762
+ }
1763
+ // Use standard HTTP client for other auth types with abort signal
1764
+ const requestConfig = {};
1765
+ if (options?.signal) {
1766
+ requestConfig.signal = options.signal;
1767
+ }
1768
+ const response = await this.httpClient.get(requestPath, { params: queryParams, ...requestConfig });
1769
+ // Report completion progress if callback provided
1770
+ if (options?.onProgress) {
1771
+ await options.onProgress({
1772
+ progress: 100,
1773
+ total: 100,
1774
+ message: `Completed get_block request`
1775
+ });
1776
+ }
1777
+ const duration = Date.now() - startTime;
1778
+ this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
1779
+ endpoint: 'get_block',
1780
+ method: 'GET',
1781
+ path: '/blocks/{block_id}',
1782
+ duration_ms: duration,
1783
+ responseDataSize: JSON.stringify(response.data).length
1784
+ });
1785
+ return {
1786
+ content: [
1787
+ {
1788
+ type: 'text',
1789
+ text: JSON.stringify(response.data, null, 2)
1790
+ }
1791
+ ]
1792
+ };
1793
+ }
1794
+ catch (error) {
1795
+ const duration = Date.now() - startTime;
1796
+ // Check if error is due to cancellation
1797
+ if (axios.isCancel(error)) {
1798
+ this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
1799
+ endpoint: 'get_block',
1800
+ method: 'GET',
1801
+ path: '/blocks/{block_id}',
1802
+ duration_ms: duration
1803
+ });
1804
+ throw new Error('Request was cancelled');
1805
+ }
1806
+ this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
1807
+ endpoint: 'get_block',
1808
+ method: 'GET',
1809
+ path: '/blocks/{block_id}',
1810
+ duration_ms: duration,
1811
+ error: error instanceof Error ? error.message : String(error),
1812
+ errorType: error instanceof Error ? error.constructor.name : 'unknown'
1813
+ });
1814
+ throw new Error(`Failed to execute get_block: ${error instanceof Error ? error.message : String(error)}`);
1815
+ }
1816
+ }
1817
+ /* DEBUG: endpoint={"name":"update_block","method":"PATCH","path":"/blocks/{block_id}","description":"Update block content","parameters":{"block_id":{"type":"string","required":true,"description":"Block ID to update","location":"path"},"paragraph":{"type":"object","required":false,"description":"Updated paragraph block content","location":"body"},"heading_1":{"type":"object","required":false,"description":"Updated heading 1 block content","location":"body"},"heading_2":{"type":"object","required":false,"description":"Updated heading 2 block content","location":"body"},"heading_3":{"type":"object","required":false,"description":"Updated heading 3 block content","location":"body"},"bulleted_list_item":{"type":"object","required":false,"description":"Updated bulleted list item content","location":"body"},"numbered_list_item":{"type":"object","required":false,"description":"Updated numbered list item content","location":"body"},"to_do":{"type":"object","required":false,"description":"Updated to-do block content","location":"body"},"toggle":{"type":"object","required":false,"description":"Updated toggle block content","location":"body"},"code":{"type":"object","required":false,"description":"Updated code block content","location":"body"},"embed":{"type":"object","required":false,"description":"Updated embed block content","location":"body"},"image":{"type":"object","required":false,"description":"Updated image block content","location":"body"},"video":{"type":"object","required":false,"description":"Updated video block content","location":"body"},"file":{"type":"object","required":false,"description":"Updated file block content","location":"body"},"pdf":{"type":"object","required":false,"description":"Updated PDF block content","location":"body"},"bookmark":{"type":"object","required":false,"description":"Updated bookmark block content","location":"body"},"callout":{"type":"object","required":false,"description":"Updated callout block content","location":"body"},"quote":{"type":"object","required":false,"description":"Updated quote block content","location":"body"},"equation":{"type":"object","required":false,"description":"Updated equation block content","location":"body"},"divider":{"type":"object","required":false,"description":"Updated divider block content","location":"body"},"table_of_contents":{"type":"object","required":false,"description":"Updated table of contents block content","location":"body"},"column":{"type":"object","required":false,"description":"Updated column block content","location":"body"},"column_list":{"type":"object","required":false,"description":"Updated column list block content","location":"body"},"link_preview":{"type":"object","required":false,"description":"Updated link preview block content","location":"body"},"synced_block":{"type":"object","required":false,"description":"Updated synced block content","location":"body"},"table":{"type":"object","required":false,"description":"Updated table block content","location":"body"},"table_row":{"type":"object","required":false,"description":"Updated table row block content","location":"body"},"archived":{"type":"boolean","required":false,"description":"Archive or unarchive the block","location":"body"}},"response_format":"json","category":"Page Creation & Editing"} */
1818
+ async updateBlock(params, options) {
1819
+ const startTime = Date.now();
1820
+ this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
1821
+ endpoint: 'update_block',
1822
+ method: 'PATCH',
1823
+ path: '/blocks/{block_id}',
1824
+ paramCount: Object.keys(params || {}).length,
1825
+ paramKeys: Object.keys(params || {})
1826
+ });
1827
+ try {
1828
+ // Extract and separate parameters by location: path, query, body
1829
+ const pathTemplate = '/blocks/{block_id}';
1830
+ const pathParams = {};
1831
+ const queryParams = {};
1832
+ const bodyParams = {};
1833
+ const extractedParams = [];
1834
+ // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
1835
+ const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
1836
+ let match;
1837
+ while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
1838
+ const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
1839
+ if (paramName && params[paramName] !== undefined) {
1840
+ pathParams[paramName] = params[paramName];
1841
+ extractedParams.push(paramName);
1842
+ }
1843
+ }
1844
+ // Handle standard path templates: {resourceName}
1845
+ const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
1846
+ standardPathParams.forEach(paramTemplate => {
1847
+ const paramName = paramTemplate.slice(1, -1); // Remove { }
1848
+ // Only process if not already handled by Google template logic
1849
+ if (!extractedParams.includes(paramName)) {
1850
+ if (params[paramName] !== undefined) {
1851
+ pathParams[paramName] = params[paramName];
1852
+ extractedParams.push(paramName);
1853
+ }
1854
+ else {
1855
+ // Provide default values for optional path parameters
1856
+ if (paramName === 'userId') {
1857
+ pathParams[paramName] = 'me'; // Default to authenticated user
1858
+ extractedParams.push(paramName);
1859
+ }
1860
+ }
1861
+ }
1862
+ });
1863
+ // Check if any parameter has raw_array flag
1864
+ let hasRawArrayBody = false;
1865
+ let rawBodyData = undefined;
1866
+ // Separate remaining parameters by location (query vs body)
1867
+ if (params[""] !== undefined) {
1868
+ bodyParams[""] = params[""];
1869
+ extractedParams.push("");
1870
+ }
1871
+ if (params[""] !== undefined) {
1872
+ bodyParams[""] = params[""];
1873
+ extractedParams.push("");
1874
+ }
1875
+ if (params[""] !== undefined) {
1876
+ bodyParams[""] = params[""];
1877
+ extractedParams.push("");
1878
+ }
1879
+ if (params[""] !== undefined) {
1880
+ bodyParams[""] = params[""];
1881
+ extractedParams.push("");
1882
+ }
1883
+ if (params[""] !== undefined) {
1884
+ bodyParams[""] = params[""];
1885
+ extractedParams.push("");
1886
+ }
1887
+ if (params[""] !== undefined) {
1888
+ bodyParams[""] = params[""];
1889
+ extractedParams.push("");
1890
+ }
1891
+ if (params[""] !== undefined) {
1892
+ bodyParams[""] = params[""];
1893
+ extractedParams.push("");
1894
+ }
1895
+ if (params[""] !== undefined) {
1896
+ bodyParams[""] = params[""];
1897
+ extractedParams.push("");
1898
+ }
1899
+ if (params[""] !== undefined) {
1900
+ bodyParams[""] = params[""];
1901
+ extractedParams.push("");
1902
+ }
1903
+ if (params[""] !== undefined) {
1904
+ bodyParams[""] = params[""];
1905
+ extractedParams.push("");
1906
+ }
1907
+ if (params[""] !== undefined) {
1908
+ bodyParams[""] = params[""];
1909
+ extractedParams.push("");
1910
+ }
1911
+ if (params[""] !== undefined) {
1912
+ bodyParams[""] = params[""];
1913
+ extractedParams.push("");
1914
+ }
1915
+ if (params[""] !== undefined) {
1916
+ bodyParams[""] = params[""];
1917
+ extractedParams.push("");
1918
+ }
1919
+ if (params[""] !== undefined) {
1920
+ bodyParams[""] = params[""];
1921
+ extractedParams.push("");
1922
+ }
1923
+ if (params[""] !== undefined) {
1924
+ bodyParams[""] = params[""];
1925
+ extractedParams.push("");
1926
+ }
1927
+ if (params[""] !== undefined) {
1928
+ bodyParams[""] = params[""];
1929
+ extractedParams.push("");
1930
+ }
1931
+ if (params[""] !== undefined) {
1932
+ bodyParams[""] = params[""];
1933
+ extractedParams.push("");
1934
+ }
1935
+ if (params[""] !== undefined) {
1936
+ bodyParams[""] = params[""];
1937
+ extractedParams.push("");
1938
+ }
1939
+ if (params[""] !== undefined) {
1940
+ bodyParams[""] = params[""];
1941
+ extractedParams.push("");
1942
+ }
1943
+ if (params[""] !== undefined) {
1944
+ bodyParams[""] = params[""];
1945
+ extractedParams.push("");
1946
+ }
1947
+ if (params[""] !== undefined) {
1948
+ bodyParams[""] = params[""];
1949
+ extractedParams.push("");
1950
+ }
1951
+ if (params[""] !== undefined) {
1952
+ bodyParams[""] = params[""];
1953
+ extractedParams.push("");
1954
+ }
1955
+ if (params[""] !== undefined) {
1956
+ bodyParams[""] = params[""];
1957
+ extractedParams.push("");
1958
+ }
1959
+ if (params[""] !== undefined) {
1960
+ bodyParams[""] = params[""];
1961
+ extractedParams.push("");
1962
+ }
1963
+ if (params[""] !== undefined) {
1964
+ bodyParams[""] = params[""];
1965
+ extractedParams.push("");
1966
+ }
1967
+ if (params[""] !== undefined) {
1968
+ bodyParams[""] = params[""];
1969
+ extractedParams.push("");
1970
+ }
1971
+ if (params[""] !== undefined) {
1972
+ bodyParams[""] = params[""];
1973
+ extractedParams.push("");
1974
+ }
1975
+ // Any remaining unprocessed parameters default to body for backward compatibility
1976
+ for (const [key, value] of Object.entries(params)) {
1977
+ if (!extractedParams.includes(key)) {
1978
+ bodyParams[key] = value;
1979
+ }
1980
+ }
1981
+ // Validate required parameters
1982
+ const path = this.buildPath('/blocks/{block_id}', pathParams);
1983
+ // For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
1984
+ const requestPath = path === '/' ? '' : path;
1985
+ // Report initial progress if callback provided
1986
+ if (options?.onProgress) {
1987
+ await options.onProgress({
1988
+ progress: 0,
1989
+ total: 100,
1990
+ message: `Starting update_block request...`
1991
+ });
1992
+ }
1993
+ // Use standard HTTP client for other auth types with abort signal
1994
+ const requestConfig = {};
1995
+ if (options?.signal) {
1996
+ requestConfig.signal = options.signal;
1997
+ }
1998
+ const response = await this.httpClient.patch(requestPath, hasRawArrayBody ? rawBodyData : (Object.keys(bodyParams).length > 0 ? bodyParams : undefined), { params: queryParams, ...requestConfig });
1999
+ // Report completion progress if callback provided
2000
+ if (options?.onProgress) {
2001
+ await options.onProgress({
2002
+ progress: 100,
2003
+ total: 100,
2004
+ message: `Completed update_block request`
2005
+ });
2006
+ }
2007
+ const duration = Date.now() - startTime;
2008
+ this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
2009
+ endpoint: 'update_block',
2010
+ method: 'PATCH',
2011
+ path: '/blocks/{block_id}',
2012
+ duration_ms: duration,
2013
+ responseDataSize: JSON.stringify(response.data).length
2014
+ });
2015
+ return {
2016
+ content: [
2017
+ {
2018
+ type: 'text',
2019
+ text: JSON.stringify(response.data, null, 2)
2020
+ }
2021
+ ]
2022
+ };
2023
+ }
2024
+ catch (error) {
2025
+ const duration = Date.now() - startTime;
2026
+ // Check if error is due to cancellation
2027
+ if (axios.isCancel(error)) {
2028
+ this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
2029
+ endpoint: 'update_block',
2030
+ method: 'PATCH',
2031
+ path: '/blocks/{block_id}',
2032
+ duration_ms: duration
2033
+ });
2034
+ throw new Error('Request was cancelled');
2035
+ }
2036
+ this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
2037
+ endpoint: 'update_block',
2038
+ method: 'PATCH',
2039
+ path: '/blocks/{block_id}',
2040
+ duration_ms: duration,
2041
+ error: error instanceof Error ? error.message : String(error),
2042
+ errorType: error instanceof Error ? error.constructor.name : 'unknown'
2043
+ });
2044
+ throw new Error(`Failed to execute update_block: ${error instanceof Error ? error.message : String(error)}`);
2045
+ }
2046
+ }
2047
+ /* DEBUG: endpoint={"name":"delete_block","method":"DELETE","path":"/blocks/{block_id}","description":"Delete a block","parameters":{"block_id":{"type":"string","required":true,"description":"Block ID to delete","location":"path"}},"response_format":"json","category":"Block Content"} */
2048
+ async deleteBlock(params, options) {
2049
+ const startTime = Date.now();
2050
+ this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
2051
+ endpoint: 'delete_block',
2052
+ method: 'DELETE',
2053
+ path: '/blocks/{block_id}',
2054
+ paramCount: Object.keys(params || {}).length,
2055
+ paramKeys: Object.keys(params || {})
2056
+ });
2057
+ try {
2058
+ // Extract and separate parameters by location: path, query, body
2059
+ const pathTemplate = '/blocks/{block_id}';
2060
+ const pathParams = {};
2061
+ const queryParams = {};
2062
+ const bodyParams = {};
2063
+ const extractedParams = [];
2064
+ // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
2065
+ const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
2066
+ let match;
2067
+ while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
2068
+ const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
2069
+ if (paramName && params[paramName] !== undefined) {
2070
+ pathParams[paramName] = params[paramName];
2071
+ extractedParams.push(paramName);
2072
+ }
2073
+ }
2074
+ // Handle standard path templates: {resourceName}
2075
+ const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
2076
+ standardPathParams.forEach(paramTemplate => {
2077
+ const paramName = paramTemplate.slice(1, -1); // Remove { }
2078
+ // Only process if not already handled by Google template logic
2079
+ if (!extractedParams.includes(paramName)) {
2080
+ if (params[paramName] !== undefined) {
2081
+ pathParams[paramName] = params[paramName];
2082
+ extractedParams.push(paramName);
2083
+ }
2084
+ else {
2085
+ // Provide default values for optional path parameters
2086
+ if (paramName === 'userId') {
2087
+ pathParams[paramName] = 'me'; // Default to authenticated user
2088
+ extractedParams.push(paramName);
2089
+ }
2090
+ }
2091
+ }
2092
+ });
2093
+ // Check if any parameter has raw_array flag
2094
+ let hasRawArrayBody = false;
2095
+ let rawBodyData = undefined;
2096
+ // Separate remaining parameters by location (query vs body)
2097
+ // Any remaining unprocessed parameters default to body for backward compatibility
2098
+ for (const [key, value] of Object.entries(params)) {
2099
+ if (!extractedParams.includes(key)) {
2100
+ bodyParams[key] = value;
2101
+ }
2102
+ }
2103
+ // Validate required parameters
2104
+ const path = this.buildPath('/blocks/{block_id}', pathParams);
2105
+ // For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
2106
+ const requestPath = path === '/' ? '' : path;
2107
+ // Report initial progress if callback provided
2108
+ if (options?.onProgress) {
2109
+ await options.onProgress({
2110
+ progress: 0,
2111
+ total: 100,
2112
+ message: `Starting delete_block request...`
2113
+ });
2114
+ }
2115
+ // Use standard HTTP client for other auth types with abort signal
2116
+ const requestConfig = {};
2117
+ if (options?.signal) {
2118
+ requestConfig.signal = options.signal;
2119
+ }
2120
+ const response = await this.httpClient.delete(requestPath, { params: queryParams, ...requestConfig });
2121
+ // Report completion progress if callback provided
2122
+ if (options?.onProgress) {
2123
+ await options.onProgress({
2124
+ progress: 100,
2125
+ total: 100,
2126
+ message: `Completed delete_block request`
2127
+ });
2128
+ }
2129
+ const duration = Date.now() - startTime;
2130
+ this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
2131
+ endpoint: 'delete_block',
2132
+ method: 'DELETE',
2133
+ path: '/blocks/{block_id}',
2134
+ duration_ms: duration,
2135
+ responseDataSize: JSON.stringify(response.data).length
2136
+ });
2137
+ return {
2138
+ content: [
2139
+ {
2140
+ type: 'text',
2141
+ text: JSON.stringify(response.data, null, 2)
2142
+ }
2143
+ ]
2144
+ };
2145
+ }
2146
+ catch (error) {
2147
+ const duration = Date.now() - startTime;
2148
+ // Check if error is due to cancellation
2149
+ if (axios.isCancel(error)) {
2150
+ this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
2151
+ endpoint: 'delete_block',
2152
+ method: 'DELETE',
2153
+ path: '/blocks/{block_id}',
2154
+ duration_ms: duration
2155
+ });
2156
+ throw new Error('Request was cancelled');
2157
+ }
2158
+ this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
2159
+ endpoint: 'delete_block',
2160
+ method: 'DELETE',
2161
+ path: '/blocks/{block_id}',
2162
+ duration_ms: duration,
2163
+ error: error instanceof Error ? error.message : String(error),
2164
+ errorType: error instanceof Error ? error.constructor.name : 'unknown'
2165
+ });
2166
+ throw new Error(`Failed to execute delete_block: ${error instanceof Error ? error.message : String(error)}`);
2167
+ }
2168
+ }
2169
+ /* DEBUG: endpoint={"name":"list_users","method":"GET","path":"/users","description":"List all users","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":"Workspace Collaboration"} */
2170
+ async listUsers(params, options) {
2171
+ const startTime = Date.now();
2172
+ this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
2173
+ endpoint: 'list_users',
2174
+ method: 'GET',
2175
+ path: '/users',
2176
+ paramCount: Object.keys(params || {}).length,
2177
+ paramKeys: Object.keys(params || {})
2178
+ });
2179
+ try {
2180
+ // Extract and separate parameters by location: path, query, body
2181
+ const pathTemplate = '/users';
2182
+ const pathParams = {};
2183
+ const queryParams = {};
2184
+ const bodyParams = {};
2185
+ const extractedParams = [];
2186
+ // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
2187
+ const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
2188
+ let match;
2189
+ while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
2190
+ const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
2191
+ if (paramName && params[paramName] !== undefined) {
2192
+ pathParams[paramName] = params[paramName];
2193
+ extractedParams.push(paramName);
2194
+ }
2195
+ }
2196
+ // Handle standard path templates: {resourceName}
2197
+ const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
2198
+ standardPathParams.forEach(paramTemplate => {
2199
+ const paramName = paramTemplate.slice(1, -1); // Remove { }
2200
+ // Only process if not already handled by Google template logic
2201
+ if (!extractedParams.includes(paramName)) {
2202
+ if (params[paramName] !== undefined) {
2203
+ pathParams[paramName] = params[paramName];
2204
+ extractedParams.push(paramName);
2205
+ }
2206
+ else {
2207
+ // Provide default values for optional path parameters
2208
+ if (paramName === 'userId') {
2209
+ pathParams[paramName] = 'me'; // Default to authenticated user
2210
+ extractedParams.push(paramName);
2211
+ }
2212
+ }
2213
+ }
2214
+ });
2215
+ // Check if any parameter has raw_array flag
2216
+ let hasRawArrayBody = false;
2217
+ let rawBodyData = undefined;
2218
+ // Separate remaining parameters by location (query vs body)
2219
+ if (params[""] !== undefined) {
2220
+ queryParams[""] = params[""];
2221
+ extractedParams.push("");
2222
+ }
2223
+ if (params[""] !== undefined) {
2224
+ queryParams[""] = params[""];
2225
+ extractedParams.push("");
2226
+ }
2227
+ // Any remaining unprocessed parameters default to body for backward compatibility
2228
+ for (const [key, value] of Object.entries(params)) {
2229
+ if (!extractedParams.includes(key)) {
2230
+ bodyParams[key] = value;
2231
+ }
2232
+ }
2233
+ // Validate required parameters
2234
+ const path = this.buildPath('/users', pathParams);
2235
+ // For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
2236
+ const requestPath = path === '/' ? '' : path;
2237
+ // Report initial progress if callback provided
2238
+ if (options?.onProgress) {
2239
+ await options.onProgress({
2240
+ progress: 0,
2241
+ total: 100,
2242
+ message: `Starting list_users request...`
2243
+ });
2244
+ }
2245
+ // Use standard HTTP client for other auth types with abort signal
2246
+ const requestConfig = {};
2247
+ if (options?.signal) {
2248
+ requestConfig.signal = options.signal;
2249
+ }
2250
+ const response = await this.httpClient.get(requestPath, { params: queryParams, ...requestConfig });
2251
+ // Report completion progress if callback provided
2252
+ if (options?.onProgress) {
2253
+ await options.onProgress({
2254
+ progress: 100,
2255
+ total: 100,
2256
+ message: `Completed list_users request`
2257
+ });
2258
+ }
2259
+ const duration = Date.now() - startTime;
2260
+ this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
2261
+ endpoint: 'list_users',
2262
+ method: 'GET',
2263
+ path: '/users',
2264
+ duration_ms: duration,
2265
+ responseDataSize: JSON.stringify(response.data).length
2266
+ });
2267
+ return {
2268
+ content: [
2269
+ {
2270
+ type: 'text',
2271
+ text: JSON.stringify(response.data, null, 2)
2272
+ }
2273
+ ]
2274
+ };
2275
+ }
2276
+ catch (error) {
2277
+ const duration = Date.now() - startTime;
2278
+ // Check if error is due to cancellation
2279
+ if (axios.isCancel(error)) {
2280
+ this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
2281
+ endpoint: 'list_users',
2282
+ method: 'GET',
2283
+ path: '/users',
2284
+ duration_ms: duration
2285
+ });
2286
+ throw new Error('Request was cancelled');
2287
+ }
2288
+ this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
2289
+ endpoint: 'list_users',
2290
+ method: 'GET',
2291
+ path: '/users',
2292
+ duration_ms: duration,
2293
+ error: error instanceof Error ? error.message : String(error),
2294
+ errorType: error instanceof Error ? error.constructor.name : 'unknown'
2295
+ });
2296
+ throw new Error(`Failed to execute list_users: ${error instanceof Error ? error.message : String(error)}`);
2297
+ }
2298
+ }
2299
+ /* DEBUG: endpoint={"name":"get_user","method":"GET","path":"/users/{user_id}","description":"Get user by ID","parameters":{"user_id":{"type":"string","required":true,"description":"User ID to fetch","location":"path"}},"response_format":"json","category":"Workspace Collaboration"} */
2300
+ async getUser(params, options) {
2301
+ const startTime = Date.now();
2302
+ this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
2303
+ endpoint: 'get_user',
2304
+ method: 'GET',
2305
+ path: '/users/{user_id}',
2306
+ paramCount: Object.keys(params || {}).length,
2307
+ paramKeys: Object.keys(params || {})
2308
+ });
2309
+ try {
2310
+ // Extract and separate parameters by location: path, query, body
2311
+ const pathTemplate = '/users/{user_id}';
2312
+ const pathParams = {};
2313
+ const queryParams = {};
2314
+ const bodyParams = {};
2315
+ const extractedParams = [];
2316
+ // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
2317
+ const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
2318
+ let match;
2319
+ while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
2320
+ const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
2321
+ if (paramName && params[paramName] !== undefined) {
2322
+ pathParams[paramName] = params[paramName];
2323
+ extractedParams.push(paramName);
2324
+ }
2325
+ }
2326
+ // Handle standard path templates: {resourceName}
2327
+ const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
2328
+ standardPathParams.forEach(paramTemplate => {
2329
+ const paramName = paramTemplate.slice(1, -1); // Remove { }
2330
+ // Only process if not already handled by Google template logic
2331
+ if (!extractedParams.includes(paramName)) {
2332
+ if (params[paramName] !== undefined) {
2333
+ pathParams[paramName] = params[paramName];
2334
+ extractedParams.push(paramName);
2335
+ }
2336
+ else {
2337
+ // Provide default values for optional path parameters
2338
+ if (paramName === 'userId') {
2339
+ pathParams[paramName] = 'me'; // Default to authenticated user
2340
+ extractedParams.push(paramName);
2341
+ }
2342
+ }
2343
+ }
2344
+ });
2345
+ // Check if any parameter has raw_array flag
2346
+ let hasRawArrayBody = false;
2347
+ let rawBodyData = undefined;
2348
+ // Separate remaining parameters by location (query vs body)
2349
+ // Any remaining unprocessed parameters default to body for backward compatibility
2350
+ for (const [key, value] of Object.entries(params)) {
2351
+ if (!extractedParams.includes(key)) {
2352
+ bodyParams[key] = value;
2353
+ }
2354
+ }
2355
+ // Validate required parameters
2356
+ const path = this.buildPath('/users/{user_id}', pathParams);
2357
+ // For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
2358
+ const requestPath = path === '/' ? '' : path;
2359
+ // Report initial progress if callback provided
2360
+ if (options?.onProgress) {
2361
+ await options.onProgress({
2362
+ progress: 0,
2363
+ total: 100,
2364
+ message: `Starting get_user request...`
2365
+ });
2366
+ }
2367
+ // Use standard HTTP client for other auth types with abort signal
2368
+ const requestConfig = {};
2369
+ if (options?.signal) {
2370
+ requestConfig.signal = options.signal;
2371
+ }
2372
+ const response = await this.httpClient.get(requestPath, { params: queryParams, ...requestConfig });
2373
+ // Report completion progress if callback provided
2374
+ if (options?.onProgress) {
2375
+ await options.onProgress({
2376
+ progress: 100,
2377
+ total: 100,
2378
+ message: `Completed get_user request`
2379
+ });
2380
+ }
2381
+ const duration = Date.now() - startTime;
2382
+ this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
2383
+ endpoint: 'get_user',
2384
+ method: 'GET',
2385
+ path: '/users/{user_id}',
2386
+ duration_ms: duration,
2387
+ responseDataSize: JSON.stringify(response.data).length
2388
+ });
2389
+ return {
2390
+ content: [
2391
+ {
2392
+ type: 'text',
2393
+ text: JSON.stringify(response.data, null, 2)
2394
+ }
2395
+ ]
2396
+ };
2397
+ }
2398
+ catch (error) {
2399
+ const duration = Date.now() - startTime;
2400
+ // Check if error is due to cancellation
2401
+ if (axios.isCancel(error)) {
2402
+ this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
2403
+ endpoint: 'get_user',
2404
+ method: 'GET',
2405
+ path: '/users/{user_id}',
2406
+ duration_ms: duration
2407
+ });
2408
+ throw new Error('Request was cancelled');
2409
+ }
2410
+ this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
2411
+ endpoint: 'get_user',
2412
+ method: 'GET',
2413
+ path: '/users/{user_id}',
2414
+ duration_ms: duration,
2415
+ error: error instanceof Error ? error.message : String(error),
2416
+ errorType: error instanceof Error ? error.constructor.name : 'unknown'
2417
+ });
2418
+ throw new Error(`Failed to execute get_user: ${error instanceof Error ? error.message : String(error)}`);
2419
+ }
2420
+ }
2421
+ /* DEBUG: endpoint={"name":"get_me","method":"GET","path":"/users/me","description":"Get current bot user","parameters":{},"response_format":"json","category":"Workspace Collaboration"} */
2422
+ async getMe(params, options) {
2423
+ const startTime = Date.now();
2424
+ this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
2425
+ endpoint: 'get_me',
2426
+ method: 'GET',
2427
+ path: '/users/me',
2428
+ paramCount: Object.keys(params || {}).length,
2429
+ paramKeys: Object.keys(params || {})
2430
+ });
2431
+ try {
2432
+ // Extract and separate parameters by location: path, query, body
2433
+ const pathTemplate = '/users/me';
2434
+ const pathParams = {};
2435
+ const queryParams = {};
2436
+ const bodyParams = {};
2437
+ const extractedParams = [];
2438
+ // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
2439
+ const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
2440
+ let match;
2441
+ while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
2442
+ const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
2443
+ if (paramName && params[paramName] !== undefined) {
2444
+ pathParams[paramName] = params[paramName];
2445
+ extractedParams.push(paramName);
2446
+ }
2447
+ }
2448
+ // Handle standard path templates: {resourceName}
2449
+ const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
2450
+ standardPathParams.forEach(paramTemplate => {
2451
+ const paramName = paramTemplate.slice(1, -1); // Remove { }
2452
+ // Only process if not already handled by Google template logic
2453
+ if (!extractedParams.includes(paramName)) {
2454
+ if (params[paramName] !== undefined) {
2455
+ pathParams[paramName] = params[paramName];
2456
+ extractedParams.push(paramName);
2457
+ }
2458
+ else {
2459
+ // Provide default values for optional path parameters
2460
+ if (paramName === 'userId') {
2461
+ pathParams[paramName] = 'me'; // Default to authenticated user
2462
+ extractedParams.push(paramName);
2463
+ }
2464
+ }
2465
+ }
2466
+ });
2467
+ // Check if any parameter has raw_array flag
2468
+ let hasRawArrayBody = false;
2469
+ let rawBodyData = undefined;
2470
+ // Separate remaining parameters by location (query vs body)
2471
+ // Any remaining unprocessed parameters default to body for backward compatibility
2472
+ for (const [key, value] of Object.entries(params)) {
2473
+ if (!extractedParams.includes(key)) {
2474
+ bodyParams[key] = value;
2475
+ }
2476
+ }
2477
+ // Validate required parameters
2478
+ const path = this.buildPath('/users/me', pathParams);
2479
+ // For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
2480
+ const requestPath = path === '/' ? '' : path;
2481
+ // Report initial progress if callback provided
2482
+ if (options?.onProgress) {
2483
+ await options.onProgress({
2484
+ progress: 0,
2485
+ total: 100,
2486
+ message: `Starting get_me request...`
2487
+ });
2488
+ }
2489
+ // Use standard HTTP client for other auth types with abort signal
2490
+ const requestConfig = {};
2491
+ if (options?.signal) {
2492
+ requestConfig.signal = options.signal;
2493
+ }
2494
+ const response = await this.httpClient.get(requestPath, { params: queryParams, ...requestConfig });
2495
+ // Report completion progress if callback provided
2496
+ if (options?.onProgress) {
2497
+ await options.onProgress({
2498
+ progress: 100,
2499
+ total: 100,
2500
+ message: `Completed get_me request`
2501
+ });
2502
+ }
2503
+ const duration = Date.now() - startTime;
2504
+ this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
2505
+ endpoint: 'get_me',
2506
+ method: 'GET',
2507
+ path: '/users/me',
2508
+ duration_ms: duration,
2509
+ responseDataSize: JSON.stringify(response.data).length
2510
+ });
2511
+ return {
2512
+ content: [
2513
+ {
2514
+ type: 'text',
2515
+ text: JSON.stringify(response.data, null, 2)
2516
+ }
2517
+ ]
2518
+ };
2519
+ }
2520
+ catch (error) {
2521
+ const duration = Date.now() - startTime;
2522
+ // Check if error is due to cancellation
2523
+ if (axios.isCancel(error)) {
2524
+ this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
2525
+ endpoint: 'get_me',
2526
+ method: 'GET',
2527
+ path: '/users/me',
2528
+ duration_ms: duration
2529
+ });
2530
+ throw new Error('Request was cancelled');
2531
+ }
2532
+ this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
2533
+ endpoint: 'get_me',
2534
+ method: 'GET',
2535
+ path: '/users/me',
2536
+ duration_ms: duration,
2537
+ error: error instanceof Error ? error.message : String(error),
2538
+ errorType: error instanceof Error ? error.constructor.name : 'unknown'
2539
+ });
2540
+ throw new Error(`Failed to execute get_me: ${error instanceof Error ? error.message : String(error)}`);
2541
+ }
2542
+ }
2543
+ /* DEBUG: endpoint={"name":"search","method":"POST","path":"/search","description":"Search pages and databases","parameters":{"query":{"type":"string","required":false,"description":"Search query string","location":"body"},"sort":{"type":"object","required":false,"description":"Sort configuration","location":"body"},"filter":{"type":"object","required":false,"description":"Filter configuration","location":"body"},"start_cursor":{"type":"string","required":false,"description":"Pagination cursor","location":"body"},"page_size":{"type":"number","required":false,"description":"Number of results per page (max 100)","location":"body","default":100}},"response_format":"json","category":"Page Creation & Editing"} */
2544
+ async search(params, options) {
2545
+ const startTime = Date.now();
2546
+ this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
2547
+ endpoint: 'search',
2548
+ method: 'POST',
2549
+ path: '/search',
2550
+ paramCount: Object.keys(params || {}).length,
2551
+ paramKeys: Object.keys(params || {})
2552
+ });
2553
+ try {
2554
+ // Extract and separate parameters by location: path, query, body
2555
+ const pathTemplate = '/search';
2556
+ const pathParams = {};
2557
+ const queryParams = {};
2558
+ const bodyParams = {};
2559
+ const extractedParams = [];
2560
+ // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
2561
+ const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
2562
+ let match;
2563
+ while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
2564
+ const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
2565
+ if (paramName && params[paramName] !== undefined) {
2566
+ pathParams[paramName] = params[paramName];
2567
+ extractedParams.push(paramName);
2568
+ }
2569
+ }
2570
+ // Handle standard path templates: {resourceName}
2571
+ const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
2572
+ standardPathParams.forEach(paramTemplate => {
2573
+ const paramName = paramTemplate.slice(1, -1); // Remove { }
2574
+ // Only process if not already handled by Google template logic
2575
+ if (!extractedParams.includes(paramName)) {
2576
+ if (params[paramName] !== undefined) {
2577
+ pathParams[paramName] = params[paramName];
2578
+ extractedParams.push(paramName);
2579
+ }
2580
+ else {
2581
+ // Provide default values for optional path parameters
2582
+ if (paramName === 'userId') {
2583
+ pathParams[paramName] = 'me'; // Default to authenticated user
2584
+ extractedParams.push(paramName);
2585
+ }
2586
+ }
2587
+ }
2588
+ });
2589
+ // Check if any parameter has raw_array flag
2590
+ let hasRawArrayBody = false;
2591
+ let rawBodyData = undefined;
2592
+ // Separate remaining parameters by location (query vs body)
2593
+ if (params[""] !== undefined) {
2594
+ bodyParams[""] = params[""];
2595
+ extractedParams.push("");
2596
+ }
2597
+ if (params[""] !== undefined) {
2598
+ bodyParams[""] = params[""];
2599
+ extractedParams.push("");
2600
+ }
2601
+ if (params[""] !== undefined) {
2602
+ bodyParams[""] = params[""];
2603
+ extractedParams.push("");
2604
+ }
2605
+ if (params[""] !== undefined) {
2606
+ bodyParams[""] = params[""];
2607
+ extractedParams.push("");
2608
+ }
2609
+ if (params[""] !== undefined) {
2610
+ bodyParams[""] = params[""];
2611
+ extractedParams.push("");
2612
+ }
2613
+ // Any remaining unprocessed parameters default to body for backward compatibility
2614
+ for (const [key, value] of Object.entries(params)) {
2615
+ if (!extractedParams.includes(key)) {
2616
+ bodyParams[key] = value;
2617
+ }
2618
+ }
2619
+ // Validate required parameters
2620
+ const path = this.buildPath('/search', pathParams);
2621
+ // For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
2622
+ const requestPath = path === '/' ? '' : path;
2623
+ // Report initial progress if callback provided
2624
+ if (options?.onProgress) {
2625
+ await options.onProgress({
2626
+ progress: 0,
2627
+ total: 100,
2628
+ message: `Starting search request...`
2629
+ });
2630
+ }
2631
+ // Use standard HTTP client for other auth types with abort signal
2632
+ const requestConfig = {};
2633
+ if (options?.signal) {
2634
+ requestConfig.signal = options.signal;
2635
+ }
2636
+ const response = await this.httpClient.post(requestPath, hasRawArrayBody ? rawBodyData : (Object.keys(bodyParams).length > 0 ? bodyParams : undefined), { params: queryParams, ...requestConfig });
2637
+ // Report completion progress if callback provided
2638
+ if (options?.onProgress) {
2639
+ await options.onProgress({
2640
+ progress: 100,
2641
+ total: 100,
2642
+ message: `Completed search request`
2643
+ });
2644
+ }
2645
+ const duration = Date.now() - startTime;
2646
+ this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
2647
+ endpoint: 'search',
2648
+ method: 'POST',
2649
+ path: '/search',
2650
+ duration_ms: duration,
2651
+ responseDataSize: JSON.stringify(response.data).length
2652
+ });
2653
+ return {
2654
+ content: [
2655
+ {
2656
+ type: 'text',
2657
+ text: JSON.stringify(response.data, null, 2)
2658
+ }
2659
+ ]
2660
+ };
2661
+ }
2662
+ catch (error) {
2663
+ const duration = Date.now() - startTime;
2664
+ // Check if error is due to cancellation
2665
+ if (axios.isCancel(error)) {
2666
+ this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
2667
+ endpoint: 'search',
2668
+ method: 'POST',
2669
+ path: '/search',
2670
+ duration_ms: duration
2671
+ });
2672
+ throw new Error('Request was cancelled');
2673
+ }
2674
+ this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
2675
+ endpoint: 'search',
2676
+ method: 'POST',
2677
+ path: '/search',
2678
+ duration_ms: duration,
2679
+ error: error instanceof Error ? error.message : String(error),
2680
+ errorType: error instanceof Error ? error.constructor.name : 'unknown'
2681
+ });
2682
+ throw new Error(`Failed to execute search: ${error instanceof Error ? error.message : String(error)}`);
2683
+ }
2684
+ }
2685
+ /* DEBUG: endpoint={"name":"create_comment","method":"POST","path":"/comments","description":"Create a comment on a page or block","parameters":{"parent":{"type":"object","required":true,"description":"Parent object (page or block)","location":"body"},"rich_text":{"type":"array","required":true,"description":"Array of rich text objects for comment content","location":"body"}},"response_format":"json","category":"Page Creation & Editing"} */
2686
+ async createComment(params, options) {
2687
+ const startTime = Date.now();
2688
+ this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
2689
+ endpoint: 'create_comment',
2690
+ method: 'POST',
2691
+ path: '/comments',
2692
+ paramCount: Object.keys(params || {}).length,
2693
+ paramKeys: Object.keys(params || {})
2694
+ });
2695
+ try {
2696
+ // Extract and separate parameters by location: path, query, body
2697
+ const pathTemplate = '/comments';
2698
+ const pathParams = {};
2699
+ const queryParams = {};
2700
+ const bodyParams = {};
2701
+ const extractedParams = [];
2702
+ // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
2703
+ const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
2704
+ let match;
2705
+ while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
2706
+ const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
2707
+ if (paramName && params[paramName] !== undefined) {
2708
+ pathParams[paramName] = params[paramName];
2709
+ extractedParams.push(paramName);
2710
+ }
2711
+ }
2712
+ // Handle standard path templates: {resourceName}
2713
+ const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
2714
+ standardPathParams.forEach(paramTemplate => {
2715
+ const paramName = paramTemplate.slice(1, -1); // Remove { }
2716
+ // Only process if not already handled by Google template logic
2717
+ if (!extractedParams.includes(paramName)) {
2718
+ if (params[paramName] !== undefined) {
2719
+ pathParams[paramName] = params[paramName];
2720
+ extractedParams.push(paramName);
2721
+ }
2722
+ else {
2723
+ // Provide default values for optional path parameters
2724
+ if (paramName === 'userId') {
2725
+ pathParams[paramName] = 'me'; // Default to authenticated user
2726
+ extractedParams.push(paramName);
2727
+ }
2728
+ }
2729
+ }
2730
+ });
2731
+ // Check if any parameter has raw_array flag
2732
+ let hasRawArrayBody = false;
2733
+ let rawBodyData = undefined;
2734
+ // Separate remaining parameters by location (query vs body)
2735
+ if (params[""] !== undefined) {
2736
+ bodyParams[""] = params[""];
2737
+ extractedParams.push("");
2738
+ }
2739
+ if (params[""] !== undefined) {
2740
+ bodyParams[""] = params[""];
2741
+ extractedParams.push("");
2742
+ }
2743
+ // Any remaining unprocessed parameters default to body for backward compatibility
2744
+ for (const [key, value] of Object.entries(params)) {
2745
+ if (!extractedParams.includes(key)) {
2746
+ bodyParams[key] = value;
2747
+ }
2748
+ }
2749
+ // Validate required parameters
2750
+ const path = this.buildPath('/comments', pathParams);
2751
+ // For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
2752
+ const requestPath = path === '/' ? '' : path;
2753
+ // Report initial progress if callback provided
2754
+ if (options?.onProgress) {
2755
+ await options.onProgress({
2756
+ progress: 0,
2757
+ total: 100,
2758
+ message: `Starting create_comment request...`
2759
+ });
2760
+ }
2761
+ // Use standard HTTP client for other auth types with abort signal
2762
+ const requestConfig = {};
2763
+ if (options?.signal) {
2764
+ requestConfig.signal = options.signal;
2765
+ }
2766
+ const response = await this.httpClient.post(requestPath, hasRawArrayBody ? rawBodyData : (Object.keys(bodyParams).length > 0 ? bodyParams : undefined), { params: queryParams, ...requestConfig });
2767
+ // Report completion progress if callback provided
2768
+ if (options?.onProgress) {
2769
+ await options.onProgress({
2770
+ progress: 100,
2771
+ total: 100,
2772
+ message: `Completed create_comment request`
2773
+ });
2774
+ }
2775
+ const duration = Date.now() - startTime;
2776
+ this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
2777
+ endpoint: 'create_comment',
2778
+ method: 'POST',
2779
+ path: '/comments',
2780
+ duration_ms: duration,
2781
+ responseDataSize: JSON.stringify(response.data).length
2782
+ });
2783
+ return {
2784
+ content: [
2785
+ {
2786
+ type: 'text',
2787
+ text: JSON.stringify(response.data, null, 2)
2788
+ }
2789
+ ]
2790
+ };
2791
+ }
2792
+ catch (error) {
2793
+ const duration = Date.now() - startTime;
2794
+ // Check if error is due to cancellation
2795
+ if (axios.isCancel(error)) {
2796
+ this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
2797
+ endpoint: 'create_comment',
2798
+ method: 'POST',
2799
+ path: '/comments',
2800
+ duration_ms: duration
2801
+ });
2802
+ throw new Error('Request was cancelled');
2803
+ }
2804
+ this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
2805
+ endpoint: 'create_comment',
2806
+ method: 'POST',
2807
+ path: '/comments',
2808
+ duration_ms: duration,
2809
+ error: error instanceof Error ? error.message : String(error),
2810
+ errorType: error instanceof Error ? error.constructor.name : 'unknown'
2811
+ });
2812
+ throw new Error(`Failed to execute create_comment: ${error instanceof Error ? error.message : String(error)}`);
2813
+ }
2814
+ }
2815
+ /* DEBUG: endpoint={"name":"get_comments","method":"GET","path":"/comments","description":"Get comments for a page or block","parameters":{"block_id":{"type":"string","required":true,"description":"Block ID to get comments for","location":"query"},"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":"Page Creation & Editing"} */
2816
+ async getComments(params, options) {
2817
+ const startTime = Date.now();
2818
+ this.logger.info('ENDPOINT_START', 'Endpoint execution started', {
2819
+ endpoint: 'get_comments',
2820
+ method: 'GET',
2821
+ path: '/comments',
2822
+ paramCount: Object.keys(params || {}).length,
2823
+ paramKeys: Object.keys(params || {})
2824
+ });
2825
+ try {
2826
+ // Extract and separate parameters by location: path, query, body
2827
+ const pathTemplate = '/comments';
2828
+ const pathParams = {};
2829
+ const queryParams = {};
2830
+ const bodyParams = {};
2831
+ const extractedParams = [];
2832
+ // Handle Google-style path templates: {resourceName=people/*} and {person.resourceName=people/*}
2833
+ const googlePathTemplateRegex = /{([^}=]+)=[^}]*}/g;
2834
+ let match;
2835
+ while ((match = googlePathTemplateRegex.exec(pathTemplate)) !== null) {
2836
+ const paramName = match[1]; // e.g., "resourceName" or "person.resourceName"
2837
+ if (paramName && params[paramName] !== undefined) {
2838
+ pathParams[paramName] = params[paramName];
2839
+ extractedParams.push(paramName);
2840
+ }
2841
+ }
2842
+ // Handle standard path templates: {resourceName}
2843
+ const standardPathParams = pathTemplate.match(/{([^}=]+)}/g) || [];
2844
+ standardPathParams.forEach(paramTemplate => {
2845
+ const paramName = paramTemplate.slice(1, -1); // Remove { }
2846
+ // Only process if not already handled by Google template logic
2847
+ if (!extractedParams.includes(paramName)) {
2848
+ if (params[paramName] !== undefined) {
2849
+ pathParams[paramName] = params[paramName];
2850
+ extractedParams.push(paramName);
2851
+ }
2852
+ else {
2853
+ // Provide default values for optional path parameters
2854
+ if (paramName === 'userId') {
2855
+ pathParams[paramName] = 'me'; // Default to authenticated user
2856
+ extractedParams.push(paramName);
2857
+ }
2858
+ }
2859
+ }
2860
+ });
2861
+ // Check if any parameter has raw_array flag
2862
+ let hasRawArrayBody = false;
2863
+ let rawBodyData = undefined;
2864
+ // Separate remaining parameters by location (query vs body)
2865
+ if (params[""] !== undefined) {
2866
+ queryParams[""] = params[""];
2867
+ extractedParams.push("");
2868
+ }
2869
+ if (params[""] !== undefined) {
2870
+ queryParams[""] = params[""];
2871
+ extractedParams.push("");
2872
+ }
2873
+ if (params[""] !== undefined) {
2874
+ queryParams[""] = params[""];
2875
+ extractedParams.push("");
2876
+ }
2877
+ // Any remaining unprocessed parameters default to body for backward compatibility
2878
+ for (const [key, value] of Object.entries(params)) {
2879
+ if (!extractedParams.includes(key)) {
2880
+ bodyParams[key] = value;
2881
+ }
2882
+ }
2883
+ // Validate required parameters
2884
+ const path = this.buildPath('/comments', pathParams);
2885
+ // For GraphQL endpoints that use '/' as the path, use empty string to avoid double slash
2886
+ const requestPath = path === '/' ? '' : path;
2887
+ // Report initial progress if callback provided
2888
+ if (options?.onProgress) {
2889
+ await options.onProgress({
2890
+ progress: 0,
2891
+ total: 100,
2892
+ message: `Starting get_comments request...`
2893
+ });
2894
+ }
2895
+ // Use standard HTTP client for other auth types with abort signal
2896
+ const requestConfig = {};
2897
+ if (options?.signal) {
2898
+ requestConfig.signal = options.signal;
2899
+ }
2900
+ const response = await this.httpClient.get(requestPath, { params: queryParams, ...requestConfig });
2901
+ // Report completion progress if callback provided
2902
+ if (options?.onProgress) {
2903
+ await options.onProgress({
2904
+ progress: 100,
2905
+ total: 100,
2906
+ message: `Completed get_comments request`
2907
+ });
2908
+ }
2909
+ const duration = Date.now() - startTime;
2910
+ this.logger.info('ENDPOINT_SUCCESS', 'Endpoint execution completed successfully', {
2911
+ endpoint: 'get_comments',
2912
+ method: 'GET',
2913
+ path: '/comments',
2914
+ duration_ms: duration,
2915
+ responseDataSize: JSON.stringify(response.data).length
2916
+ });
2917
+ return {
2918
+ content: [
2919
+ {
2920
+ type: 'text',
2921
+ text: JSON.stringify(response.data, null, 2)
2922
+ }
2923
+ ]
2924
+ };
2925
+ }
2926
+ catch (error) {
2927
+ const duration = Date.now() - startTime;
2928
+ // Check if error is due to cancellation
2929
+ if (axios.isCancel(error)) {
2930
+ this.logger.info('REQUEST_CANCELLED', 'Request was cancelled', {
2931
+ endpoint: 'get_comments',
2932
+ method: 'GET',
2933
+ path: '/comments',
2934
+ duration_ms: duration
2935
+ });
2936
+ throw new Error('Request was cancelled');
2937
+ }
2938
+ this.logger.error('ENDPOINT_ERROR', 'Endpoint execution failed', {
2939
+ endpoint: 'get_comments',
2940
+ method: 'GET',
2941
+ path: '/comments',
2942
+ duration_ms: duration,
2943
+ error: error instanceof Error ? error.message : String(error),
2944
+ errorType: error instanceof Error ? error.constructor.name : 'unknown'
2945
+ });
2946
+ throw new Error(`Failed to execute get_comments: ${error instanceof Error ? error.message : String(error)}`);
2947
+ }
2948
+ }
2949
+ }
2950
+ //# sourceMappingURL=notion-client.js.map