@n24q02m/better-notion-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +240 -0
  3. package/bin/cli.mjs +117 -0
  4. package/build/scripts/start-server.d.ts +6 -0
  5. package/build/scripts/start-server.d.ts.map +1 -0
  6. package/build/scripts/start-server.js +21 -0
  7. package/build/scripts/start-server.js.map +1 -0
  8. package/build/src/init-server.d.ts +29 -0
  9. package/build/src/init-server.d.ts.map +1 -0
  10. package/build/src/init-server.js +32 -0
  11. package/build/src/init-server.js.map +1 -0
  12. package/build/src/tools/composite/blocks.d.ts +16 -0
  13. package/build/src/tools/composite/blocks.d.ts.map +1 -0
  14. package/build/src/tools/composite/blocks.js +104 -0
  15. package/build/src/tools/composite/blocks.js.map +1 -0
  16. package/build/src/tools/composite/comments.d.ts +16 -0
  17. package/build/src/tools/composite/comments.d.ts.map +1 -0
  18. package/build/src/tools/composite/comments.js +69 -0
  19. package/build/src/tools/composite/comments.js.map +1 -0
  20. package/build/src/tools/composite/content.d.ts +13 -0
  21. package/build/src/tools/composite/content.d.ts.map +1 -0
  22. package/build/src/tools/composite/content.js +50 -0
  23. package/build/src/tools/composite/content.js.map +1 -0
  24. package/build/src/tools/composite/databases.d.ts +33 -0
  25. package/build/src/tools/composite/databases.d.ts.map +1 -0
  26. package/build/src/tools/composite/databases.js +419 -0
  27. package/build/src/tools/composite/databases.js.map +1 -0
  28. package/build/src/tools/composite/pages.d.ts +24 -0
  29. package/build/src/tools/composite/pages.d.ts.map +1 -0
  30. package/build/src/tools/composite/pages.js +316 -0
  31. package/build/src/tools/composite/pages.js.map +1 -0
  32. package/build/src/tools/composite/search.d.ts +23 -0
  33. package/build/src/tools/composite/search.d.ts.map +1 -0
  34. package/build/src/tools/composite/search.js +94 -0
  35. package/build/src/tools/composite/search.js.map +1 -0
  36. package/build/src/tools/composite/users.d.ts +15 -0
  37. package/build/src/tools/composite/users.d.ts.map +1 -0
  38. package/build/src/tools/composite/users.js +93 -0
  39. package/build/src/tools/composite/users.js.map +1 -0
  40. package/build/src/tools/composite/workspace.d.ts +25 -0
  41. package/build/src/tools/composite/workspace.d.ts.map +1 -0
  42. package/build/src/tools/composite/workspace.js +72 -0
  43. package/build/src/tools/composite/workspace.js.map +1 -0
  44. package/build/src/tools/helpers/errors.d.ts +43 -0
  45. package/build/src/tools/helpers/errors.d.ts.map +1 -0
  46. package/build/src/tools/helpers/errors.js +162 -0
  47. package/build/src/tools/helpers/errors.js.map +1 -0
  48. package/build/src/tools/helpers/markdown.d.ts +45 -0
  49. package/build/src/tools/helpers/markdown.d.ts.map +1 -0
  50. package/build/src/tools/helpers/markdown.js +320 -0
  51. package/build/src/tools/helpers/markdown.js.map +1 -0
  52. package/build/src/tools/helpers/pagination.d.ts +42 -0
  53. package/build/src/tools/helpers/pagination.d.ts.map +1 -0
  54. package/build/src/tools/helpers/pagination.js +72 -0
  55. package/build/src/tools/helpers/pagination.js.map +1 -0
  56. package/build/src/tools/helpers/properties.d.ts +10 -0
  57. package/build/src/tools/helpers/properties.d.ts.map +1 -0
  58. package/build/src/tools/helpers/properties.js +57 -0
  59. package/build/src/tools/helpers/properties.js.map +1 -0
  60. package/build/src/tools/helpers/richtext.d.ts +85 -0
  61. package/build/src/tools/helpers/richtext.d.ts.map +1 -0
  62. package/build/src/tools/helpers/richtext.js +146 -0
  63. package/build/src/tools/helpers/richtext.js.map +1 -0
  64. package/build/src/tools/registry.d.ts +10 -0
  65. package/build/src/tools/registry.d.ts.map +1 -0
  66. package/build/src/tools/registry.js +342 -0
  67. package/build/src/tools/registry.js.map +1 -0
  68. package/build/tsconfig.tsbuildinfo +1 -0
  69. package/package.json +71 -0
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Workspace Mega Tool
3
+ * Workspace exploration and info
4
+ */
5
+ import { NotionMCPError, withErrorHandling } from '../helpers/errors.js';
6
+ import { autoPaginate } from '../helpers/pagination.js';
7
+ /**
8
+ * Unified workspace tool
9
+ * Maps to: GET /v1/users/me and POST /v1/search
10
+ */
11
+ export async function workspace(notion, input) {
12
+ return withErrorHandling(async () => {
13
+ switch (input.action) {
14
+ case 'info': {
15
+ const botUser = await notion.users.retrieve({ user_id: 'me' });
16
+ return {
17
+ action: 'info',
18
+ bot: {
19
+ id: botUser.id,
20
+ name: botUser.name || 'Bot',
21
+ type: botUser.type,
22
+ owner: botUser.bot?.owner
23
+ }
24
+ };
25
+ }
26
+ case 'search': {
27
+ if (!input.query) {
28
+ throw new NotionMCPError('query required for search action', 'VALIDATION_ERROR', 'Provide search query');
29
+ }
30
+ const searchParams = {
31
+ query: input.query
32
+ };
33
+ if (input.filter?.object) {
34
+ searchParams.filter = {
35
+ value: input.filter.object,
36
+ property: 'object'
37
+ };
38
+ }
39
+ if (input.sort) {
40
+ searchParams.sort = {
41
+ direction: input.sort.direction || 'descending',
42
+ timestamp: input.sort.timestamp || 'last_edited_time'
43
+ };
44
+ }
45
+ // Fetch results with pagination
46
+ const allResults = await autoPaginate((cursor) => notion.search({
47
+ ...searchParams,
48
+ start_cursor: cursor,
49
+ page_size: 100
50
+ }));
51
+ const results = input.limit ? allResults.slice(0, input.limit) : allResults;
52
+ return {
53
+ action: 'search',
54
+ query: input.query,
55
+ total: results.length,
56
+ results: results.map((item) => ({
57
+ id: item.id,
58
+ object: item.object,
59
+ title: item.object === 'page'
60
+ ? (item.properties?.title?.title?.[0]?.plain_text || item.properties?.Name?.title?.[0]?.plain_text || 'Untitled')
61
+ : (item.title?.[0]?.plain_text || 'Untitled'),
62
+ url: item.url,
63
+ last_edited_time: item.last_edited_time
64
+ }))
65
+ };
66
+ }
67
+ default:
68
+ throw new NotionMCPError(`Unknown action: ${input.action}`, 'VALIDATION_ERROR', 'Supported actions: info, search');
69
+ }
70
+ })();
71
+ }
72
+ //# sourceMappingURL=workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../../../src/tools/composite/workspace.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Error Handling Utilities
3
+ * AI-friendly error messages and suggestions
4
+ */
5
+ export declare class NotionMCPError extends Error {
6
+ code: string;
7
+ suggestion?: string | undefined;
8
+ details?: any | undefined;
9
+ constructor(message: string, code: string, suggestion?: string | undefined, details?: any | undefined);
10
+ toJSON(): {
11
+ error: string;
12
+ code: string;
13
+ message: string;
14
+ suggestion: string | undefined;
15
+ details: any;
16
+ };
17
+ }
18
+ /**
19
+ * Enhance Notion API error with helpful context
20
+ */
21
+ export declare function enhanceError(error: any): NotionMCPError;
22
+ /**
23
+ * Create AI-readable error message
24
+ */
25
+ export declare function aiReadableMessage(error: NotionMCPError): string;
26
+ /**
27
+ * Suggest fixes based on error
28
+ */
29
+ export declare function suggestFixes(error: NotionMCPError): string[];
30
+ /**
31
+ * Wrap async function with error handling
32
+ */
33
+ export declare function withErrorHandling<T extends (...args: any[]) => Promise<any>>(fn: T): (...args: Parameters<T>) => Promise<ReturnType<T>>;
34
+ /**
35
+ * Retry with exponential backoff
36
+ */
37
+ export declare function retryWithBackoff<T>(fn: () => Promise<T>, options?: {
38
+ maxRetries?: number;
39
+ initialDelay?: number;
40
+ maxDelay?: number;
41
+ backoffMultiplier?: number;
42
+ }): Promise<T>;
43
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../../src/tools/helpers/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,qBAAa,cAAe,SAAQ,KAAK;IAG9B,IAAI,EAAE,MAAM;IACZ,UAAU,CAAC,EAAE,MAAM;IACnB,OAAO,CAAC,EAAE,GAAG;gBAHpB,OAAO,EAAE,MAAM,EACR,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM,YAAA,EACnB,OAAO,CAAC,EAAE,GAAG,YAAA;IAMtB,MAAM;;;;;;;CASP;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,GAAG,GAAG,cAAc,CAsBvD;AA6ED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAY/D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,EAAE,CAyC5D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,EAC1E,EAAE,EAAE,CAAC,GACJ,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAQpD;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,GAAE;IACP,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAA;CACtB,GACL,OAAO,CAAC,CAAC,CAAC,CAkCZ"}
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Error Handling Utilities
3
+ * AI-friendly error messages and suggestions
4
+ */
5
+ export class NotionMCPError extends Error {
6
+ constructor(message, code, suggestion, details) {
7
+ super(message);
8
+ this.code = code;
9
+ this.suggestion = suggestion;
10
+ this.details = details;
11
+ this.name = 'NotionMCPError';
12
+ }
13
+ toJSON() {
14
+ return {
15
+ error: this.name,
16
+ code: this.code,
17
+ message: this.message,
18
+ suggestion: this.suggestion,
19
+ details: this.details
20
+ };
21
+ }
22
+ }
23
+ /**
24
+ * Enhance Notion API error with helpful context
25
+ */
26
+ export function enhanceError(error) {
27
+ // Notion API error
28
+ if (error.code) {
29
+ return handleNotionError(error);
30
+ }
31
+ // Network error
32
+ if (error.message?.includes('ECONNREFUSED') || error.message?.includes('ENOTFOUND')) {
33
+ return new NotionMCPError('Cannot connect to Notion API', 'NETWORK_ERROR', 'Check your internet connection and try again');
34
+ }
35
+ // Generic error
36
+ return new NotionMCPError(error.message || 'Unknown error occurred', 'UNKNOWN_ERROR', 'Please check your request and try again', error);
37
+ }
38
+ /**
39
+ * Handle specific Notion API errors
40
+ */
41
+ function handleNotionError(error) {
42
+ const code = error.code;
43
+ const message = error.message || 'Unknown Notion API error';
44
+ // Log full error for debugging
45
+ console.error('Notion API Error:', JSON.stringify({
46
+ code,
47
+ message,
48
+ body: error.body,
49
+ status: error.status
50
+ }, null, 2));
51
+ switch (code) {
52
+ case 'unauthorized':
53
+ return new NotionMCPError('Invalid or missing Notion API token', 'UNAUTHORIZED', 'Set NOTION_TOKEN environment variable with a valid integration token from https://www.notion.so/my-integrations');
54
+ case 'restricted_resource':
55
+ return new NotionMCPError('Integration does not have access to this resource', 'RESTRICTED_RESOURCE', 'Share the page/database with your integration in Notion settings');
56
+ case 'object_not_found':
57
+ return new NotionMCPError('Page or database not found', 'NOT_FOUND', 'Check that the ID is correct and the resource exists');
58
+ case 'validation_error':
59
+ return new NotionMCPError('Invalid request parameters', 'VALIDATION_ERROR', 'Check the API documentation for valid parameter formats', error.details);
60
+ case 'rate_limited':
61
+ return new NotionMCPError('Too many requests to Notion API', 'RATE_LIMITED', 'Wait a few seconds and try again. Consider batching operations.');
62
+ case 'conflict_error':
63
+ return new NotionMCPError('Conflict with existing data', 'CONFLICT', 'The resource may have been modified. Refresh and try again.');
64
+ case 'service_unavailable':
65
+ return new NotionMCPError('Notion API is temporarily unavailable', 'SERVICE_UNAVAILABLE', 'Wait a moment and try again. Check https://status.notion.so for updates.');
66
+ default:
67
+ return new NotionMCPError(message, code.toUpperCase(), 'Check the Notion API documentation for this error code');
68
+ }
69
+ }
70
+ /**
71
+ * Create AI-readable error message
72
+ */
73
+ export function aiReadableMessage(error) {
74
+ let message = `❌ ${error.message}`;
75
+ if (error.suggestion) {
76
+ message += `\n\n💡 Suggestion: ${error.suggestion}`;
77
+ }
78
+ if (error.details) {
79
+ message += `\n\n📋 Details: ${JSON.stringify(error.details, null, 2)}`;
80
+ }
81
+ return message;
82
+ }
83
+ /**
84
+ * Suggest fixes based on error
85
+ */
86
+ export function suggestFixes(error) {
87
+ const suggestions = [];
88
+ switch (error.code) {
89
+ case 'UNAUTHORIZED':
90
+ suggestions.push('Check that NOTION_TOKEN is set in your environment');
91
+ suggestions.push('Verify token at https://www.notion.so/my-integrations');
92
+ suggestions.push('Create a new integration token if needed');
93
+ break;
94
+ case 'RESTRICTED_RESOURCE':
95
+ suggestions.push('Open the page/database in Notion');
96
+ suggestions.push('Click "..." menu → Add connections → Select your integration');
97
+ suggestions.push('Grant access to parent pages if needed');
98
+ break;
99
+ case 'NOT_FOUND':
100
+ suggestions.push('Verify the page/database ID is correct');
101
+ suggestions.push('Check that the resource was not deleted');
102
+ suggestions.push('Ensure you have access permissions');
103
+ break;
104
+ case 'VALIDATION_ERROR':
105
+ suggestions.push('Check parameter types and formats');
106
+ suggestions.push('Review required vs optional parameters');
107
+ suggestions.push('Verify property names match database schema');
108
+ break;
109
+ case 'RATE_LIMITED':
110
+ suggestions.push('Reduce request frequency');
111
+ suggestions.push('Implement exponential backoff retry logic');
112
+ suggestions.push('Batch multiple operations together');
113
+ break;
114
+ default:
115
+ suggestions.push('Check Notion API status at https://status.notion.so');
116
+ suggestions.push('Review request parameters');
117
+ suggestions.push('Try again in a few moments');
118
+ }
119
+ return suggestions;
120
+ }
121
+ /**
122
+ * Wrap async function with error handling
123
+ */
124
+ export function withErrorHandling(fn) {
125
+ return async (...args) => {
126
+ try {
127
+ return await fn(...args);
128
+ }
129
+ catch (error) {
130
+ throw enhanceError(error);
131
+ }
132
+ };
133
+ }
134
+ /**
135
+ * Retry with exponential backoff
136
+ */
137
+ export async function retryWithBackoff(fn, options = {}) {
138
+ const { maxRetries = 3, initialDelay = 1000, maxDelay = 10000, backoffMultiplier = 2 } = options;
139
+ let lastError;
140
+ let delay = initialDelay;
141
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
142
+ try {
143
+ return await fn();
144
+ }
145
+ catch (error) {
146
+ lastError = error;
147
+ // Don't retry on certain errors
148
+ if (error.code === 'UNAUTHORIZED' || error.code === 'NOT_FOUND') {
149
+ throw enhanceError(error);
150
+ }
151
+ // Last attempt
152
+ if (attempt === maxRetries) {
153
+ break;
154
+ }
155
+ // Wait with exponential backoff
156
+ await new Promise(resolve => setTimeout(resolve, delay));
157
+ delay = Math.min(delay * backoffMultiplier, maxDelay);
158
+ }
159
+ }
160
+ throw enhanceError(lastError);
161
+ }
162
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../../src/tools/helpers/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,OAAO,cAAe,SAAQ,KAAK;IACvC,YACE,OAAe,EACR,IAAY,EACZ,UAAmB,EACnB,OAAa;QAEpB,KAAK,CAAC,OAAO,CAAC,CAAA;QAJP,SAAI,GAAJ,IAAI,CAAQ;QACZ,eAAU,GAAV,UAAU,CAAS;QACnB,YAAO,GAAP,OAAO,CAAM;QAGpB,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAA;IAC9B,CAAC;IAED,MAAM;QACJ,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,IAAI;YAChB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAA;IACH,CAAC;CACF;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,KAAU;IACrC,mBAAmB;IACnB,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QACf,OAAO,iBAAiB,CAAC,KAAK,CAAC,CAAA;IACjC,CAAC;IAED,gBAAgB;IAChB,IAAI,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,cAAc,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QACpF,OAAO,IAAI,cAAc,CACvB,8BAA8B,EAC9B,eAAe,EACf,8CAA8C,CAC/C,CAAA;IACH,CAAC;IAED,gBAAgB;IAChB,OAAO,IAAI,cAAc,CACvB,KAAK,CAAC,OAAO,IAAI,wBAAwB,EACzC,eAAe,EACf,yCAAyC,EACzC,KAAK,CACN,CAAA;AACH,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,KAAU;IACnC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAA;IACvB,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,0BAA0B,CAAA;IAE3D,+BAA+B;IAC/B,OAAO,CAAC,KAAK,CAAC,mBAAmB,EAAE,IAAI,CAAC,SAAS,CAAC;QAChD,IAAI;QACJ,OAAO;QACP,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,MAAM,EAAE,KAAK,CAAC,MAAM;KACrB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;IAEZ,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,cAAc;YACjB,OAAO,IAAI,cAAc,CACvB,qCAAqC,EACrC,cAAc,EACd,iHAAiH,CAClH,CAAA;QAEH,KAAK,qBAAqB;YACxB,OAAO,IAAI,cAAc,CACvB,mDAAmD,EACnD,qBAAqB,EACrB,kEAAkE,CACnE,CAAA;QAEH,KAAK,kBAAkB;YACrB,OAAO,IAAI,cAAc,CACvB,4BAA4B,EAC5B,WAAW,EACX,sDAAsD,CACvD,CAAA;QAEH,KAAK,kBAAkB;YACrB,OAAO,IAAI,cAAc,CACvB,4BAA4B,EAC5B,kBAAkB,EAClB,yDAAyD,EACzD,KAAK,CAAC,OAAO,CACd,CAAA;QAEH,KAAK,cAAc;YACjB,OAAO,IAAI,cAAc,CACvB,iCAAiC,EACjC,cAAc,EACd,iEAAiE,CAClE,CAAA;QAEH,KAAK,gBAAgB;YACnB,OAAO,IAAI,cAAc,CACvB,6BAA6B,EAC7B,UAAU,EACV,6DAA6D,CAC9D,CAAA;QAEH,KAAK,qBAAqB;YACxB,OAAO,IAAI,cAAc,CACvB,uCAAuC,EACvC,qBAAqB,EACrB,0EAA0E,CAC3E,CAAA;QAEH;YACE,OAAO,IAAI,cAAc,CACvB,OAAO,EACP,IAAI,CAAC,WAAW,EAAE,EAClB,wDAAwD,CACzD,CAAA;IACL,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAqB;IACrD,IAAI,OAAO,GAAG,KAAK,KAAK,CAAC,OAAO,EAAE,CAAA;IAElC,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACrB,OAAO,IAAI,sBAAsB,KAAK,CAAC,UAAU,EAAE,CAAA;IACrD,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,OAAO,IAAI,mBAAmB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAA;IACxE,CAAC;IAED,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,KAAqB;IAChD,MAAM,WAAW,GAAa,EAAE,CAAA;IAEhC,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,cAAc;YACjB,WAAW,CAAC,IAAI,CAAC,oDAAoD,CAAC,CAAA;YACtE,WAAW,CAAC,IAAI,CAAC,uDAAuD,CAAC,CAAA;YACzE,WAAW,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAA;YAC5D,MAAK;QAEP,KAAK,qBAAqB;YACxB,WAAW,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAA;YACpD,WAAW,CAAC,IAAI,CAAC,8DAA8D,CAAC,CAAA;YAChF,WAAW,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAA;YAC1D,MAAK;QAEP,KAAK,WAAW;YACd,WAAW,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAA;YAC1D,WAAW,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAA;YAC3D,WAAW,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAA;YACtD,MAAK;QAEP,KAAK,kBAAkB;YACrB,WAAW,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAA;YACrD,WAAW,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAA;YAC1D,WAAW,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAA;YAC/D,MAAK;QAEP,KAAK,cAAc;YACjB,WAAW,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAA;YAC5C,WAAW,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAA;YAC7D,WAAW,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAA;YACtD,MAAK;QAEP;YACE,WAAW,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAA;YACvE,WAAW,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAA;YAC7C,WAAW,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAA;IAClD,CAAC;IAED,OAAO,WAAW,CAAA;AACpB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAC/B,EAAK;IAEL,OAAO,KAAK,EAAE,GAAG,IAAmB,EAA0B,EAAE;QAC9D,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,CAAA;QAC1B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,YAAY,CAAC,KAAK,CAAC,CAAA;QAC3B,CAAC;IACH,CAAC,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,EAAoB,EACpB,UAKI,EAAE;IAEN,MAAM,EACJ,UAAU,GAAG,CAAC,EACd,YAAY,GAAG,IAAI,EACnB,QAAQ,GAAG,KAAK,EAChB,iBAAiB,GAAG,CAAC,EACtB,GAAG,OAAO,CAAA;IAEX,IAAI,SAAc,CAAA;IAClB,IAAI,KAAK,GAAG,YAAY,CAAA;IAExB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;QACvD,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAA;QACnB,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,SAAS,GAAG,KAAK,CAAA;YAEjB,gCAAgC;YAChC,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBAChE,MAAM,YAAY,CAAC,KAAK,CAAC,CAAA;YAC3B,CAAC;YAED,eAAe;YACf,IAAI,OAAO,KAAK,UAAU,EAAE,CAAC;gBAC3B,MAAK;YACP,CAAC;YAED,gCAAgC;YAChC,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAA;YACxD,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,iBAAiB,EAAE,QAAQ,CAAC,CAAA;QACvD,CAAC;IACH,CAAC;IAED,MAAM,YAAY,CAAC,SAAS,CAAC,CAAA;AAC/B,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Markdown to Notion Blocks Converter
3
+ * Converts markdown text to Notion block format
4
+ */
5
+ export interface NotionBlock {
6
+ object: 'block';
7
+ type: string;
8
+ [key: string]: any;
9
+ }
10
+ export interface RichText {
11
+ type: 'text';
12
+ text: {
13
+ content: string;
14
+ link?: {
15
+ url: string;
16
+ } | null;
17
+ };
18
+ annotations: {
19
+ bold: boolean;
20
+ italic: boolean;
21
+ strikethrough: boolean;
22
+ underline: boolean;
23
+ code: boolean;
24
+ color: string;
25
+ };
26
+ plain_text?: string;
27
+ href?: string | null;
28
+ }
29
+ /**
30
+ * Convert markdown string to Notion blocks
31
+ */
32
+ export declare function markdownToBlocks(markdown: string): NotionBlock[];
33
+ /**
34
+ * Convert Notion blocks to markdown
35
+ */
36
+ export declare function blocksToMarkdown(blocks: NotionBlock[]): string;
37
+ /**
38
+ * Parse inline markdown formatting to rich text
39
+ */
40
+ export declare function parseRichText(text: string): RichText[];
41
+ /**
42
+ * Extract plain text from rich text
43
+ */
44
+ export declare function extractPlainText(richText: RichText[]): string;
45
+ //# sourceMappingURL=markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../../../src/tools/helpers/markdown.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE;QACJ,OAAO,EAAE,MAAM,CAAA;QACf,IAAI,CAAC,EAAE;YAAE,GAAG,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAA;KAC9B,CAAA;IACD,WAAW,EAAE;QACX,IAAI,EAAE,OAAO,CAAA;QACb,MAAM,EAAE,OAAO,CAAA;QACf,aAAa,EAAE,OAAO,CAAA;QACtB,SAAS,EAAE,OAAO,CAAA;QAClB,IAAI,EAAE,OAAO,CAAA;QACb,KAAK,EAAE,MAAM,CAAA;KACd,CAAA;IACD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,EAAE,CAwEhE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAyC9D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CA0FtD;AAuBD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,MAAM,CAE7D"}
@@ -0,0 +1,320 @@
1
+ /**
2
+ * Markdown to Notion Blocks Converter
3
+ * Converts markdown text to Notion block format
4
+ */
5
+ /**
6
+ * Convert markdown string to Notion blocks
7
+ */
8
+ export function markdownToBlocks(markdown) {
9
+ const lines = markdown.split('\n');
10
+ const blocks = [];
11
+ let currentList = [];
12
+ let currentListType = null;
13
+ for (let i = 0; i < lines.length; i++) {
14
+ const line = lines[i];
15
+ // Flush list if we're not in a list anymore
16
+ if (currentListType && !isListItem(line)) {
17
+ blocks.push(...currentList);
18
+ currentList = [];
19
+ currentListType = null;
20
+ }
21
+ // Skip empty lines
22
+ if (!line.trim()) {
23
+ continue;
24
+ }
25
+ // Heading
26
+ if (line.startsWith('# ')) {
27
+ blocks.push(createHeading(1, line.slice(2)));
28
+ }
29
+ else if (line.startsWith('## ')) {
30
+ blocks.push(createHeading(2, line.slice(3)));
31
+ }
32
+ else if (line.startsWith('### ')) {
33
+ blocks.push(createHeading(3, line.slice(4)));
34
+ }
35
+ // Code block
36
+ else if (line.startsWith('```')) {
37
+ const language = line.slice(3).trim();
38
+ const codeLines = [];
39
+ i++;
40
+ while (i < lines.length && !lines[i].startsWith('```')) {
41
+ codeLines.push(lines[i]);
42
+ i++;
43
+ }
44
+ blocks.push(createCodeBlock(codeLines.join('\n'), language));
45
+ }
46
+ // Bulleted list
47
+ else if (line.match(/^[\-\*]\s/)) {
48
+ const text = line.slice(2);
49
+ currentListType = 'bulleted';
50
+ currentList.push(createBulletedListItem(text));
51
+ }
52
+ // Numbered list
53
+ else if (line.match(/^\d+\.\s/)) {
54
+ const text = line.replace(/^\d+\.\s/, '');
55
+ currentListType = 'numbered';
56
+ currentList.push(createNumberedListItem(text));
57
+ }
58
+ // Quote
59
+ else if (line.startsWith('> ')) {
60
+ blocks.push(createQuote(line.slice(2)));
61
+ }
62
+ // Divider
63
+ else if (line.match(/^[\-\*]{3,}$/)) {
64
+ blocks.push(createDivider());
65
+ }
66
+ // Regular paragraph
67
+ else {
68
+ blocks.push(createParagraph(line));
69
+ }
70
+ }
71
+ // Flush remaining list
72
+ if (currentList.length > 0) {
73
+ blocks.push(...currentList);
74
+ }
75
+ return blocks;
76
+ }
77
+ /**
78
+ * Convert Notion blocks to markdown
79
+ */
80
+ export function blocksToMarkdown(blocks) {
81
+ const lines = [];
82
+ for (const block of blocks) {
83
+ switch (block.type) {
84
+ case 'heading_1':
85
+ lines.push(`# ${richTextToMarkdown(block.heading_1.rich_text)}`);
86
+ break;
87
+ case 'heading_2':
88
+ lines.push(`## ${richTextToMarkdown(block.heading_2.rich_text)}`);
89
+ break;
90
+ case 'heading_3':
91
+ lines.push(`### ${richTextToMarkdown(block.heading_3.rich_text)}`);
92
+ break;
93
+ case 'paragraph':
94
+ lines.push(richTextToMarkdown(block.paragraph.rich_text));
95
+ break;
96
+ case 'bulleted_list_item':
97
+ lines.push(`- ${richTextToMarkdown(block.bulleted_list_item.rich_text)}`);
98
+ break;
99
+ case 'numbered_list_item':
100
+ lines.push(`1. ${richTextToMarkdown(block.numbered_list_item.rich_text)}`);
101
+ break;
102
+ case 'code':
103
+ lines.push('```' + (block.code.language || ''));
104
+ lines.push(richTextToMarkdown(block.code.rich_text));
105
+ lines.push('```');
106
+ break;
107
+ case 'quote':
108
+ lines.push(`> ${richTextToMarkdown(block.quote.rich_text)}`);
109
+ break;
110
+ case 'divider':
111
+ lines.push('---');
112
+ break;
113
+ default:
114
+ // Unsupported block type, skip
115
+ break;
116
+ }
117
+ }
118
+ return lines.join('\n');
119
+ }
120
+ /**
121
+ * Parse inline markdown formatting to rich text
122
+ */
123
+ export function parseRichText(text) {
124
+ const richText = [];
125
+ let current = '';
126
+ let bold = false;
127
+ let italic = false;
128
+ let code = false;
129
+ let strikethrough = false;
130
+ for (let i = 0; i < text.length; i++) {
131
+ const char = text[i];
132
+ const next = text[i + 1];
133
+ // Link [text](url)
134
+ if (char === '[') {
135
+ const closeBracket = text.indexOf(']', i);
136
+ const openParen = closeBracket !== -1 ? text.indexOf('(', closeBracket) : -1;
137
+ const closeParen = openParen !== -1 ? text.indexOf(')', openParen) : -1;
138
+ if (closeBracket !== -1 && openParen === closeBracket + 1 && closeParen !== -1) {
139
+ if (current) {
140
+ richText.push(createRichText(current, { bold, italic, code, strikethrough }));
141
+ current = '';
142
+ }
143
+ const linkText = text.slice(i + 1, closeBracket);
144
+ const linkUrl = text.slice(openParen + 1, closeParen);
145
+ richText.push({
146
+ type: 'text',
147
+ text: { content: linkText, link: { url: linkUrl } },
148
+ annotations: {
149
+ bold, italic, strikethrough,
150
+ underline: false,
151
+ code,
152
+ color: 'default'
153
+ }
154
+ });
155
+ i = closeParen;
156
+ continue;
157
+ }
158
+ }
159
+ // Bold **text**
160
+ if (char === '*' && next === '*') {
161
+ if (current) {
162
+ richText.push(createRichText(current, { bold, italic, code, strikethrough }));
163
+ current = '';
164
+ }
165
+ bold = !bold;
166
+ i++; // Skip next *
167
+ continue;
168
+ }
169
+ // Italic *text*
170
+ else if (char === '*' && next !== '*') {
171
+ if (current) {
172
+ richText.push(createRichText(current, { bold, italic, code, strikethrough }));
173
+ current = '';
174
+ }
175
+ italic = !italic;
176
+ continue;
177
+ }
178
+ // Code `text`
179
+ else if (char === '`') {
180
+ if (current) {
181
+ richText.push(createRichText(current, { bold, italic, code, strikethrough }));
182
+ current = '';
183
+ }
184
+ code = !code;
185
+ continue;
186
+ }
187
+ // Strikethrough ~~text~~
188
+ else if (char === '~' && next === '~') {
189
+ if (current) {
190
+ richText.push(createRichText(current, { bold, italic, code, strikethrough }));
191
+ current = '';
192
+ }
193
+ strikethrough = !strikethrough;
194
+ i++; // Skip next ~
195
+ continue;
196
+ }
197
+ current += char;
198
+ }
199
+ if (current) {
200
+ richText.push(createRichText(current, { bold, italic, code, strikethrough }));
201
+ }
202
+ return richText.length > 0 ? richText : [createRichText(text)];
203
+ }
204
+ /**
205
+ * Convert rich text array to plain markdown
206
+ */
207
+ function richTextToMarkdown(richText) {
208
+ if (!richText || !Array.isArray(richText))
209
+ return '';
210
+ return richText.map(rt => {
211
+ if (!rt || !rt.text)
212
+ return '';
213
+ let text = rt.text.content || '';
214
+ const annotations = rt.annotations || {};
215
+ if (annotations.bold)
216
+ text = `**${text}**`;
217
+ if (annotations.italic)
218
+ text = `*${text}*`;
219
+ if (annotations.code)
220
+ text = `\`${text}\``;
221
+ if (annotations.strikethrough)
222
+ text = `~~${text}~~`;
223
+ if (rt.text.link)
224
+ text = `[${text}](${rt.text.link.url})`;
225
+ return text;
226
+ }).join('');
227
+ }
228
+ /**
229
+ * Extract plain text from rich text
230
+ */
231
+ export function extractPlainText(richText) {
232
+ return richText.map(rt => rt.text.content).join('');
233
+ }
234
+ // Helper creators
235
+ function createRichText(content, annotations = {}) {
236
+ return {
237
+ type: 'text',
238
+ text: { content, link: null },
239
+ annotations: {
240
+ bold: annotations.bold || false,
241
+ italic: annotations.italic || false,
242
+ strikethrough: annotations.strikethrough || false,
243
+ underline: false,
244
+ code: annotations.code || false,
245
+ color: 'default'
246
+ }
247
+ };
248
+ }
249
+ function createHeading(level, text) {
250
+ const type = `heading_${level}`;
251
+ return {
252
+ object: 'block',
253
+ type,
254
+ [type]: {
255
+ rich_text: parseRichText(text),
256
+ color: 'default'
257
+ }
258
+ };
259
+ }
260
+ function createParagraph(text) {
261
+ return {
262
+ object: 'block',
263
+ type: 'paragraph',
264
+ paragraph: {
265
+ rich_text: parseRichText(text),
266
+ color: 'default'
267
+ }
268
+ };
269
+ }
270
+ function createBulletedListItem(text) {
271
+ return {
272
+ object: 'block',
273
+ type: 'bulleted_list_item',
274
+ bulleted_list_item: {
275
+ rich_text: parseRichText(text),
276
+ color: 'default'
277
+ }
278
+ };
279
+ }
280
+ function createNumberedListItem(text) {
281
+ return {
282
+ object: 'block',
283
+ type: 'numbered_list_item',
284
+ numbered_list_item: {
285
+ rich_text: parseRichText(text),
286
+ color: 'default'
287
+ }
288
+ };
289
+ }
290
+ function createCodeBlock(code, language) {
291
+ return {
292
+ object: 'block',
293
+ type: 'code',
294
+ code: {
295
+ rich_text: [createRichText(code)],
296
+ language: language || 'plain text'
297
+ }
298
+ };
299
+ }
300
+ function createQuote(text) {
301
+ return {
302
+ object: 'block',
303
+ type: 'quote',
304
+ quote: {
305
+ rich_text: parseRichText(text),
306
+ color: 'default'
307
+ }
308
+ };
309
+ }
310
+ function createDivider() {
311
+ return {
312
+ object: 'block',
313
+ type: 'divider',
314
+ divider: {}
315
+ };
316
+ }
317
+ function isListItem(line) {
318
+ return line.match(/^[\-\*]\s/) !== null || line.match(/^\d+\.\s/) !== null;
319
+ }
320
+ //# sourceMappingURL=markdown.js.map