@atikk-co-jp/notion-mcp-server 0.6.0 → 0.8.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 (135) hide show
  1. package/README.ja.md +1 -0
  2. package/README.md +20 -2
  3. package/dist/src/converters/__tests__/block-to-markdown.test.js +482 -111
  4. package/dist/src/converters/__tests__/markdown-to-blocks.test.js +116 -13
  5. package/dist/src/converters/__tests__/page-to-markdown.test.js +83 -70
  6. package/dist/src/converters/__tests__/rich-text-to-markdown.test.js +23 -26
  7. package/dist/src/converters/block-to-markdown.d.ts +4 -13
  8. package/dist/src/converters/block-to-markdown.d.ts.map +1 -1
  9. package/dist/src/converters/block-to-markdown.js +117 -120
  10. package/dist/src/converters/index.d.ts +3 -3
  11. package/dist/src/converters/index.d.ts.map +1 -1
  12. package/dist/src/converters/index.js +1 -1
  13. package/dist/src/converters/markdown-to-blocks.d.ts +21 -7
  14. package/dist/src/converters/markdown-to-blocks.d.ts.map +1 -1
  15. package/dist/src/converters/markdown-to-blocks.js +56 -0
  16. package/dist/src/converters/page-to-markdown.d.ts +5 -20
  17. package/dist/src/converters/page-to-markdown.d.ts.map +1 -1
  18. package/dist/src/converters/page-to-markdown.js +47 -37
  19. package/dist/src/converters/rich-text-to-markdown.d.ts +3 -47
  20. package/dist/src/converters/rich-text-to-markdown.d.ts.map +1 -1
  21. package/dist/src/converters/rich-text-to-markdown.js +15 -14
  22. package/dist/src/notion-client.d.ts +5 -177
  23. package/dist/src/notion-client.d.ts.map +1 -1
  24. package/dist/src/notion-client.js +6 -172
  25. package/dist/src/schemas/descriptions/examples.d.ts +14 -0
  26. package/dist/src/schemas/descriptions/examples.d.ts.map +1 -0
  27. package/dist/src/schemas/descriptions/examples.js +87 -0
  28. package/dist/src/schemas/descriptions/fields.d.ts +146 -0
  29. package/dist/src/schemas/descriptions/fields.d.ts.map +1 -0
  30. package/dist/src/schemas/descriptions/fields.js +184 -0
  31. package/dist/src/schemas/descriptions/index.d.ts +3 -0
  32. package/dist/src/schemas/descriptions/index.d.ts.map +1 -0
  33. package/dist/src/schemas/descriptions/index.js +2 -0
  34. package/dist/src/schemas/index.d.ts +1 -5
  35. package/dist/src/schemas/index.d.ts.map +1 -1
  36. package/dist/src/schemas/index.js +2 -10
  37. package/dist/src/tools/__tests__/context-size.test.d.ts +2 -0
  38. package/dist/src/tools/__tests__/context-size.test.d.ts.map +1 -0
  39. package/dist/src/tools/__tests__/context-size.test.js +143 -0
  40. package/dist/src/tools/__tests__/error-handler.test.d.ts +2 -0
  41. package/dist/src/tools/__tests__/error-handler.test.d.ts.map +1 -0
  42. package/dist/src/tools/__tests__/error-handler.test.js +125 -0
  43. package/dist/src/tools/append-block-children.d.ts.map +1 -1
  44. package/dist/src/tools/append-block-children.js +8 -5
  45. package/dist/src/tools/append-blocks-simple.d.ts.map +1 -1
  46. package/dist/src/tools/append-blocks-simple.js +9 -13
  47. package/dist/src/tools/archive-database.d.ts.map +1 -1
  48. package/dist/src/tools/archive-database.js +4 -4
  49. package/dist/src/tools/archive-page.d.ts.map +1 -1
  50. package/dist/src/tools/archive-page.js +3 -3
  51. package/dist/src/tools/create-comment-simple.d.ts +4 -0
  52. package/dist/src/tools/create-comment-simple.d.ts.map +1 -0
  53. package/dist/src/tools/create-comment-simple.js +51 -0
  54. package/dist/src/tools/create-comment.d.ts.map +1 -1
  55. package/dist/src/tools/create-comment.js +28 -8
  56. package/dist/src/tools/create-database.d.ts.map +1 -1
  57. package/dist/src/tools/create-database.js +19 -25
  58. package/dist/src/tools/create-page-simple.d.ts +1 -1
  59. package/dist/src/tools/create-page-simple.d.ts.map +1 -1
  60. package/dist/src/tools/create-page-simple.js +41 -26
  61. package/dist/src/tools/create-page.d.ts.map +1 -1
  62. package/dist/src/tools/create-page.js +11 -7
  63. package/dist/src/tools/delete-block.d.ts.map +1 -1
  64. package/dist/src/tools/delete-block.js +2 -1
  65. package/dist/src/tools/get-block-children.d.ts +1 -1
  66. package/dist/src/tools/get-block-children.d.ts.map +1 -1
  67. package/dist/src/tools/get-block-children.js +13 -27
  68. package/dist/src/tools/index.d.ts +4 -3
  69. package/dist/src/tools/index.d.ts.map +1 -1
  70. package/dist/src/tools/index.js +5 -3
  71. package/dist/src/tools/list-comments.d.ts +1 -1
  72. package/dist/src/tools/list-comments.d.ts.map +1 -1
  73. package/dist/src/tools/list-comments.js +11 -10
  74. package/dist/src/tools/list-users.d.ts.map +1 -1
  75. package/dist/src/tools/list-users.js +4 -3
  76. package/dist/src/tools/move-page.d.ts.map +1 -1
  77. package/dist/src/tools/move-page.js +5 -4
  78. package/dist/src/tools/query-data-source.d.ts +1 -1
  79. package/dist/src/tools/query-data-source.d.ts.map +1 -1
  80. package/dist/src/tools/query-data-source.js +23 -24
  81. package/dist/src/tools/retrieve-block.d.ts +1 -1
  82. package/dist/src/tools/retrieve-block.d.ts.map +1 -1
  83. package/dist/src/tools/retrieve-block.js +13 -10
  84. package/dist/src/tools/retrieve-bot-user.js +1 -1
  85. package/dist/src/tools/retrieve-data-source.d.ts +1 -1
  86. package/dist/src/tools/retrieve-data-source.d.ts.map +1 -1
  87. package/dist/src/tools/retrieve-data-source.js +15 -11
  88. package/dist/src/tools/retrieve-database.d.ts +1 -1
  89. package/dist/src/tools/retrieve-database.d.ts.map +1 -1
  90. package/dist/src/tools/retrieve-database.js +11 -6
  91. package/dist/src/tools/retrieve-page-property.d.ts.map +1 -1
  92. package/dist/src/tools/retrieve-page-property.js +7 -7
  93. package/dist/src/tools/retrieve-page.d.ts +1 -1
  94. package/dist/src/tools/retrieve-page.d.ts.map +1 -1
  95. package/dist/src/tools/retrieve-page.js +15 -20
  96. package/dist/src/tools/retrieve-user.d.ts.map +1 -1
  97. package/dist/src/tools/retrieve-user.js +2 -1
  98. package/dist/src/tools/search.d.ts.map +1 -1
  99. package/dist/src/tools/search.js +10 -17
  100. package/dist/src/tools/update-block-simple.d.ts +1 -1
  101. package/dist/src/tools/update-block-simple.d.ts.map +1 -1
  102. package/dist/src/tools/update-block-simple.js +14 -4
  103. package/dist/src/tools/update-block.d.ts.map +1 -1
  104. package/dist/src/tools/update-block.js +8 -5
  105. package/dist/src/tools/update-data-source.d.ts.map +1 -1
  106. package/dist/src/tools/update-data-source.js +8 -8
  107. package/dist/src/tools/update-database.d.ts.map +1 -1
  108. package/dist/src/tools/update-database.js +22 -32
  109. package/dist/src/tools/update-page.d.ts.map +1 -1
  110. package/dist/src/tools/update-page.js +13 -8
  111. package/dist/src/utils/error-handler.d.ts +20 -0
  112. package/dist/src/utils/error-handler.d.ts.map +1 -1
  113. package/dist/src/utils/error-handler.js +63 -0
  114. package/dist/src/utils/index.d.ts +1 -1
  115. package/dist/src/utils/index.d.ts.map +1 -1
  116. package/dist/src/utils/index.js +1 -1
  117. package/package.json +2 -1
  118. package/dist/src/schemas/block.d.ts +0 -3787
  119. package/dist/src/schemas/block.d.ts.map +0 -1
  120. package/dist/src/schemas/block.js +0 -402
  121. package/dist/src/schemas/common.d.ts +0 -638
  122. package/dist/src/schemas/common.d.ts.map +0 -1
  123. package/dist/src/schemas/common.js +0 -163
  124. package/dist/src/schemas/database.d.ts +0 -687
  125. package/dist/src/schemas/database.d.ts.map +0 -1
  126. package/dist/src/schemas/database.js +0 -258
  127. package/dist/src/schemas/filter.d.ts +0 -611
  128. package/dist/src/schemas/filter.d.ts.map +0 -1
  129. package/dist/src/schemas/filter.js +0 -222
  130. package/dist/src/schemas/page.d.ts +0 -2607
  131. package/dist/src/schemas/page.d.ts.map +0 -1
  132. package/dist/src/schemas/page.js +0 -328
  133. package/dist/src/schemas/schemas.test.d.ts +0 -2
  134. package/dist/src/schemas/schemas.test.d.ts.map +0 -1
  135. package/dist/src/schemas/schemas.test.js +0 -418
