@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.
- package/LICENSE +21 -0
- package/README.ja.md +203 -0
- package/README.md +203 -0
- package/dist/bin/cli.d.ts +3 -0
- package/dist/bin/cli.d.ts.map +1 -0
- package/dist/bin/cli.js +25 -0
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +9 -0
- package/dist/src/notion-client.d.ts +82 -0
- package/dist/src/notion-client.d.ts.map +1 -0
- package/dist/src/notion-client.js +93 -0
- package/dist/src/schemas/block.d.ts +2250 -0
- package/dist/src/schemas/block.d.ts.map +1 -0
- package/dist/src/schemas/block.js +195 -0
- package/dist/src/schemas/common.d.ts +126 -0
- package/dist/src/schemas/common.d.ts.map +1 -0
- package/dist/src/schemas/common.js +85 -0
- package/dist/src/schemas/filter.d.ts +105 -0
- package/dist/src/schemas/filter.d.ts.map +1 -0
- package/dist/src/schemas/filter.js +125 -0
- package/dist/src/schemas/index.d.ts +5 -0
- package/dist/src/schemas/index.d.ts.map +1 -0
- package/dist/src/schemas/index.js +8 -0
- package/dist/src/schemas/page.d.ts +474 -0
- package/dist/src/schemas/page.d.ts.map +1 -0
- package/dist/src/schemas/page.js +134 -0
- package/dist/src/server.d.ts +5 -0
- package/dist/src/server.d.ts.map +1 -0
- package/dist/src/server.js +16 -0
- package/dist/src/tools/append-block-children.d.ts +4 -0
- package/dist/src/tools/append-block-children.d.ts.map +1 -0
- package/dist/src/tools/append-block-children.js +32 -0
- package/dist/src/tools/create-comment.d.ts +4 -0
- package/dist/src/tools/create-comment.d.ts.map +1 -0
- package/dist/src/tools/create-comment.js +35 -0
- package/dist/src/tools/create-page.d.ts +4 -0
- package/dist/src/tools/create-page.d.ts.map +1 -0
- package/dist/src/tools/create-page.js +50 -0
- package/dist/src/tools/get-block-children.d.ts +4 -0
- package/dist/src/tools/get-block-children.d.ts.map +1 -0
- package/dist/src/tools/get-block-children.js +30 -0
- package/dist/src/tools/index.d.ts +13 -0
- package/dist/src/tools/index.d.ts.map +1 -0
- package/dist/src/tools/index.js +24 -0
- package/dist/src/tools/query-database.d.ts +4 -0
- package/dist/src/tools/query-database.d.ts.map +1 -0
- package/dist/src/tools/query-database.js +51 -0
- package/dist/src/tools/retrieve-page.d.ts +4 -0
- package/dist/src/tools/retrieve-page.d.ts.map +1 -0
- package/dist/src/tools/retrieve-page.js +16 -0
- package/dist/src/tools/search.d.ts +4 -0
- package/dist/src/tools/search.d.ts.map +1 -0
- package/dist/src/tools/search.js +58 -0
- package/dist/src/tools/update-page.d.ts +4 -0
- package/dist/src/tools/update-page.d.ts.map +1 -0
- package/dist/src/tools/update-page.js +47 -0
- package/dist/src/utils/error-handler.d.ts +11 -0
- package/dist/src/utils/error-handler.d.ts.map +1 -0
- package/dist/src/utils/error-handler.js +64 -0
- package/dist/src/utils/index.d.ts +3 -0
- package/dist/src/utils/index.d.ts.map +1 -0
- package/dist/src/utils/index.js +2 -0
- package/dist/src/utils/response-formatter.d.ts +5 -0
- package/dist/src/utils/response-formatter.d.ts.map +1 -0
- package/dist/src/utils/response-formatter.js +30 -0
- 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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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,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
|
+
}
|