notion-mcp-server 2.8.0 → 2.10.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.
|
@@ -53,6 +53,34 @@ register({
|
|
|
53
53
|
return { ok: true, data: slimDataSource(ds, verbose ?? false) };
|
|
54
54
|
}),
|
|
55
55
|
});
|
|
56
|
+
const ListDataSourceTemplatesParams = z.object({
|
|
57
|
+
data_source_id: z.string().describe("Data source ID to list templates for."),
|
|
58
|
+
name: z.string().optional().describe("Case-insensitive substring filter on template name."),
|
|
59
|
+
start_cursor: z.string().optional(),
|
|
60
|
+
page_size: z.number().int().min(1).max(100).optional(),
|
|
61
|
+
});
|
|
62
|
+
register({
|
|
63
|
+
name: "list_data_source_templates",
|
|
64
|
+
access: "read",
|
|
65
|
+
domain: "data_sources",
|
|
66
|
+
description: "List the page templates available for a data source. Returns {id, name, is_default} per template. Pass a returned id as template.template_id to create_page to apply it.",
|
|
67
|
+
batchable: false,
|
|
68
|
+
schema: ListDataSourceTemplatesParams,
|
|
69
|
+
example: { data_source_id: "<data-source-id>" },
|
|
70
|
+
handler: tryHandler(async ({ data_source_id, name, start_cursor, page_size }) => {
|
|
71
|
+
const notion = await getClient();
|
|
72
|
+
const res = await notion.dataSources.listTemplates({
|
|
73
|
+
data_source_id,
|
|
74
|
+
...(name !== undefined ? { name } : {}),
|
|
75
|
+
...(start_cursor !== undefined ? { start_cursor } : {}),
|
|
76
|
+
...(page_size !== undefined ? { page_size } : {}),
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
ok: true,
|
|
80
|
+
data: { data_source_id, templates: res.templates },
|
|
81
|
+
};
|
|
82
|
+
}),
|
|
83
|
+
});
|
|
56
84
|
const UpdateDataSourceParams = z.object({
|
|
57
85
|
data_source_id: z.string(),
|
|
58
86
|
title: z.array(z.unknown()).optional().describe("Rich text array for the data source title."),
|
|
@@ -47,12 +47,26 @@ const CreatePageParams = z
|
|
|
47
47
|
properties: z.record(z.string(), PROPERTY_VALUE_SCHEMA).optional(),
|
|
48
48
|
markdown: z.string().optional().describe("Page body as markdown. Parsed server-side."),
|
|
49
49
|
children: z.array(z.unknown()).optional().describe("Structured Notion blocks. Mutually exclusive with markdown."),
|
|
50
|
+
template: z
|
|
51
|
+
.object({
|
|
52
|
+
type: z.enum(["default", "none", "template_id"]).describe("`template_id` to apply a specific template, `default` for the data source's default template, `none` for no template."),
|
|
53
|
+
template_id: z.string().optional().describe("Required when type is 'template_id'. Discover IDs via list_data_source_templates."),
|
|
54
|
+
timezone: z.string().optional().describe("IANA tz (e.g. 'Asia/Hong_Kong') controlling @now/@today resolution inside the template."),
|
|
55
|
+
})
|
|
56
|
+
.optional()
|
|
57
|
+
.describe("Create the page from a Notion template. The API forbids `markdown`/`children` alongside a template; only data_source_id parents support templates."),
|
|
50
58
|
icon: ICON_SCHEMA.nullable().optional(),
|
|
51
59
|
cover: FILE_SCHEMA.nullable().optional(),
|
|
52
60
|
verbose: VERBOSE,
|
|
53
61
|
})
|
|
54
62
|
.refine((v) => !(v.markdown && v.children), {
|
|
55
63
|
message: "Pass either `markdown` or `children`, not both.",
|
|
64
|
+
})
|
|
65
|
+
.refine((v) => !(v.template && (v.markdown || v.children)), {
|
|
66
|
+
message: "Pass either a `template` or body content (`markdown`/`children`), not both — the Notion API rejects children alongside a template.",
|
|
67
|
+
})
|
|
68
|
+
.refine((v) => !(v.template?.type === "template_id" && !v.template.template_id), {
|
|
69
|
+
message: "template.template_id is required when template.type is 'template_id'.",
|
|
56
70
|
});
|
|
57
71
|
register({
|
|
58
72
|
name: "create_page",
|
|
@@ -100,14 +114,18 @@ register({
|
|
|
100
114
|
title: [{ type: "text", text: { content: params.title } }],
|
|
101
115
|
};
|
|
102
116
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
117
|
+
// A template and explicit body content are mutually exclusive (API rule).
|
|
118
|
+
const children = params.template
|
|
119
|
+
? undefined
|
|
120
|
+
: params.markdown
|
|
121
|
+
? parseMarkdownToBlocks(params.markdown)
|
|
122
|
+
: params.children;
|
|
106
123
|
const notion = await getClient();
|
|
107
124
|
const body = {
|
|
108
125
|
parent,
|
|
109
126
|
properties,
|
|
110
127
|
...(children && children.length ? { children } : {}),
|
|
128
|
+
...(params.template ? { template: params.template } : {}),
|
|
111
129
|
...(params.icon !== undefined ? { icon: params.icon } : {}),
|
|
112
130
|
...(params.cover !== undefined ? { cover: params.cover } : {}),
|
|
113
131
|
};
|
package/build/services/notion.js
CHANGED
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
import { Client } from "@notionhq/client";
|
|
2
|
+
import nodeFetch from "node-fetch";
|
|
3
|
+
import { HttpsProxyAgent } from "https-proxy-agent";
|
|
2
4
|
import { authProvider } from "./auth.js";
|
|
3
5
|
let cachedClient = null;
|
|
4
6
|
let cachedToken = null;
|
|
7
|
+
// Route the Notion SDK's HTTP calls through an HTTP(S) proxy when one is
|
|
8
|
+
// configured via the standard env vars. node-fetch is used (instead of global
|
|
9
|
+
// fetch) because it accepts a custom `agent`. When no proxy is set we still go
|
|
10
|
+
// through node-fetch so behavior is uniform.
|
|
11
|
+
const proxyFetch = (url, init) => {
|
|
12
|
+
const proxyURL = process.env.HTTPS_PROXY ||
|
|
13
|
+
process.env.https_proxy ||
|
|
14
|
+
process.env.HTTP_PROXY ||
|
|
15
|
+
process.env.http_proxy ||
|
|
16
|
+
null;
|
|
17
|
+
if (!proxyURL)
|
|
18
|
+
return nodeFetch(url, init);
|
|
19
|
+
return nodeFetch(url, { ...init, agent: new HttpsProxyAgent(proxyURL) });
|
|
20
|
+
};
|
|
5
21
|
export async function getClient() {
|
|
6
22
|
const token = await authProvider.getToken();
|
|
7
23
|
if (token !== cachedToken || cachedClient === null) {
|
|
8
24
|
const fresh = new Client({
|
|
9
25
|
auth: token,
|
|
10
26
|
notionVersion: "2026-03-11",
|
|
27
|
+
fetch: proxyFetch,
|
|
11
28
|
});
|
|
12
29
|
cachedClient = fresh;
|
|
13
30
|
cachedToken = token;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "notion-mcp-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"notion-mcp-server": "build/index.js"
|
|
@@ -43,6 +43,8 @@
|
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
45
45
|
"@notionhq/client": "^5.22.0",
|
|
46
|
+
"https-proxy-agent": "9.1.0",
|
|
47
|
+
"node-fetch": "3.3.2",
|
|
46
48
|
"remark-gfm": "^4.0.1",
|
|
47
49
|
"remark-parse": "^11.0.0",
|
|
48
50
|
"unified": "^11.0.5",
|
|
@@ -50,9 +52,9 @@
|
|
|
50
52
|
},
|
|
51
53
|
"devDependencies": {
|
|
52
54
|
"@types/mdast": "^4.0.4",
|
|
53
|
-
"@types/node": "^
|
|
54
|
-
"shx": "^0.
|
|
55
|
-
"typescript": "^
|
|
55
|
+
"@types/node": "^25.9.3",
|
|
56
|
+
"shx": "^0.4.0",
|
|
57
|
+
"typescript": "^6.0.3",
|
|
56
58
|
"vitest": "^4.1.7"
|
|
57
59
|
},
|
|
58
60
|
"engines": {
|