@atikk-co-jp/notion-mcp-server 0.1.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 (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.ja.md +203 -0
  3. package/README.md +203 -0
  4. package/dist/bin/cli.d.ts +3 -0
  5. package/dist/bin/cli.d.ts.map +1 -0
  6. package/dist/bin/cli.js +25 -0
  7. package/dist/src/index.d.ts +7 -0
  8. package/dist/src/index.d.ts.map +1 -0
  9. package/dist/src/index.js +9 -0
  10. package/dist/src/notion-client.d.ts +82 -0
  11. package/dist/src/notion-client.d.ts.map +1 -0
  12. package/dist/src/notion-client.js +93 -0
  13. package/dist/src/schemas/block.d.ts +2250 -0
  14. package/dist/src/schemas/block.d.ts.map +1 -0
  15. package/dist/src/schemas/block.js +195 -0
  16. package/dist/src/schemas/common.d.ts +126 -0
  17. package/dist/src/schemas/common.d.ts.map +1 -0
  18. package/dist/src/schemas/common.js +85 -0
  19. package/dist/src/schemas/filter.d.ts +105 -0
  20. package/dist/src/schemas/filter.d.ts.map +1 -0
  21. package/dist/src/schemas/filter.js +125 -0
  22. package/dist/src/schemas/index.d.ts +5 -0
  23. package/dist/src/schemas/index.d.ts.map +1 -0
  24. package/dist/src/schemas/index.js +8 -0
  25. package/dist/src/schemas/page.d.ts +474 -0
  26. package/dist/src/schemas/page.d.ts.map +1 -0
  27. package/dist/src/schemas/page.js +134 -0
  28. package/dist/src/server.d.ts +5 -0
  29. package/dist/src/server.d.ts.map +1 -0
  30. package/dist/src/server.js +16 -0
  31. package/dist/src/tools/append-block-children.d.ts +4 -0
  32. package/dist/src/tools/append-block-children.d.ts.map +1 -0
  33. package/dist/src/tools/append-block-children.js +32 -0
  34. package/dist/src/tools/create-comment.d.ts +4 -0
  35. package/dist/src/tools/create-comment.d.ts.map +1 -0
  36. package/dist/src/tools/create-comment.js +35 -0
  37. package/dist/src/tools/create-page.d.ts +4 -0
  38. package/dist/src/tools/create-page.d.ts.map +1 -0
  39. package/dist/src/tools/create-page.js +50 -0
  40. package/dist/src/tools/get-block-children.d.ts +4 -0
  41. package/dist/src/tools/get-block-children.d.ts.map +1 -0
  42. package/dist/src/tools/get-block-children.js +30 -0
  43. package/dist/src/tools/index.d.ts +13 -0
  44. package/dist/src/tools/index.d.ts.map +1 -0
  45. package/dist/src/tools/index.js +24 -0
  46. package/dist/src/tools/query-database.d.ts +4 -0
  47. package/dist/src/tools/query-database.d.ts.map +1 -0
  48. package/dist/src/tools/query-database.js +51 -0
  49. package/dist/src/tools/retrieve-page.d.ts +4 -0
  50. package/dist/src/tools/retrieve-page.d.ts.map +1 -0
  51. package/dist/src/tools/retrieve-page.js +16 -0
  52. package/dist/src/tools/search.d.ts +4 -0
  53. package/dist/src/tools/search.d.ts.map +1 -0
  54. package/dist/src/tools/search.js +58 -0
  55. package/dist/src/tools/update-page.d.ts +4 -0
  56. package/dist/src/tools/update-page.d.ts.map +1 -0
  57. package/dist/src/tools/update-page.js +47 -0
  58. package/dist/src/utils/error-handler.d.ts +11 -0
  59. package/dist/src/utils/error-handler.d.ts.map +1 -0
  60. package/dist/src/utils/error-handler.js +64 -0
  61. package/dist/src/utils/index.d.ts +3 -0
  62. package/dist/src/utils/index.d.ts.map +1 -0
  63. package/dist/src/utils/index.js +2 -0
  64. package/dist/src/utils/response-formatter.d.ts +5 -0
  65. package/dist/src/utils/response-formatter.d.ts.map +1 -0
  66. package/dist/src/utils/response-formatter.js +30 -0
  67. package/package.json +58 -0
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { NotionClient } from "../notion-client.js";
3
+ export declare function registerCreateComment(server: McpServer, notion: NotionClient): void;
4
+ //# sourceMappingURL=create-comment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-comment.d.ts","sourceRoot":"","sources":["../../../src/tools/create-comment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAoBxD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CA+BnF"}
@@ -0,0 +1,35 @@
1
+ import { z } from "zod";
2
+ import { formatResponse, handleError } from "../utils/index.js";
3
+ const inputSchema = {
4
+ page_id: z.string().describe("The ID of the page to add a comment to"),
5
+ rich_text: z
6
+ .array(z.any())
7
+ .describe("The comment content as rich text. " +
8
+ 'Example: [{ "type": "text", "text": { "content": "This is a comment" } }]'),
9
+ discussion_id: z
10
+ .string()
11
+ .optional()
12
+ .describe("Optional ID of an existing discussion to add the comment to. " +
13
+ "If not provided, a new discussion is created."),
14
+ };
15
+ export function registerCreateComment(server, notion) {
16
+ server.tool("create-comment", "Add a comment to a Notion page. Creates a new discussion or adds to an existing one.", inputSchema, async ({ page_id, rich_text, discussion_id }) => {
17
+ try {
18
+ // The Notion API requires either parent or discussion_id
19
+ const params = {
20
+ rich_text,
21
+ };
22
+ if (discussion_id) {
23
+ params.discussion_id = discussion_id;
24
+ }
25
+ else {
26
+ params.parent = { page_id };
27
+ }
28
+ const response = await notion.comments.create(params);
29
+ return formatResponse(response);
30
+ }
31
+ catch (error) {
32
+ return handleError(error);
33
+ }
34
+ });
35
+ }
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { NotionClient } from "../notion-client.js";
3
+ export declare function registerCreatePage(server: McpServer, notion: NotionClient): void;
4
+ //# sourceMappingURL=create-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-page.d.ts","sourceRoot":"","sources":["../../../src/tools/create-page.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAoCxD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAqChF"}
@@ -0,0 +1,50 @@
1
+ import { z } from "zod";
2
+ import { formatResponse, handleError } from "../utils/index.js";
3
+ const inputSchema = {
4
+ database_id: z.string().describe("The ID of the database where the page will be created"),
5
+ properties: z
6
+ .record(z.string(), z.any())
7
+ .describe("Page properties as a JSON object. Keys are property names, values are property values. " +
8
+ 'Example: { "Name": { "title": [{ "text": { "content": "My Page" } }] }, ' +
9
+ '"Status": { "status": { "name": "In Progress" } } }'),
10
+ children: z
11
+ .array(z.any())
12
+ .optional()
13
+ .describe("Optional array of block objects for the page content. " +
14
+ 'Example: [{ "type": "paragraph", "paragraph": { "rich_text": [{ "text": { "content": "Hello" } }] } }]'),
15
+ icon: z
16
+ .any()
17
+ .optional()
18
+ .describe("Optional icon for the page. " +
19
+ 'Emoji example: { "type": "emoji", "emoji": "🚀" }. ' +
20
+ 'External example: { "type": "external", "external": { "url": "https://..." } }'),
21
+ cover: z
22
+ .any()
23
+ .optional()
24
+ .describe("Optional cover image for the page. " +
25
+ 'Example: { "type": "external", "external": { "url": "https://..." } }'),
26
+ };
27
+ export function registerCreatePage(server, notion) {
28
+ server.tool("create-page", "Create a new page in a Notion database. Requires a database_id and properties object.", inputSchema, async ({ database_id, properties, children, icon, cover }) => {
29
+ try {
30
+ const params = {
31
+ parent: { database_id },
32
+ properties,
33
+ };
34
+ if (children) {
35
+ params.children = children;
36
+ }
37
+ if (icon) {
38
+ params.icon = icon;
39
+ }
40
+ if (cover) {
41
+ params.cover = cover;
42
+ }
43
+ const response = await notion.pages.create(params);
44
+ return formatResponse(response);
45
+ }
46
+ catch (error) {
47
+ return handleError(error);
48
+ }
49
+ });
50
+ }
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { NotionClient } from "../notion-client.js";
3
+ export declare function registerGetBlockChildren(server: McpServer, notion: NotionClient): void;
4
+ //# sourceMappingURL=get-block-children.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-block-children.d.ts","sourceRoot":"","sources":["../../../src/tools/get-block-children.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAuBxD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAkBtF"}
@@ -0,0 +1,30 @@
1
+ import { z } from "zod";
2
+ import { formatPaginatedResponse, handleError } from "../utils/index.js";
3
+ const inputSchema = {
4
+ block_id: z.string().describe("The ID of the block or page to get children from"),
5
+ start_cursor: z
6
+ .string()
7
+ .optional()
8
+ .describe("Cursor for pagination. Use the next_cursor from previous response."),
9
+ page_size: z
10
+ .number()
11
+ .min(1)
12
+ .max(100)
13
+ .optional()
14
+ .describe("Number of results to return (1-100). Default is 100."),
15
+ };
16
+ export function registerGetBlockChildren(server, notion) {
17
+ server.tool("get-block-children", "Retrieve the children blocks of a block or page. Returns paginated results.", inputSchema, async ({ block_id, start_cursor, page_size }) => {
18
+ try {
19
+ const response = await notion.blocks.children.list({
20
+ block_id,
21
+ start_cursor,
22
+ page_size,
23
+ });
24
+ return formatPaginatedResponse(response.results, response.has_more, response.next_cursor);
25
+ }
26
+ catch (error) {
27
+ return handleError(error);
28
+ }
29
+ });
30
+ }
@@ -0,0 +1,13 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { NotionClient } from "../notion-client.js";
3
+ import { registerAppendBlockChildren } from "./append-block-children.js";
4
+ import { registerCreateComment } from "./create-comment.js";
5
+ import { registerCreatePage } from "./create-page.js";
6
+ import { registerGetBlockChildren } from "./get-block-children.js";
7
+ import { registerQueryDatabase } from "./query-database.js";
8
+ import { registerRetrievePage } from "./retrieve-page.js";
9
+ import { registerSearch } from "./search.js";
10
+ import { registerUpdatePage } from "./update-page.js";
11
+ export declare function registerAllTools(server: McpServer, notion: NotionClient): void;
12
+ export { registerRetrievePage, registerCreatePage, registerUpdatePage, registerQueryDatabase, registerSearch, registerGetBlockChildren, registerAppendBlockChildren, registerCreateComment, };
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAkB9E;AAED,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,cAAc,EACd,wBAAwB,EACxB,2BAA2B,EAC3B,qBAAqB,GACtB,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { registerAppendBlockChildren } from "./append-block-children.js";
2
+ import { registerCreateComment } from "./create-comment.js";
3
+ import { registerCreatePage } from "./create-page.js";
4
+ import { registerGetBlockChildren } from "./get-block-children.js";
5
+ import { registerQueryDatabase } from "./query-database.js";
6
+ import { registerRetrievePage } from "./retrieve-page.js";
7
+ import { registerSearch } from "./search.js";
8
+ import { registerUpdatePage } from "./update-page.js";
9
+ export function registerAllTools(server, notion) {
10
+ // Page operations
11
+ registerRetrievePage(server, notion);
12
+ registerCreatePage(server, notion);
13
+ registerUpdatePage(server, notion);
14
+ // Database operations
15
+ registerQueryDatabase(server, notion);
16
+ // Search
17
+ registerSearch(server, notion);
18
+ // Block operations
19
+ registerGetBlockChildren(server, notion);
20
+ registerAppendBlockChildren(server, notion);
21
+ // Comment operations
22
+ registerCreateComment(server, notion);
23
+ }
24
+ export { registerRetrievePage, registerCreatePage, registerUpdatePage, registerQueryDatabase, registerSearch, registerGetBlockChildren, registerAppendBlockChildren, registerCreateComment, };
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { NotionClient } from "../notion-client.js";
3
+ export declare function registerQueryDatabase(server: McpServer, notion: NotionClient): void;
4
+ //# sourceMappingURL=query-database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-database.d.ts","sourceRoot":"","sources":["../../../src/tools/query-database.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAuCxD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAsCnF"}
@@ -0,0 +1,51 @@
1
+ import { z } from "zod";
2
+ import { formatPaginatedResponse, handleError } from "../utils/index.js";
3
+ const inputSchema = {
4
+ database_id: z.string().describe("The ID of the database to query"),
5
+ filter: z
6
+ .any()
7
+ .optional()
8
+ .describe("Filter conditions as a JSON object. " +
9
+ 'Property filter example: { "property": "Status", "status": { "equals": "Done" } }. ' +
10
+ 'Compound filter example: { "and": [{ "property": "Status", "status": { "equals": "Done" } }] }'),
11
+ sorts: z
12
+ .array(z.any())
13
+ .optional()
14
+ .describe("Sort conditions as an array. " +
15
+ 'Example: [{ "property": "Created", "direction": "descending" }] or ' +
16
+ '[{ "timestamp": "created_time", "direction": "ascending" }]'),
17
+ start_cursor: z
18
+ .string()
19
+ .optional()
20
+ .describe("Cursor for pagination. Use the next_cursor from previous response."),
21
+ page_size: z
22
+ .number()
23
+ .min(1)
24
+ .max(100)
25
+ .optional()
26
+ .describe("Number of results to return (1-100). Default is 100."),
27
+ };
28
+ export function registerQueryDatabase(server, notion) {
29
+ server.tool("query-database", "Query a Notion database with optional filters and sorts. Returns paginated results.", inputSchema, async ({ database_id, filter, sorts, start_cursor, page_size }) => {
30
+ try {
31
+ const params = { database_id };
32
+ if (filter) {
33
+ params.filter = filter;
34
+ }
35
+ if (sorts) {
36
+ params.sorts = sorts;
37
+ }
38
+ if (start_cursor) {
39
+ params.start_cursor = start_cursor;
40
+ }
41
+ if (page_size) {
42
+ params.page_size = page_size;
43
+ }
44
+ const response = await notion.databases.query(params);
45
+ return formatPaginatedResponse(response.results, response.has_more, response.next_cursor);
46
+ }
47
+ catch (error) {
48
+ return handleError(error);
49
+ }
50
+ });
51
+ }
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { NotionClient } from "../notion-client.js";
3
+ export declare function registerRetrievePage(server: McpServer, notion: NotionClient): void;
4
+ //# sourceMappingURL=retrieve-page.d.ts.map
@@ -0,0 +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,CAAC;AAEzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAOxD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAclF"}
@@ -0,0 +1,16 @@
1
+ import { z } from "zod";
2
+ import { formatResponse, handleError } from "../utils/index.js";
3
+ const inputSchema = {
4
+ page_id: z.string().describe("The ID of the page to retrieve"),
5
+ };
6
+ export function registerRetrievePage(server, notion) {
7
+ server.tool("retrieve-page", "Retrieve a Notion page by its ID. Returns the page properties and metadata.", inputSchema, async ({ page_id }) => {
8
+ try {
9
+ const response = await notion.pages.retrieve({ page_id });
10
+ return formatResponse(response);
11
+ }
12
+ catch (error) {
13
+ return handleError(error);
14
+ }
15
+ });
16
+ }
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { NotionClient } from "../notion-client.js";
3
+ export declare function registerSearch(server: McpServer, notion: NotionClient): void;
4
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/tools/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AA2CxD,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CA0C5E"}
@@ -0,0 +1,58 @@
1
+ import { z } from "zod";
2
+ import { formatPaginatedResponse, handleError } from "../utils/index.js";
3
+ const inputSchema = {
4
+ query: z.string().optional().describe("Text to search for in page titles and content"),
5
+ filter: z
6
+ .object({
7
+ value: z.enum(["page", "database"]),
8
+ property: z.literal("object"),
9
+ })
10
+ .optional()
11
+ .describe("Filter to limit results to pages or databases. " +
12
+ 'Example: { "value": "page", "property": "object" }'),
13
+ sort: z
14
+ .object({
15
+ direction: z.enum(["ascending", "descending"]),
16
+ timestamp: z.literal("last_edited_time"),
17
+ })
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."),
31
+ };
32
+ export function registerSearch(server, notion) {
33
+ server.tool("search", "Search across all pages and databases in the workspace. Returns paginated results.", inputSchema, async ({ query, filter, sort, start_cursor, page_size }) => {
34
+ try {
35
+ const params = {};
36
+ if (query) {
37
+ params.query = query;
38
+ }
39
+ if (filter) {
40
+ params.filter = filter;
41
+ }
42
+ if (sort) {
43
+ params.sort = sort;
44
+ }
45
+ if (start_cursor) {
46
+ params.start_cursor = start_cursor;
47
+ }
48
+ if (page_size) {
49
+ params.page_size = page_size;
50
+ }
51
+ const response = await notion.search(params);
52
+ return formatPaginatedResponse(response.results, response.has_more, response.next_cursor);
53
+ }
54
+ catch (error) {
55
+ return handleError(error);
56
+ }
57
+ });
58
+ }
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { NotionClient } from "../notion-client.js";
3
+ export declare function registerUpdatePage(server: McpServer, notion: NotionClient): void;
4
+ //# sourceMappingURL=update-page.d.ts.map
@@ -0,0 +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,CAAC;AAEzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AA+BxD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAsChF"}
@@ -0,0 +1,47 @@
1
+ import { z } from "zod";
2
+ import { formatResponse, handleError } from "../utils/index.js";
3
+ const inputSchema = {
4
+ page_id: z.string().describe("The ID of the page to update"),
5
+ properties: z
6
+ .record(z.string(), z.any())
7
+ .optional()
8
+ .describe("Properties to update as a JSON object. Keys are property names, values are property values. " +
9
+ 'Example: { "Status": { "status": { "name": "Done" } } }'),
10
+ archived: z.boolean().optional().describe("Set to true to archive (delete) the page"),
11
+ icon: z
12
+ .any()
13
+ .optional()
14
+ .describe("Optional icon for the page. " +
15
+ 'Emoji example: { "type": "emoji", "emoji": "🚀" }. ' +
16
+ "Set to null to remove the icon."),
17
+ cover: z
18
+ .any()
19
+ .optional()
20
+ .describe("Optional cover image for the page. " +
21
+ 'Example: { "type": "external", "external": { "url": "https://..." } }. ' +
22
+ "Set to null to remove the cover."),
23
+ };
24
+ export function registerUpdatePage(server, notion) {
25
+ server.tool("update-page", "Update a Notion page's properties, icon, cover, or archive status.", inputSchema, async ({ page_id, properties, archived, icon, cover }) => {
26
+ try {
27
+ const params = { page_id };
28
+ if (properties !== undefined) {
29
+ params.properties = properties;
30
+ }
31
+ if (archived !== undefined) {
32
+ params.archived = archived;
33
+ }
34
+ if (icon !== undefined) {
35
+ params.icon = icon;
36
+ }
37
+ if (cover !== undefined) {
38
+ params.cover = cover;
39
+ }
40
+ const response = await notion.pages.update(params);
41
+ return formatResponse(response);
42
+ }
43
+ catch (error) {
44
+ return handleError(error);
45
+ }
46
+ });
47
+ }
@@ -0,0 +1,11 @@
1
+ export interface McpTextContent {
2
+ type: "text";
3
+ text: string;
4
+ }
5
+ export interface McpResponse {
6
+ [key: string]: unknown;
7
+ content: McpTextContent[];
8
+ isError?: boolean;
9
+ }
10
+ export declare function handleError(error: unknown): McpResponse;
11
+ //# sourceMappingURL=error-handler.d.ts.map
@@ -0,0 +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,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAaD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAoBvD"}
@@ -0,0 +1,64 @@
1
+ function isNotionApiError(error) {
2
+ if (!(error instanceof Error))
3
+ return false;
4
+ const errObj = error;
5
+ return typeof errObj.code === "string" && typeof errObj.message === "string";
6
+ }
7
+ export function handleError(error) {
8
+ if (isNotionApiError(error)) {
9
+ const message = formatNotionError(error);
10
+ return {
11
+ content: [{ type: "text", text: `Notion API Error: ${message}` }],
12
+ isError: true,
13
+ };
14
+ }
15
+ if (error instanceof Error) {
16
+ return {
17
+ content: [{ type: "text", text: `Error: ${error.message}` }],
18
+ isError: true,
19
+ };
20
+ }
21
+ return {
22
+ content: [{ type: "text", text: `Unknown error: ${String(error)}` }],
23
+ isError: true,
24
+ };
25
+ }
26
+ // Notion API error codes
27
+ const NOTION_ERROR_CODES = {
28
+ object_not_found: "object_not_found",
29
+ unauthorized: "unauthorized",
30
+ rate_limited: "rate_limited",
31
+ invalid_json: "invalid_json",
32
+ invalid_request_url: "invalid_request_url",
33
+ invalid_request: "invalid_request",
34
+ validation_error: "validation_error",
35
+ conflict_error: "conflict_error",
36
+ internal_server_error: "internal_server_error",
37
+ service_unavailable: "service_unavailable",
38
+ };
39
+ function formatNotionError(error) {
40
+ switch (error.code) {
41
+ case NOTION_ERROR_CODES.object_not_found:
42
+ return `Object not found. Please check the ID is correct and the integration has access. Details: ${error.message}`;
43
+ case NOTION_ERROR_CODES.unauthorized:
44
+ return `Unauthorized. Please check your NOTION_TOKEN is valid and has the required permissions. Details: ${error.message}`;
45
+ case NOTION_ERROR_CODES.rate_limited:
46
+ return `Rate limited. Please wait and retry. Details: ${error.message}`;
47
+ case NOTION_ERROR_CODES.invalid_json:
48
+ return `Invalid JSON in request. Details: ${error.message}`;
49
+ case NOTION_ERROR_CODES.invalid_request_url:
50
+ return `Invalid request URL. Details: ${error.message}`;
51
+ case NOTION_ERROR_CODES.invalid_request:
52
+ return `Invalid request. Details: ${error.message}`;
53
+ case NOTION_ERROR_CODES.validation_error:
54
+ return `Validation error: ${error.message}`;
55
+ case NOTION_ERROR_CODES.conflict_error:
56
+ return `Conflict error. The resource may have been modified. Details: ${error.message}`;
57
+ case NOTION_ERROR_CODES.internal_server_error:
58
+ return `Notion internal server error. Please retry later. Details: ${error.message}`;
59
+ case NOTION_ERROR_CODES.service_unavailable:
60
+ return `Notion service unavailable. Please retry later. Details: ${error.message}`;
61
+ default:
62
+ return error.message;
63
+ }
64
+ }
@@ -0,0 +1,3 @@
1
+ export { handleError, type McpResponse, type McpTextContent } from "./error-handler.js";
2
+ export { formatPaginatedResponse, formatResponse, formatSuccessMessage, } from "./response-formatter.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACxF,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,oBAAoB,GACrB,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { handleError } from "./error-handler.js";
2
+ export { formatPaginatedResponse, formatResponse, formatSuccessMessage, } from "./response-formatter.js";
@@ -0,0 +1,5 @@
1
+ import type { McpResponse } from "./error-handler.js";
2
+ export declare function formatResponse(data: unknown): McpResponse;
3
+ export declare function formatSuccessMessage(message: string): McpResponse;
4
+ export declare function formatPaginatedResponse(data: unknown, hasMore: boolean, nextCursor: string | null): McpResponse;
5
+ //# sourceMappingURL=response-formatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-formatter.d.ts","sourceRoot":"","sources":["../../../src/utils/response-formatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,WAAW,CASzD;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAIjE;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,WAAW,CAcb"}
@@ -0,0 +1,30 @@
1
+ export function formatResponse(data) {
2
+ return {
3
+ content: [
4
+ {
5
+ type: "text",
6
+ text: JSON.stringify(data, null, 2),
7
+ },
8
+ ],
9
+ };
10
+ }
11
+ export function formatSuccessMessage(message) {
12
+ return {
13
+ content: [{ type: "text", text: message }],
14
+ };
15
+ }
16
+ export function formatPaginatedResponse(data, hasMore, nextCursor) {
17
+ const result = {
18
+ data,
19
+ has_more: hasMore,
20
+ next_cursor: nextCursor,
21
+ };
22
+ return {
23
+ content: [
24
+ {
25
+ type: "text",
26
+ text: JSON.stringify(result, null, 2),
27
+ },
28
+ ],
29
+ };
30
+ }
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@atikk-co-jp/notion-mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for Notion API - Create, read, update pages and databases",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "atikk-co-jp",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/atikk-co-jp/notion-mcp-server.git"
11
+ },
12
+ "homepage": "https://github.com/atikk-co-jp/notion-mcp-server#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/atikk-co-jp/notion-mcp-server/issues"
15
+ },
16
+ "keywords": [
17
+ "mcp",
18
+ "notion",
19
+ "api",
20
+ "model-context-protocol"
21
+ ],
22
+ "bin": {
23
+ "atikk-notion-mcp-server": "./dist/bin/cli.js"
24
+ },
25
+ "main": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "default": "./dist/index.js"
31
+ }
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "README.md",
36
+ "README.ja.md",
37
+ "LICENSE"
38
+ ],
39
+ "scripts": {
40
+ "build": "tsc",
41
+ "typecheck": "tsc --noEmit",
42
+ "dev": "tsx watch bin/cli.ts",
43
+ "start": "tsx bin/cli.ts",
44
+ "prepublishOnly": "pnpm build"
45
+ },
46
+ "dependencies": {
47
+ "@modelcontextprotocol/sdk": "^1.25.1",
48
+ "zod": "^4.3.5"
49
+ },
50
+ "devDependencies": {
51
+ "@types/node": "^22.0.0",
52
+ "tsx": "^4.19.0",
53
+ "typescript": "5.9.3"
54
+ },
55
+ "engines": {
56
+ "node": ">=18"
57
+ }
58
+ }