@m2ai-mcp/notion-mcp 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 (60) hide show
  1. package/.env.example +7 -0
  2. package/LICENSE +21 -0
  3. package/README.md +228 -0
  4. package/dist/index.d.ts +10 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +374 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/tools/blocks.d.ts +43 -0
  9. package/dist/tools/blocks.d.ts.map +1 -0
  10. package/dist/tools/blocks.js +124 -0
  11. package/dist/tools/blocks.js.map +1 -0
  12. package/dist/tools/databases.d.ts +71 -0
  13. package/dist/tools/databases.d.ts.map +1 -0
  14. package/dist/tools/databases.js +121 -0
  15. package/dist/tools/databases.js.map +1 -0
  16. package/dist/tools/index.d.ts +9 -0
  17. package/dist/tools/index.d.ts.map +1 -0
  18. package/dist/tools/index.js +9 -0
  19. package/dist/tools/index.js.map +1 -0
  20. package/dist/tools/pages.d.ts +72 -0
  21. package/dist/tools/pages.d.ts.map +1 -0
  22. package/dist/tools/pages.js +153 -0
  23. package/dist/tools/pages.js.map +1 -0
  24. package/dist/tools/search.d.ts +28 -0
  25. package/dist/tools/search.d.ts.map +1 -0
  26. package/dist/tools/search.js +62 -0
  27. package/dist/tools/search.js.map +1 -0
  28. package/dist/tools/users.d.ts +33 -0
  29. package/dist/tools/users.d.ts.map +1 -0
  30. package/dist/tools/users.js +51 -0
  31. package/dist/tools/users.js.map +1 -0
  32. package/dist/utils/markdown-converter.d.ts +31 -0
  33. package/dist/utils/markdown-converter.d.ts.map +1 -0
  34. package/dist/utils/markdown-converter.js +355 -0
  35. package/dist/utils/markdown-converter.js.map +1 -0
  36. package/dist/utils/notion-client.d.ts +32 -0
  37. package/dist/utils/notion-client.d.ts.map +1 -0
  38. package/dist/utils/notion-client.js +111 -0
  39. package/dist/utils/notion-client.js.map +1 -0
  40. package/dist/utils/types.d.ts +212 -0
  41. package/dist/utils/types.d.ts.map +1 -0
  42. package/dist/utils/types.js +18 -0
  43. package/dist/utils/types.js.map +1 -0
  44. package/jest.config.cjs +33 -0
  45. package/package.json +53 -0
  46. package/server.json +92 -0
  47. package/src/index.ts +435 -0
  48. package/src/tools/blocks.ts +184 -0
  49. package/src/tools/databases.ts +216 -0
  50. package/src/tools/index.ts +9 -0
  51. package/src/tools/pages.ts +253 -0
  52. package/src/tools/search.ts +96 -0
  53. package/src/tools/users.ts +93 -0
  54. package/src/utils/markdown-converter.ts +408 -0
  55. package/src/utils/notion-client.ts +159 -0
  56. package/src/utils/types.ts +237 -0
  57. package/tests/markdown-converter.test.ts +252 -0
  58. package/tests/notion-client.test.ts +67 -0
  59. package/tests/tools.test.ts +448 -0
  60. package/tsconfig.json +20 -0
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Notion API Client
3
+ * Handles all HTTP communication with Notion API
4
+ */
5
+ const NOTION_API_BASE = 'https://api.notion.com/v1';
6
+ const NOTION_VERSION = '2022-06-28';
7
+ export class NotionClient {
8
+ apiKey;
9
+ lastRequestTime = 0;
10
+ rateLimitConfig = {
11
+ requestsPerSecond: 3,
12
+ minDelayMs: 334 // ~3 requests per second
13
+ };
14
+ constructor(config) {
15
+ this.apiKey = config.apiKey;
16
+ }
17
+ async rateLimit() {
18
+ const now = Date.now();
19
+ const timeSinceLastRequest = now - this.lastRequestTime;
20
+ if (timeSinceLastRequest < this.rateLimitConfig.minDelayMs) {
21
+ const delay = this.rateLimitConfig.minDelayMs - timeSinceLastRequest;
22
+ await new Promise(resolve => setTimeout(resolve, delay));
23
+ }
24
+ this.lastRequestTime = Date.now();
25
+ }
26
+ getHeaders() {
27
+ return {
28
+ 'Authorization': `Bearer ${this.apiKey}`,
29
+ 'Notion-Version': NOTION_VERSION,
30
+ 'Content-Type': 'application/json'
31
+ };
32
+ }
33
+ async request(method, endpoint, body) {
34
+ await this.rateLimit();
35
+ const url = `${NOTION_API_BASE}${endpoint}`;
36
+ try {
37
+ const options = {
38
+ method,
39
+ headers: this.getHeaders()
40
+ };
41
+ if (body && (method === 'POST' || method === 'PATCH')) {
42
+ options.body = JSON.stringify(body);
43
+ }
44
+ const response = await fetch(url, options);
45
+ const data = await response.json();
46
+ if (!response.ok) {
47
+ return {
48
+ success: false,
49
+ error: data.message || `HTTP ${response.status}: ${response.statusText}`,
50
+ status: response.status
51
+ };
52
+ }
53
+ return {
54
+ success: true,
55
+ data: data,
56
+ status: response.status
57
+ };
58
+ }
59
+ catch (error) {
60
+ return {
61
+ success: false,
62
+ error: error instanceof Error ? error.message : 'Unknown error occurred'
63
+ };
64
+ }
65
+ }
66
+ async get(endpoint) {
67
+ return this.request('GET', endpoint);
68
+ }
69
+ async post(endpoint, body) {
70
+ return this.request('POST', endpoint, body);
71
+ }
72
+ async patch(endpoint, body) {
73
+ return this.request('PATCH', endpoint, body);
74
+ }
75
+ async delete(endpoint) {
76
+ return this.request('DELETE', endpoint);
77
+ }
78
+ }
79
+ // Helper to format ID with dashes
80
+ function formatWithDashes(id) {
81
+ const clean = id.toLowerCase().replace(/-/g, '');
82
+ return clean.replace(/([a-f0-9]{8})([a-f0-9]{4})([a-f0-9]{4})([a-f0-9]{4})([a-f0-9]{12})/, '$1-$2-$3-$4-$5');
83
+ }
84
+ // Extract page/database ID from URL if provided
85
+ export function extractNotionId(input) {
86
+ // If it's already a UUID format (with or without dashes)
87
+ const uuidPattern = /^[a-f0-9]{8}-?[a-f0-9]{4}-?[a-f0-9]{4}-?[a-f0-9]{4}-?[a-f0-9]{12}$/i;
88
+ if (uuidPattern.test(input)) {
89
+ return formatWithDashes(input);
90
+ }
91
+ // Check if it's just a 32-char hex string
92
+ if (/^[a-f0-9]{32}$/i.test(input)) {
93
+ return formatWithDashes(input);
94
+ }
95
+ // Try to extract ID from Notion URL - look for 32 hex chars at end of path
96
+ // Pattern: matches ID that appears after a dash following text (like "Page-Title-abc123...")
97
+ const urlWithTitlePattern = /[/-]([a-f0-9]{32})(?:\?|$|#)/i;
98
+ const urlMatch = input.match(urlWithTitlePattern);
99
+ if (urlMatch) {
100
+ return formatWithDashes(urlMatch[1]);
101
+ }
102
+ // Try pattern with dashes in URL (like /12345678-90ab-cdef-1234-567890abcdef)
103
+ const urlPatternDashes = /([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/i;
104
+ const dashMatch = input.match(urlPatternDashes);
105
+ if (dashMatch) {
106
+ return formatWithDashes(dashMatch[1]);
107
+ }
108
+ // Return as-is if no pattern matches (will fail at API level)
109
+ return input;
110
+ }
111
+ //# sourceMappingURL=notion-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notion-client.js","sourceRoot":"","sources":["../../src/utils/notion-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Notion API Types
3
+ */
4
+ export interface NotionUser {
5
+ object: 'user';
6
+ id: string;
7
+ type?: 'person' | 'bot';
8
+ name?: string;
9
+ avatar_url?: string | null;
10
+ person?: {
11
+ email?: string;
12
+ };
13
+ }
14
+ export interface NotionParent {
15
+ type: 'database_id' | 'page_id' | 'workspace';
16
+ database_id?: string;
17
+ page_id?: string;
18
+ workspace?: boolean;
19
+ }
20
+ export interface NotionRichTextItem {
21
+ type: 'text' | 'mention' | 'equation';
22
+ text?: {
23
+ content: string;
24
+ link?: {
25
+ url: string;
26
+ } | null;
27
+ };
28
+ annotations?: {
29
+ bold: boolean;
30
+ italic: boolean;
31
+ strikethrough: boolean;
32
+ underline: boolean;
33
+ code: boolean;
34
+ color: string;
35
+ };
36
+ plain_text?: string;
37
+ href?: string | null;
38
+ }
39
+ export interface NotionPage {
40
+ object: 'page';
41
+ id: string;
42
+ created_time: string;
43
+ last_edited_time: string;
44
+ created_by: NotionUser;
45
+ last_edited_by: NotionUser;
46
+ parent: NotionParent;
47
+ archived: boolean;
48
+ properties: Record<string, NotionProperty>;
49
+ url: string;
50
+ icon?: NotionIcon | null;
51
+ cover?: NotionFile | null;
52
+ }
53
+ export interface NotionIcon {
54
+ type: 'emoji' | 'external' | 'file';
55
+ emoji?: string;
56
+ external?: {
57
+ url: string;
58
+ };
59
+ file?: {
60
+ url: string;
61
+ };
62
+ }
63
+ export interface NotionFile {
64
+ type: 'external' | 'file';
65
+ external?: {
66
+ url: string;
67
+ };
68
+ file?: {
69
+ url: string;
70
+ expiry_time: string;
71
+ };
72
+ }
73
+ export interface NotionProperty {
74
+ id: string;
75
+ type: string;
76
+ [key: string]: unknown;
77
+ }
78
+ export interface TitleProperty extends NotionProperty {
79
+ type: 'title';
80
+ title: NotionRichTextItem[];
81
+ }
82
+ export interface RichTextProperty extends NotionProperty {
83
+ type: 'rich_text';
84
+ rich_text: NotionRichTextItem[];
85
+ }
86
+ export interface NumberProperty extends NotionProperty {
87
+ type: 'number';
88
+ number: number | null;
89
+ }
90
+ export interface SelectProperty extends NotionProperty {
91
+ type: 'select';
92
+ select: {
93
+ id: string;
94
+ name: string;
95
+ color: string;
96
+ } | null;
97
+ }
98
+ export interface MultiSelectProperty extends NotionProperty {
99
+ type: 'multi_select';
100
+ multi_select: {
101
+ id: string;
102
+ name: string;
103
+ color: string;
104
+ }[];
105
+ }
106
+ export interface DateProperty extends NotionProperty {
107
+ type: 'date';
108
+ date: {
109
+ start: string;
110
+ end?: string | null;
111
+ time_zone?: string | null;
112
+ } | null;
113
+ }
114
+ export interface CheckboxProperty extends NotionProperty {
115
+ type: 'checkbox';
116
+ checkbox: boolean;
117
+ }
118
+ export interface UrlProperty extends NotionProperty {
119
+ type: 'url';
120
+ url: string | null;
121
+ }
122
+ export interface EmailProperty extends NotionProperty {
123
+ type: 'email';
124
+ email: string | null;
125
+ }
126
+ export interface PhoneProperty extends NotionProperty {
127
+ type: 'phone_number';
128
+ phone_number: string | null;
129
+ }
130
+ export interface PeopleProperty extends NotionProperty {
131
+ type: 'people';
132
+ people: NotionUser[];
133
+ }
134
+ export interface NotionDatabase {
135
+ object: 'database';
136
+ id: string;
137
+ created_time: string;
138
+ last_edited_time: string;
139
+ title: NotionRichTextItem[];
140
+ description: NotionRichTextItem[];
141
+ properties: Record<string, DatabaseProperty>;
142
+ parent: NotionParent;
143
+ url: string;
144
+ archived: boolean;
145
+ is_inline: boolean;
146
+ }
147
+ export interface DatabaseProperty {
148
+ id: string;
149
+ name: string;
150
+ type: string;
151
+ [key: string]: unknown;
152
+ }
153
+ export interface NotionBlock {
154
+ object: 'block';
155
+ id: string;
156
+ parent: NotionParent;
157
+ type: string;
158
+ created_time: string;
159
+ last_edited_time: string;
160
+ created_by: NotionUser;
161
+ last_edited_by: NotionUser;
162
+ has_children: boolean;
163
+ archived: boolean;
164
+ [key: string]: unknown;
165
+ }
166
+ export interface SearchRequest {
167
+ query?: string;
168
+ filter?: {
169
+ value: 'page' | 'database';
170
+ property: 'object';
171
+ };
172
+ sort?: {
173
+ direction: 'ascending' | 'descending';
174
+ timestamp: 'last_edited_time';
175
+ };
176
+ start_cursor?: string;
177
+ page_size?: number;
178
+ }
179
+ export interface SearchResponse {
180
+ object: 'list';
181
+ results: (NotionPage | NotionDatabase)[];
182
+ next_cursor: string | null;
183
+ has_more: boolean;
184
+ }
185
+ export interface DatabaseQueryRequest {
186
+ filter?: NotionFilter;
187
+ sorts?: NotionSort[];
188
+ start_cursor?: string;
189
+ page_size?: number;
190
+ }
191
+ export interface NotionFilter {
192
+ and?: NotionFilter[];
193
+ or?: NotionFilter[];
194
+ property?: string;
195
+ [key: string]: unknown;
196
+ }
197
+ export interface NotionSort {
198
+ property?: string;
199
+ timestamp?: 'created_time' | 'last_edited_time';
200
+ direction: 'ascending' | 'descending';
201
+ }
202
+ export interface PaginatedResponse<T> {
203
+ object: 'list';
204
+ results: T[];
205
+ next_cursor: string | null;
206
+ has_more: boolean;
207
+ }
208
+ export interface UsersListResponse extends PaginatedResponse<NotionUser> {
209
+ }
210
+ export declare function getPageTitle(page: NotionPage): string;
211
+ export declare function getDatabaseTitle(database: NotionDatabase): string;
212
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/utils/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Notion API Types
3
+ */
4
+ // Helper to extract title from page
5
+ export function getPageTitle(page) {
6
+ for (const [, prop] of Object.entries(page.properties)) {
7
+ if (prop.type === 'title') {
8
+ const titleProp = prop;
9
+ return titleProp.title.map(t => t.plain_text || t.text?.content || '').join('');
10
+ }
11
+ }
12
+ return 'Untitled';
13
+ }
14
+ // Helper to extract title from database
15
+ export function getDatabaseTitle(database) {
16
+ return database.title.map(t => t.plain_text || t.text?.content || '').join('') || 'Untitled';
17
+ }
18
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/utils/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AA4NH,oCAAoC;AACpC,MAAM,UAAU,YAAY,CAAC,IAAgB;IAC3C,KAAK,MAAM,CAAC,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACvD,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC1B,MAAM,SAAS,GAAG,IAAqB,CAAC;YACxC,OAAO,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClF,CAAC;IACH,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,gBAAgB,CAAC,QAAwB;IACvD,OAAO,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,UAAU,CAAC;AAC/F,CAAC"}
@@ -0,0 +1,33 @@
1
+ /** @type {import('ts-jest').JestConfigWithTsJest} */
2
+ module.exports = {
3
+ preset: 'ts-jest',
4
+ testEnvironment: 'node',
5
+ roots: ['<rootDir>/tests'],
6
+ testMatch: ['**/*.test.ts'],
7
+ moduleFileExtensions: ['ts', 'js', 'json'],
8
+ moduleNameMapper: {
9
+ '^(\\.{1,2}/.*)\\.js$': '$1'
10
+ },
11
+ transform: {
12
+ '^.+\\.tsx?$': ['ts-jest', {
13
+ useESM: false,
14
+ tsconfig: {
15
+ module: 'commonjs',
16
+ moduleResolution: 'node'
17
+ }
18
+ }]
19
+ },
20
+ collectCoverageFrom: [
21
+ 'src/**/*.ts',
22
+ '!src/**/*.d.ts',
23
+ '!src/index.ts'
24
+ ],
25
+ coverageThreshold: {
26
+ global: {
27
+ branches: 70,
28
+ functions: 70,
29
+ lines: 70,
30
+ statements: 70
31
+ }
32
+ }
33
+ };
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@m2ai-mcp/notion-mcp",
3
+ "version": "0.1.0",
4
+ "description": "Notion Integration MCP server providing AI assistants with full access to Notion workspaces",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "notion-mcp": "./dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "start": "node dist/index.js",
13
+ "dev": "ts-node src/index.ts",
14
+ "test": "jest",
15
+ "test:coverage": "jest --coverage",
16
+ "lint": "eslint src --ext .ts",
17
+ "prepare": "npm run build"
18
+ },
19
+ "keywords": [
20
+ "mcp",
21
+ "notion",
22
+ "model-context-protocol",
23
+ "ai",
24
+ "claude"
25
+ ],
26
+ "author": "Me, Myself Plus AI LLC",
27
+ "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/m2ai-mcp-servers/notion-mcp.git"
31
+ },
32
+ "homepage": "https://github.com/m2ai-mcp-servers/notion-mcp#readme",
33
+ "bugs": {
34
+ "url": "https://github.com/m2ai-mcp-servers/notion-mcp/issues"
35
+ },
36
+ "mcpName": "io.github.m2ai-mcp-servers/notion-mcp",
37
+ "type": "module",
38
+ "dependencies": {
39
+ "@modelcontextprotocol/sdk": "^1.0.0",
40
+ "zod": "^3.22.4"
41
+ },
42
+ "devDependencies": {
43
+ "@types/jest": "^29.5.12",
44
+ "@types/node": "^20.11.0",
45
+ "jest": "^29.7.0",
46
+ "ts-jest": "^29.1.2",
47
+ "ts-node": "^10.9.2",
48
+ "typescript": "^5.3.3"
49
+ },
50
+ "engines": {
51
+ "node": ">=18.0.0"
52
+ }
53
+ }
package/server.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "$schema": "https://registry.modelcontextprotocol.io/schemas/server.json",
3
+ "name": "io.github.m2ai-mcp-servers/notion-mcp",
4
+ "displayName": "Notion MCP",
5
+ "version": "0.1.0",
6
+ "description": "Notion Integration MCP server providing AI assistants with full access to Notion workspaces. Enables Claude to search, read, create, and update pages, databases, and blocks.",
7
+ "author": {
8
+ "name": "Me, Myself Plus AI LLC"
9
+ },
10
+ "license": "MIT",
11
+ "repository": "https://github.com/m2ai-mcp-servers/notion-mcp",
12
+ "homepage": "https://github.com/m2ai-mcp-servers/notion-mcp#readme",
13
+ "runtime": "node",
14
+ "installation": {
15
+ "npm": {
16
+ "package": "@m2ai-mcp/notion-mcp"
17
+ }
18
+ },
19
+ "configuration": {
20
+ "required": [
21
+ {
22
+ "name": "NOTION_API_KEY",
23
+ "description": "Notion Internal Integration Token from https://www.notion.so/my-integrations"
24
+ }
25
+ ]
26
+ },
27
+ "tools": [
28
+ {
29
+ "name": "search",
30
+ "description": "Search across all pages and databases by keyword or title"
31
+ },
32
+ {
33
+ "name": "get_page",
34
+ "description": "Retrieve a page's properties and metadata"
35
+ },
36
+ {
37
+ "name": "create_page",
38
+ "description": "Create a new page in a database or as child of another page"
39
+ },
40
+ {
41
+ "name": "update_page",
42
+ "description": "Update a page's properties (not content blocks)"
43
+ },
44
+ {
45
+ "name": "get_page_content",
46
+ "description": "Retrieve all content blocks from a page"
47
+ },
48
+ {
49
+ "name": "append_blocks",
50
+ "description": "Add new content blocks to a page (supports markdown)"
51
+ },
52
+ {
53
+ "name": "update_block",
54
+ "description": "Update an existing block's content"
55
+ },
56
+ {
57
+ "name": "delete_block",
58
+ "description": "Delete (archive) a block"
59
+ },
60
+ {
61
+ "name": "get_database",
62
+ "description": "Retrieve database schema and properties"
63
+ },
64
+ {
65
+ "name": "query_database",
66
+ "description": "Query a database with filters and sorts"
67
+ },
68
+ {
69
+ "name": "create_database",
70
+ "description": "Create a new database as child of a page"
71
+ },
72
+ {
73
+ "name": "list_users",
74
+ "description": "List all users in the workspace"
75
+ },
76
+ {
77
+ "name": "get_user",
78
+ "description": "Get details about a specific user"
79
+ }
80
+ ],
81
+ "keywords": [
82
+ "notion",
83
+ "mcp",
84
+ "model-context-protocol",
85
+ "ai",
86
+ "claude",
87
+ "productivity",
88
+ "workspace",
89
+ "wiki",
90
+ "notes"
91
+ ]
92
+ }