@@ -1,11 +1,10 @@
1
1
  import { z } from 'zod';
2
+ import { isFullDataSource } from '../notion-client.js';
3
+ import { F } from '../schemas/descriptions/index.js';
2
4
  import { formatResponse, formatSimpleResponse, handleError } from '../utils/index.js';
3
5
  const inputSchema = {
4
- data_source_id: z.string().describe('Data source ID'),
5
- format: z
6
- .enum(['json', 'simple'])
7
- .optional()
8
- .describe("Output format: 'simple' (default) or 'json'"),
6
+ data_source_id: z.string().describe(F.data_source_id),
7
+ format: z.enum(['json', 'simple']).optional().describe(F.format),
9
8
  };
10
9
  export function registerRetrieveDataSource(server, notion) {
11
10
  server.registerTool('retrieve-data-source', {
@@ -19,6 +18,10 @@ export function registerRetrieveDataSource(server, notion) {
19
18
  if (format === 'json') {
20
19
  return formatResponse(response);
21
20
  }
21
+ // Need full data source for simple format
22
+ if (!isFullDataSource(response)) {
23
+ return formatResponse(response);
24
+ }
22
25
  // Simple format: extract essential info
23
26
  const simpleProperties = {};
24
27
  for (const [name, prop] of Object.entries(response.properties)) {
@@ -27,16 +30,17 @@ export function registerRetrieveDataSource(server, notion) {
27
30
  type: prop.type,
28
31
  };
29
32
  // Include options for select/multi_select/status
30
- if (prop.type === 'select' && prop.select) {
31
- const selectProp = prop.select;
33
+ const propAny = prop;
34
+ if (prop.type === 'select' && propAny.select) {
35
+ const selectProp = propAny.select;
32
36
  simpleProp.options = selectProp.options;
33
37
  }
34
- else if (prop.type === 'multi_select' && prop.multi_select) {
35
- const multiSelectProp = prop.multi_select;
38
+ else if (prop.type === 'multi_select' && propAny.multi_select) {
39
+ const multiSelectProp = propAny.multi_select;
36
40
  simpleProp.options = multiSelectProp.options;
37
41
  }
38
- else if (prop.type === 'status' && prop.status) {
39
- const statusProp = prop.status;
42
+ else if (prop.type === 'status' && propAny.status) {
43
+ const statusProp = propAny.status;
40
44
  simpleProp.options = statusProp.options;
41
45
  }
42
46
  simpleProperties[name] = simpleProp;
@@ -1,4 +1,4 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import type { NotionClient } from '../notion-client.js';
2
+ import { type NotionClient } from '../notion-client.js';
3
3
  export declare function registerRetrieveDatabase(server: McpServer, notion: NotionClient): void;
4
4
  //# sourceMappingURL=retrieve-database.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"retrieve-database.d.ts","sourceRoot":"","sources":["../../../src/tools/retrieve-database.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AA+BvD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAsCtF"}
1
+ {"version":3,"file":"retrieve-database.d.ts","sourceRoot":"","sources":["../../../src/tools/retrieve-database.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAA;AASvE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CA6CtF"}
@@ -1,11 +1,10 @@
1
1
  import { z } from 'zod';
2
+ import { isFullDatabase } from '../notion-client.js';
3
+ import { F } from '../schemas/descriptions/index.js';
2
4
  import { formatResponse, formatSimpleResponse, handleError } from '../utils/index.js';
3
5
  const inputSchema = {
4
- database_id: z.string().describe('Database ID'),
5
- format: z
6
- .enum(['json', 'simple'])
7
- .optional()
8
- .describe("Output format: 'simple' (default) or 'json'"),
6
+ database_id: z.string().describe(F.database_id),
7
+ format: z.enum(['json', 'simple']).optional().describe(F.format),
9
8
  };
10
9
  export function registerRetrieveDatabase(server, notion) {
11
10
  server.registerTool('retrieve-database', {
@@ -19,11 +18,17 @@ export function registerRetrieveDatabase(server, notion) {
19
18
  if (format === 'json') {
20
19
  return formatResponse(response);
21
20
  }
21
+ // Need full database for simple format
22
+ if (!isFullDatabase(response)) {
23
+ return formatResponse(response);
24
+ }
22
25
  // Simple format: extract essential info
23
26
  const simpleResponse = {
24
27
  id: response.id,
25
28
  title: response.title.map((t) => t.plain_text).join(''),
26
- description: response.description.map((t) => t.plain_text).join(''),
29
+ description: response.description
30
+ .map((t) => t.plain_text)
31
+ .join(''),
27
32
  url: response.url,
28
33
  is_inline: response.is_inline,
29
34
  in_trash: response.in_trash,
@@ -1 +1 @@
1
- {"version":3,"file":"retrieve-page-property.d.ts","sourceRoot":"","sources":["../../../src/tools/retrieve-page-property.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAmBvD,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAkC1F"}
1
+ {"version":3,"file":"retrieve-page-property.d.ts","sourceRoot":"","sources":["../../../src/tools/retrieve-page-property.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAWvD,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAiC1F"}
@@ -1,19 +1,19 @@
1
1
  import { z } from 'zod';
2
+ import { F } from '../schemas/descriptions/index.js';
2
3
  import { formatPaginatedResponse, formatResponse, handleError } from '../utils/index.js';
3
4
  const inputSchema = {
4
- page_id: z.string().describe('Page ID'),
5
- property_id: z.string().describe('Property ID (from page properties)'),
6
- start_cursor: z.string().optional().describe('Pagination cursor'),
7
- page_size: z.number().optional().describe('Number of results (1-100)'),
5
+ page_id: z.string().describe(F.page_id),
6
+ property_id: z.string().describe(F.property_id),
7
+ start_cursor: z.string().optional().describe(F.start_cursor),
8
+ page_size: z.number().optional().describe(F.page_size),
8
9
  };
9
10
  export function registerRetrievePageProperty(server, notion) {
10
11
  server.registerTool('retrieve-page-property', {
11
- description: 'Retrieve a specific property value from a page. Supports pagination for properties with many values (e.g., rollup, relation). ' +
12
- 'Use the property_id from the page properties object.',
12
+ description: 'Get a property with pagination (for relation/rollup with many items). For simple properties, use retrieve-page instead.',
13
13
  inputSchema,
14
14
  }, async ({ page_id, property_id, start_cursor, page_size }) => {
15
15
  try {
16
- const response = await notion.pages.retrieveProperty({
16
+ const response = await notion.pages.properties.retrieve({
17
17
  page_id,
18
18
  property_id,
19
19
  start_cursor,
@@ -1,4 +1,4 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import type { NotionClient } from '../notion-client.js';
2
+ import { type NotionClient } from '../notion-client.js';
3
3
  export declare function registerRetrievePage(server: McpServer, notion: NotionClient): void;
4
4
  //# sourceMappingURL=retrieve-page.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"retrieve-page.d.ts","sourceRoot":"","sources":["../../../src/tools/retrieve-page.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAQxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAkCvD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAgDlF"}
1
+ {"version":3,"file":"retrieve-page.d.ts","sourceRoot":"","sources":["../../../src/tools/retrieve-page.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,qBAAqB,CAAA;AAU5B,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAkDlF"}
@@ -1,18 +1,12 @@
1
1
  import { z } from 'zod';
2
- import { blocksToMarkdown, pageToSimple, } from '../converters/index.js';
2
+ import { blocksToMarkdown, pageToSimple } from '../converters/index.js';
3
+ import { isFullBlock, isFullPage, } from '../notion-client.js';
4
+ import { F } from '../schemas/descriptions/index.js';
3
5
  import { formatResponse, formatSimpleResponse, handleError } from '../utils/index.js';
4
6
  const inputSchema = {
5
- page_id: z.string().describe('The ID of the page to retrieve'),
6
- format: z
7
- .enum(['json', 'simple'])
8
- .optional()
9
- .default('simple')
10
- .describe("Output format: 'simple' (default) returns simplified property values with reduced token usage, 'json' returns raw Notion API response"),
11
- include_content: z
12
- .boolean()
13
- .optional()
14
- .default(true)
15
- .describe('Include page content (blocks) as markdown. Default is true. Set to false to only retrieve page properties.'),
7
+ page_id: z.string().describe(F.page_id),
8
+ format: z.enum(['json', 'simple']).optional().default('simple').describe(F.format),
9
+ include_content: z.boolean().optional().default(true).describe(F.include_content),
16
10
  };
17
11
  export function registerRetrievePage(server, notion) {
18
12
  server.registerTool('retrieve-page', {
@@ -24,19 +18,20 @@ export function registerRetrievePage(server, notion) {
24
18
  // コンテンツを取得
25
19
  let content;
26
20
  if (include_content) {
27
- const blocksResponse = await notion.blocks.children.list({
28
- block_id: page_id,
29
- });
21
+ const blocksResponse = await notion.blocks.children.list({ block_id: page_id });
22
+ // Filter to full blocks
23
+ const blocks = blocksResponse.results.filter(isFullBlock);
30
24
  // 子ブロックを再帰的に取得するヘルパー
31
25
  const fetchChildren = async (blockId) => {
32
- const res = await notion.blocks.children.list({
33
- block_id: blockId,
34
- });
35
- return res.results;
26
+ const res = await notion.blocks.children.list({ block_id: blockId });
27
+ return res.results.filter(isFullBlock);
36
28
  };
37
- content = await blocksToMarkdown(blocksResponse.results, { fetchChildren });
29
+ content = await blocksToMarkdown(blocks, { fetchChildren });
38
30
  }
39
31
  if (format === 'simple') {
32
+ if (!isFullPage(response)) {
33
+ return formatResponse(response);
34
+ }
40
35
  const simple = pageToSimple(response);
41
36
  if (content !== undefined) {
42
37
  return formatSimpleResponse({ ...simple, content });
@@ -1 +1 @@
1
- {"version":3,"file":"retrieve-user.d.ts","sourceRoot":"","sources":["../../../src/tools/retrieve-user.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAOvD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAiBlF"}
1
+ {"version":3,"file":"retrieve-user.d.ts","sourceRoot":"","sources":["../../../src/tools/retrieve-user.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAQvD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAiBlF"}
@@ -1,7 +1,8 @@
1
1
  import { z } from 'zod';
2
+ import { F } from '../schemas/descriptions/index.js';
2
3
  import { formatResponse, handleError } from '../utils/index.js';
3
4
  const inputSchema = {
4
- user_id: z.string().describe('User ID'),
5
+ user_id: z.string().describe(F.user_id),
5
6
  };
6
7
  export function registerRetrieveUser(server, notion) {
7
8
  server.registerTool('retrieve-user', {
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/tools/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AA8CvD,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAiD5E"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/tools/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AA6BvD,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAmD5E"}
@@ -1,33 +1,24 @@
1
1
  import { z } from 'zod';
2
- import { formatPaginatedResponse, handleError } from '../utils/index.js';
2
+ import { F } from '../schemas/descriptions/index.js';
3
+ import { formatPaginatedResponse, handleErrorWithContext } from '../utils/index.js';
3
4
  const inputSchema = {
4
- query: z.string().optional().describe('Text to search for in page titles and content'),
5
+ query: z.string().optional().describe(F.query),
5
6
  filter: z
6
7
  .object({
7
8
  value: z.enum(['page', 'data_source']),
8
9
  property: z.literal('object'),
9
10
  })
10
11
  .optional()
11
- .describe('Filter to limit results to pages or data sources. ' +
12
- 'Example: { "value": "page", "property": "object" }'),
12
+ .describe(F.filter_search),
13
13
  sort: z
14
14
  .object({
15
15
  direction: z.enum(['ascending', 'descending']),
16
16
  timestamp: z.literal('last_edited_time'),
17
17
  })
18
18
  .optional()
19
- .describe('Sort order for results. ' +
20
- 'Example: { "direction": "descending", "timestamp": "last_edited_time" }'),
21
- start_cursor: z
22
- .string()
23
- .optional()
24
- .describe('Cursor for pagination. Use the next_cursor from previous response.'),
25
- page_size: z
26
- .number()
27
- .min(1)
28
- .max(100)
29
- .optional()
30
- .describe('Number of results to return (1-100). Default is 100.'),
19
+ .describe(F.sort),
20
+ start_cursor: z.string().optional().describe(F.start_cursor),
21
+ page_size: z.number().min(1).max(100).optional().describe(F.page_size),
31
22
  };
32
23
  export function registerSearch(server, notion) {
33
24
  server.registerTool('search', {
@@ -59,7 +50,9 @@ export function registerSearch(server, notion) {
59
50
  return formatPaginatedResponse(response.results, response.has_more, response.next_cursor);
60
51
  }
61
52
  catch (error) {
62
- return handleError(error);
53
+ return handleErrorWithContext(error, notion, {
54
+ exampleType: 'filter',
55
+ });
63
56
  }
64
57
  });
65
58
  }
@@ -1,4 +1,4 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import type { NotionClient } from '../notion-client.js';
2
+ import { type NotionClient } from '../notion-client.js';
3
3
  export declare function registerUpdateBlockSimple(server: McpServer, notion: NotionClient): void;
4
4
  //# sourceMappingURL=update-block-simple.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"update-block-simple.d.ts","sourceRoot":"","sources":["../../../src/tools/update-block-simple.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAwBvD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CA2DvF"}
1
+ {"version":3,"file":"update-block-simple.d.ts","sourceRoot":"","sources":["../../../src/tools/update-block-simple.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAyBpE,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAiEvF"}
@@ -1,9 +1,11 @@
1
1
  import { z } from 'zod';
2
2
  import { parseInlineMarkdown } from '../converters/index.js';
3
+ import { isFullBlock } from '../notion-client.js';
4
+ import { F } from '../schemas/descriptions/index.js';
3
5
  import { formatResponse, handleError } from '../utils/index.js';
4
6
  const inputSchema = {
5
- block_id: z.string().describe('Block ID to update'),
6
- content: z.string().describe('New content in Markdown format'),
7
+ block_id: z.string().describe(F.block_id),
8
+ content: z.string().describe(F.content),
7
9
  };
8
10
  // Supported block types for simple update
9
11
  const SUPPORTED_TYPES = [
@@ -28,6 +30,14 @@ export function registerUpdateBlockSimple(server, notion) {
28
30
  try {
29
31
  // First, retrieve the block to get its type
30
32
  const existingBlock = await notion.blocks.retrieve({ block_id });
33
+ if (!isFullBlock(existingBlock)) {
34
+ return {
35
+ content: [
36
+ { type: 'text', text: 'Error: Could not retrieve full block details.' },
37
+ ],
38
+ isError: true,
39
+ };
40
+ }
31
41
  const blockType = existingBlock.type;
32
42
  if (!SUPPORTED_TYPES.includes(blockType)) {
33
43
  return {
@@ -46,11 +56,11 @@ export function registerUpdateBlockSimple(server, notion) {
46
56
  const params = {
47
57
  block_id,
48
58
  };
49
- if (blockType === 'to_do') {
59
+ if (blockType === 'to_do' && existingBlock.type === 'to_do') {
50
60
  // Preserve the checked state for to_do blocks
51
61
  params[blockType] = {
52
62
  rich_text: richText,
53
- checked: existingBlock.to_do?.checked ?? false,
63
+ checked: existingBlock.to_do.checked ?? false,
54
64
  };
55
65
  }
56
66
  else {
@@ -1 +1 @@
1
- {"version":3,"file":"update-block.d.ts","sourceRoot":"","sources":["../../../src/tools/update-block.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AASvD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CA4BjF"}
1
+ {"version":3,"file":"update-block.d.ts","sourceRoot":"","sources":["../../../src/tools/update-block.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAUvD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CA8BjF"}
@@ -1,9 +1,10 @@
1
1
  import { z } from 'zod';
2
- import { formatResponse, handleError } from '../utils/index.js';
2
+ import { F } from '../schemas/descriptions/index.js';
3
+ import { formatResponse, handleErrorWithContext } from '../utils/index.js';
3
4
  const inputSchema = {
4
- block_id: z.string().describe('Block ID to update'),
5
- block: z.record(z.string(), z.any()).describe('Block data with type-specific properties'),
6
- archived: z.boolean().optional().describe('Set to true to archive the block'),
5
+ block_id: z.string().describe(F.block_id),
6
+ block: z.record(z.string(), z.any()).describe(F.block),
7
+ archived: z.boolean().optional().describe(F.archived),
7
8
  };
8
9
  export function registerUpdateBlock(server, notion) {
9
10
  server.registerTool('update-block', {
@@ -24,7 +25,9 @@ export function registerUpdateBlock(server, notion) {
24
25
  return formatResponse(response);
25
26
  }
26
27
  catch (error) {
27
- return handleError(error);
28
+ return handleErrorWithContext(error, notion, {
29
+ exampleType: 'block',
30
+ });
28
31
  }
29
32
  });
30
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"update-data-source.d.ts","sourceRoot":"","sources":["../../../src/tools/update-data-source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAYvD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CA6BtF"}
1
+ {"version":3,"file":"update-data-source.d.ts","sourceRoot":"","sources":["../../../src/tools/update-data-source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAcvD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CA+BtF"}
@@ -1,12 +1,10 @@
1
1
  import { z } from 'zod';
2
- import { formatResponse, handleError } from '../utils/index.js';
2
+ import { F } from '../schemas/descriptions/index.js';
3
+ import { formatResponse, handleErrorWithContext } from '../utils/index.js';
3
4
  // Minimal schema for MCP (full validation by Notion API)
4
5
  const inputSchema = {
5
- data_source_id: z.string().describe('Data source ID'),
6
- properties: z
7
- .record(z.string(), z.any())
8
- .optional()
9
- .describe('Properties to add/update/delete (set to null to delete)'),
6
+ data_source_id: z.string().describe(F.data_source_id),
7
+ properties: z.record(z.string(), z.any()).optional().describe(F.properties_update),
10
8
  };
11
9
  export function registerUpdateDataSource(server, notion) {
12
10
  server.registerTool('update-data-source', {
@@ -20,12 +18,14 @@ export function registerUpdateDataSource(server, notion) {
20
18
  if (properties !== undefined) {
21
19
  params.properties = properties;
22
20
  }
23
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
24
21
  const response = await notion.dataSources.update(params);
25
22
  return formatResponse(response);
26
23
  }
27
24
  catch (error) {
28
- return handleError(error);
25
+ return handleErrorWithContext(error, notion, {
26
+ dataSourceId: data_source_id,
27
+ exampleType: 'schema',
28
+ });
29
29
  }
30
30
  });
31
31
  }
@@ -1 +1 @@
1
- {"version":3,"file":"update-database.d.ts","sourceRoot":"","sources":["../../../src/tools/update-database.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAgBvD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CA4DpF"}
1
+ {"version":3,"file":"update-database.d.ts","sourceRoot":"","sources":["../../../src/tools/update-database.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAkBvD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAgCpF"}
@@ -1,16 +1,18 @@
1
1
  import { z } from 'zod';
2
- import { formatResponse, handleError } from '../utils/index.js';
2
+ import { F } from '../schemas/descriptions/index.js';
3
+ import { formatResponse, handleErrorWithContext } from '../utils/index.js';
3
4
  // Minimal schema for MCP (full validation by Notion API)
4
5
  // Note: Properties (schema) updates should use update-data-source in API 2025-09-03
6
+ // Uses z.any() for title/description/icon/cover to reduce context size (~3,700 tokens saved)
5
7
  const inputSchema = {
6
- database_id: z.string().describe('Database ID'),
7
- title: z.array(z.any()).optional().describe('New title'),
8
- description: z.array(z.any()).optional().describe('New description'),
9
- icon: z.any().optional().describe('Icon object { type: "emoji", emoji: "📝" } or null to remove. Emoji must be an actual emoji character.'),
10
- cover: z.any().optional().describe('Cover (null to remove)'),
11
- is_inline: z.boolean().optional().describe('Inline database'),
12
- archived: z.boolean().optional().describe('Archive status'),
13
- is_locked: z.boolean().optional().describe('Lock the database to prevent edits in the UI. Set to true to lock, false to unlock.'),
8
+ database_id: z.string().describe(F.database_id),
9
+ title: z.any().optional().describe(F.title),
10
+ description: z.any().optional().describe(F.description),
11
+ icon: z.any().nullable().optional().describe(F.icon),
12
+ cover: z.any().nullable().optional().describe(F.cover),
13
+ is_inline: z.boolean().optional().describe(F.is_inline),
14
+ archived: z.boolean().optional().describe(F.archived),
15
+ is_locked: z.boolean().optional().describe(F.is_locked),
14
16
  };
15
17
  export function registerUpdateDatabase(server, notion) {
16
18
  server.registerTool('update-database', {
@@ -19,36 +21,24 @@ export function registerUpdateDatabase(server, notion) {
19
21
  inputSchema,
20
22
  }, async ({ database_id, title, description, icon, cover, is_inline, archived, is_locked }) => {
21
23
  try {
24
+ // Build params with only defined values (API validates full structure)
22
25
  const params = {
23
26
  database_id,
27
+ ...(title !== undefined && { title }),
28
+ ...(description !== undefined && { description }),
29
+ ...(icon !== undefined && { icon }),
30
+ ...(cover !== undefined && { cover }),
31
+ ...(is_inline !== undefined && { is_inline }),
32
+ ...(archived !== undefined && { archived }),
33
+ ...(is_locked !== undefined && { is_locked }),
24
34
  };
25
- if (title !== undefined) {
26
- params.title = title;
27
- }
28
- if (description !== undefined) {
29
- params.description = description;
30
- }
31
- if (icon !== undefined) {
32
- params.icon = icon;
33
- }
34
- if (cover !== undefined) {
35
- params.cover = cover;
36
- }
37
- if (is_inline !== undefined) {
38
- params.is_inline = is_inline;
39
- }
40
- if (archived !== undefined) {
41
- params.archived = archived;
42
- }
43
- if (is_locked !== undefined) {
44
- params.is_locked = is_locked;
45
- }
46
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
35
  const response = await notion.databases.update(params);
48
36
  return formatResponse(response);
49
37
  }
50
38
  catch (error) {
51
- return handleError(error);
39
+ return handleErrorWithContext(error, notion, {
40
+ exampleType: 'richTextArray',
41
+ });
52
42
  }
53
43
  });
54
44
  }
@@ -1 +1 @@
1
- {"version":3,"file":"update-page.d.ts","sourceRoot":"","sources":["../../../src/tools/update-page.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAkBvD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAkDhF"}
1
+ {"version":3,"file":"update-page.d.ts","sourceRoot":"","sources":["../../../src/tools/update-page.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAoBvD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAsDhF"}
@@ -1,13 +1,14 @@
1
1
  import { z } from 'zod';
2
- import { formatResponse, handleError } from '../utils/index.js';
2
+ import { F } from '../schemas/descriptions/index.js';
3
+ import { formatResponse, handleErrorWithContext } from '../utils/index.js';
3
4
  // Minimal schema for MCP (full validation by Notion API)
4
5
  const inputSchema = {
5
- page_id: z.string().describe('Page ID'),
6
- properties: z.record(z.string(), z.any()).optional().describe('Properties to update'),
7
- archived: z.boolean().optional().describe('Archive the page'),
8
- icon: z.any().optional().describe('Page icon { type: "emoji", emoji: "📝" } or { type: "external", external: { url: "..." } }, or null to remove. Emoji must be an actual emoji character.'),
9
- cover: z.any().optional().describe('Cover image (null to remove)'),
10
- is_locked: z.boolean().optional().describe('Lock the page to prevent edits in the UI. Set to true to lock, false to unlock.'),
6
+ page_id: z.string().describe(F.page_id),
7
+ properties: z.record(z.string(), z.any()).optional().describe(F.properties),
8
+ archived: z.boolean().optional().describe(F.archived),
9
+ icon: z.any().optional().describe(F.icon),
10
+ cover: z.any().optional().describe(F.cover),
11
+ is_locked: z.boolean().optional().describe(F.is_locked),
11
12
  };
12
13
  export function registerUpdatePage(server, notion) {
13
14
  server.registerTool('update-page', {
@@ -39,7 +40,11 @@ export function registerUpdatePage(server, notion) {
39
40
  return formatResponse(response);
40
41
  }
41
42
  catch (error) {
42
- return handleError(error);
43
+ // Note: update-page uses page_id, not data_source_id,
44
+ // so we can't fetch property list, but we can still show format examples
45
+ return handleErrorWithContext(error, notion, {
46
+ exampleType: 'page',
47
+ });
43
48
  }
44
49
  });
45
50
  }
@@ -1,3 +1,5 @@
1
+ import { type NotionClient } from '../notion-client.js';
2
+ import { type ExampleType } from '../schemas/descriptions/index.js';
1
3
  export interface McpTextContent {
2
4
  type: 'text';
3
5
  text: string;
@@ -8,4 +10,22 @@ export interface McpResponse {
8
10
  isError?: boolean;
9
11
  }
10
12
  export declare function handleError(error: unknown): McpResponse;
13
+ export interface HandleErrorOptions {
14
+ /** Data source ID to fetch schema for property list */
15
+ dataSourceId?: string;
16
+ /** Type of examples to show */
17
+ exampleType?: ExampleType;
18
+ /** Additional hint to append (e.g., usage examples) */
19
+ hint?: string;
20
+ }
21
+ /**
22
+ * Enhanced error handler that includes contextual help for validation errors.
23
+ *
24
+ * Usage:
25
+ * - Page tools: handleErrorWithContext(error, notion, { dataSourceId, exampleType: 'page' })
26
+ * - Schema tools: handleErrorWithContext(error, notion, { dataSourceId, exampleType: 'schema' })
27
+ * - Block tools: handleErrorWithContext(error, notion, { exampleType: 'block' })
28
+ * - Filter tools: handleErrorWithContext(error, notion, { exampleType: 'filter' })
29
+ */
30
+ export declare function handleErrorWithContext(error: unknown, notion: NotionClient, options?: HandleErrorOptions): Promise<McpResponse>;
11
31
  //# sourceMappingURL=error-handler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../../src/utils/error-handler.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,WAAW;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;IACtB,OAAO,EAAE,cAAc,EAAE,CAAA;IACzB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAaD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAoBvD"}
1
+ {"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../../src/utils/error-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACzE,OAAO,EACL,KAAK,WAAW,EAIjB,MAAM,kCAAkC,CAAA;AAOzC,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,WAAW;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;IACtB,OAAO,EAAE,cAAc,EAAE,CAAA;IACzB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAaD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAoBvD;AAuED,MAAM,WAAW,kBAAkB;IACjC,uDAAuD;IACvD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,+BAA+B;IAC/B,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,YAAY,EACpB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,WAAW,CAAC,CAsCtB"}
@@ -1,3 +1,5 @@
1
+ import { isFullDataSource } from '../notion-client.js';
2
+ import { getExamplesByType, PagePropertyExamples, SchemaPropertyExamples, } from '../schemas/descriptions/index.js';
1
3
  function isNotionApiError(error) {
2
4
  if (!(error instanceof Error))
3
5
  return false;
@@ -62,3 +64,64 @@ function formatNotionError(error) {
62
64
  return error.message;
63
65
  }
64
66
  }
67
+ function isValidationError(error) {
68
+ if (!(error instanceof Error))
69
+ return false;
70
+ return error.message.includes('validation_error');
71
+ }
72
+ function formatPropertyList(properties, exampleType) {
73
+ const propList = Object.entries(properties)
74
+ .map(([name, prop]) => ` - ${name} (${prop.type})`)
75
+ .join('\n');
76
+ // Get unique property types and their examples based on exampleType
77
+ const examples = exampleType === 'page' ? PagePropertyExamples : SchemaPropertyExamples;
78
+ const uniqueTypes = [...new Set(Object.values(properties).map((p) => p.type))];
79
+ const exampleLines = uniqueTypes
80
+ .filter((type) => examples[type])
81
+ .map((type) => ` ${type}: ${examples[type]}`)
82
+ .join('\n');
83
+ if (exampleLines) {
84
+ const label = exampleType === 'page' ? 'Page property' : 'Schema property';
85
+ return `${propList}\n\n${label} format examples:\n${exampleLines}`;
86
+ }
87
+ return propList;
88
+ }
89
+ /**
90
+ * Enhanced error handler that includes contextual help for validation errors.
91
+ *
92
+ * Usage:
93
+ * - Page tools: handleErrorWithContext(error, notion, { dataSourceId, exampleType: 'page' })
94
+ * - Schema tools: handleErrorWithContext(error, notion, { dataSourceId, exampleType: 'schema' })
95
+ * - Block tools: handleErrorWithContext(error, notion, { exampleType: 'block' })
96
+ * - Filter tools: handleErrorWithContext(error, notion, { exampleType: 'filter' })
97
+ */
98
+ export async function handleErrorWithContext(error, notion, options) {
99
+ const baseResponse = handleError(error);
100
+ if (!isValidationError(error)) {
101
+ return baseResponse;
102
+ }
103
+ // For data source related errors, fetch and show property list
104
+ if (options?.dataSourceId &&
105
+ (options.exampleType === 'page' || options.exampleType === 'schema')) {
106
+ try {
107
+ const schema = await notion.dataSources.retrieve({ data_source_id: options.dataSourceId });
108
+ if (isFullDataSource(schema)) {
109
+ const propList = formatPropertyList(schema.properties, options.exampleType);
110
+ baseResponse.content[0].text += `\n\nAvailable properties:\n${propList}`;
111
+ }
112
+ }
113
+ catch {
114
+ // Ignore schema fetch errors - still show examples below
115
+ }
116
+ }
117
+ // For other types (block, richTextArray, filter), show examples directly
118
+ if (options?.exampleType && options.exampleType !== 'page' && options.exampleType !== 'schema') {
119
+ const examples = getExamplesByType(options.exampleType);
120
+ baseResponse.content[0].text += `\n\n${examples}`;
121
+ }
122
+ // Add optional hint
123
+ if (options?.hint) {
124
+ baseResponse.content[0].text += `\n\n${options.hint}`;
125
+ }
126
+ return baseResponse;
127
+ }