@atikk-co-jp/notion-mcp-server 0.2.3 → 0.3.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/README.ja.md +64 -0
- package/README.md +64 -0
- package/dist/src/converters/__tests__/markdown-to-blocks.test.d.ts +2 -0
- package/dist/src/converters/__tests__/markdown-to-blocks.test.d.ts.map +1 -0
- package/dist/src/converters/__tests__/markdown-to-blocks.test.js +245 -0
- package/dist/src/converters/index.d.ts +1 -0
- package/dist/src/converters/index.d.ts.map +1 -1
- package/dist/src/converters/index.js +2 -0
- package/dist/src/converters/markdown-to-blocks.d.ts +21 -0
- package/dist/src/converters/markdown-to-blocks.d.ts.map +1 -0
- package/dist/src/converters/markdown-to-blocks.js +244 -0
- package/dist/src/tools/append-blocks-simple.d.ts +4 -0
- package/dist/src/tools/append-blocks-simple.d.ts.map +1 -0
- package/dist/src/tools/append-blocks-simple.js +36 -0
- package/dist/src/tools/create-page-simple.d.ts +4 -0
- package/dist/src/tools/create-page-simple.d.ts.map +1 -0
- package/dist/src/tools/create-page-simple.js +54 -0
- package/dist/src/tools/index.d.ts +3 -1
- package/dist/src/tools/index.d.ts.map +1 -1
- package/dist/src/tools/index.js +5 -1
- package/package.json +1 -1
package/README.ja.md
CHANGED
|
@@ -14,6 +14,7 @@ Notion API用のMCP(Model Context Protocol)サーバー。AIアシスタン
|
|
|
14
14
|
- **検索**: ページとデータベースの横断検索
|
|
15
15
|
- **コメント**: ページへのコメント追加
|
|
16
16
|
- **トークン効率化**: マークダウン/シンプル形式でトークン使用量を約96%削減
|
|
17
|
+
- **Markdown入力対応**: Markdownでコンテンツを作成・追加(出力トークン80%削減)
|
|
17
18
|
|
|
18
19
|
## インストール
|
|
19
20
|
|
|
@@ -110,6 +111,45 @@ Claude Desktopの設定ファイル(macOSの場合: `~/.config/claude/claude_d
|
|
|
110
111
|
}
|
|
111
112
|
```
|
|
112
113
|
|
|
114
|
+
### create-page-simple ⭐
|
|
115
|
+
|
|
116
|
+
Markdownを使ってページを作成します。`create-page`と比較して**出力トークン約80%削減**。
|
|
117
|
+
|
|
118
|
+
**パラメータ:**
|
|
119
|
+
- `database_id` (必須): ページを作成するデータベースのID
|
|
120
|
+
- `title` (必須): ページタイトル(文字列)
|
|
121
|
+
- `content` (任意): ページ本文(Markdown形式)
|
|
122
|
+
- `properties` (任意): 追加のNotionプロパティ
|
|
123
|
+
- `icon` (任意): 絵文字アイコン(例: "🐛")
|
|
124
|
+
|
|
125
|
+
**サポートするMarkdown記法:**
|
|
126
|
+
- 見出し: `# ## ###` (####以上はheading_3にフォールバック)
|
|
127
|
+
- リスト: `- ` または `* ` (箇条書き)、`1. ` (番号付き)
|
|
128
|
+
- チェックボックス: `- [ ]` / `- [x]`
|
|
129
|
+
- コードブロック: ` ``` ` (言語指定対応)
|
|
130
|
+
- 引用: `> `
|
|
131
|
+
- 区切り線: `---`
|
|
132
|
+
- 画像: ``
|
|
133
|
+
- インライン: `**太字**`、`*イタリック*`、`~~取り消し線~~`、`` `コード` ``、`[リンク](url)`
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"database_id": "データベースのUUID",
|
|
138
|
+
"title": "バグレポート",
|
|
139
|
+
"content": "## 再現手順\n\n1. ログイン\n2. 設定を開く\n\n## 期待動作\n\n正常に表示される",
|
|
140
|
+
"properties": {
|
|
141
|
+
"Status": { "status": { "name": "Open" } }
|
|
142
|
+
},
|
|
143
|
+
"icon": "🐛"
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**トークン比較:**
|
|
148
|
+
| 方式 | トークン数 | 削減率 |
|
|
149
|
+
|------|-----------|--------|
|
|
150
|
+
| create-page (ブロック構造) | ~152 | - |
|
|
151
|
+
| create-page-simple (Markdown) | ~26 | **83%** |
|
|
152
|
+
|
|
113
153
|
### update-page
|
|
114
154
|
|
|
115
155
|
ページのプロパティを更新します。
|
|
@@ -250,6 +290,30 @@ Claude Desktopの設定ファイル(macOSの場合: `~/.config/claude/claude_d
|
|
|
250
290
|
}
|
|
251
291
|
```
|
|
252
292
|
|
|
293
|
+
### append-blocks-simple ⭐
|
|
294
|
+
|
|
295
|
+
Markdownを使ってブロックを追加します。`append-block-children`と比較して**出力トークン約80%削減**。
|
|
296
|
+
|
|
297
|
+
**パラメータ:**
|
|
298
|
+
- `block_id` (必須): 追加先のページまたはブロックのID
|
|
299
|
+
- `content` (必須): 追加するコンテンツ(Markdown形式)
|
|
300
|
+
- `after` (任意): このブロックIDの後に挿入
|
|
301
|
+
|
|
302
|
+
`create-page-simple`と同じMarkdown記法をサポートしています。
|
|
303
|
+
|
|
304
|
+
```json
|
|
305
|
+
{
|
|
306
|
+
"block_id": "ページまたはブロックのUUID",
|
|
307
|
+
"content": "# 新しいセクション\n\nこれは**重要な**コンテンツで[リンク](https://example.com)もあります。\n\n- 項目1\n- 項目2\n\n```javascript\nconst x = 1;\n```"
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**トークン比較:**
|
|
312
|
+
| 方式 | トークン数 | 削減率 |
|
|
313
|
+
|------|-----------|--------|
|
|
314
|
+
| append-block-children (ブロック構造) | ~201 | - |
|
|
315
|
+
| append-blocks-simple (Markdown) | ~42 | **79%** |
|
|
316
|
+
|
|
253
317
|
### create-comment
|
|
254
318
|
|
|
255
319
|
ページにコメントを追加します。
|
package/README.md
CHANGED
|
@@ -14,6 +14,7 @@ MCP (Model Context Protocol) server for Notion API. Enables AI assistants to int
|
|
|
14
14
|
- **Search**: Search across pages and databases
|
|
15
15
|
- **Comments**: Add comments to pages
|
|
16
16
|
- **Token-Efficient Output**: Markdown/simple format reduces token usage by ~96%
|
|
17
|
+
- **Markdown Input**: Create and append content using Markdown (80% fewer output tokens)
|
|
17
18
|
|
|
18
19
|
## Installation
|
|
19
20
|
|
|
@@ -110,6 +111,45 @@ Create a new page in a database.
|
|
|
110
111
|
}
|
|
111
112
|
```
|
|
112
113
|
|
|
114
|
+
### create-page-simple ⭐
|
|
115
|
+
|
|
116
|
+
Create a new page using Markdown. **~80% fewer output tokens** compared to `create-page`.
|
|
117
|
+
|
|
118
|
+
**Parameters:**
|
|
119
|
+
- `database_id` (required): The database ID to create the page in
|
|
120
|
+
- `title` (required): Page title as a simple string
|
|
121
|
+
- `content` (optional): Page content in Markdown
|
|
122
|
+
- `properties` (optional): Additional Notion properties
|
|
123
|
+
- `icon` (optional): Emoji icon (e.g., "🐛")
|
|
124
|
+
|
|
125
|
+
**Supported Markdown:**
|
|
126
|
+
- Headings: `# ## ###` (#### and beyond → heading_3)
|
|
127
|
+
- Lists: `- ` or `* ` (bulleted), `1. ` (numbered)
|
|
128
|
+
- Checkboxes: `- [ ]` / `- [x]`
|
|
129
|
+
- Code blocks: ` ``` ` with language
|
|
130
|
+
- Quotes: `> `
|
|
131
|
+
- Dividers: `---`
|
|
132
|
+
- Images: ``
|
|
133
|
+
- Inline: `**bold**`, `*italic*`, `~~strike~~`, `` `code` ``, `[link](url)`
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"database_id": "database-uuid-here",
|
|
138
|
+
"title": "Bug Report",
|
|
139
|
+
"content": "## Steps to Reproduce\n\n1. Login\n2. Open settings\n\n## Expected Behavior\n\nShould display correctly",
|
|
140
|
+
"properties": {
|
|
141
|
+
"Status": { "status": { "name": "Open" } }
|
|
142
|
+
},
|
|
143
|
+
"icon": "🐛"
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Token Comparison:**
|
|
148
|
+
| Method | Tokens | Reduction |
|
|
149
|
+
|--------|--------|-----------|
|
|
150
|
+
| create-page (blocks) | ~152 | - |
|
|
151
|
+
| create-page-simple (markdown) | ~26 | **83%** |
|
|
152
|
+
|
|
113
153
|
### update-page
|
|
114
154
|
|
|
115
155
|
Update a page's properties.
|
|
@@ -250,6 +290,30 @@ Append new blocks to a page or block.
|
|
|
250
290
|
}
|
|
251
291
|
```
|
|
252
292
|
|
|
293
|
+
### append-blocks-simple ⭐
|
|
294
|
+
|
|
295
|
+
Append blocks using Markdown. **~80% fewer output tokens** compared to `append-block-children`.
|
|
296
|
+
|
|
297
|
+
**Parameters:**
|
|
298
|
+
- `block_id` (required): The page or block ID to append to
|
|
299
|
+
- `content` (required): Content in Markdown
|
|
300
|
+
- `after` (optional): Insert after this block ID
|
|
301
|
+
|
|
302
|
+
Same Markdown support as `create-page-simple`.
|
|
303
|
+
|
|
304
|
+
```json
|
|
305
|
+
{
|
|
306
|
+
"block_id": "page-or-block-uuid-here",
|
|
307
|
+
"content": "# New Section\n\nThis is **important** content with a [link](https://example.com).\n\n- Item 1\n- Item 2\n\n```javascript\nconst x = 1;\n```"
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**Token Comparison:**
|
|
312
|
+
| Method | Tokens | Reduction |
|
|
313
|
+
|--------|--------|-----------|
|
|
314
|
+
| append-block-children (blocks) | ~201 | - |
|
|
315
|
+
| append-blocks-simple (markdown) | ~42 | **79%** |
|
|
316
|
+
|
|
253
317
|
### create-comment
|
|
254
318
|
|
|
255
319
|
Add a comment to a page.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown-to-blocks.test.d.ts","sourceRoot":"","sources":["../../../../src/converters/__tests__/markdown-to-blocks.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { markdownToBlocks, parseInlineMarkdown } from '../markdown-to-blocks.js';
|
|
3
|
+
describe('parseInlineMarkdown', () => {
|
|
4
|
+
describe('plain text', () => {
|
|
5
|
+
it('should return plain text as single rich text item', () => {
|
|
6
|
+
const result = parseInlineMarkdown('Hello world');
|
|
7
|
+
expect(result).toEqual([{ type: 'text', text: { content: 'Hello world' } }]);
|
|
8
|
+
});
|
|
9
|
+
it('should handle empty string', () => {
|
|
10
|
+
const result = parseInlineMarkdown('');
|
|
11
|
+
expect(result).toEqual([]);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
describe('bold', () => {
|
|
15
|
+
it('should parse **bold** text', () => {
|
|
16
|
+
const result = parseInlineMarkdown('This is **bold** text');
|
|
17
|
+
expect(result).toHaveLength(3);
|
|
18
|
+
expect(result[0]).toEqual({ type: 'text', text: { content: 'This is ' } });
|
|
19
|
+
expect(result[1]).toEqual({
|
|
20
|
+
type: 'text',
|
|
21
|
+
text: { content: 'bold' },
|
|
22
|
+
annotations: { bold: true },
|
|
23
|
+
});
|
|
24
|
+
expect(result[2]).toEqual({ type: 'text', text: { content: ' text' } });
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe('italic', () => {
|
|
28
|
+
it('should parse *italic* text', () => {
|
|
29
|
+
const result = parseInlineMarkdown('This is *italic* text');
|
|
30
|
+
expect(result).toHaveLength(3);
|
|
31
|
+
expect(result[1]).toEqual({
|
|
32
|
+
type: 'text',
|
|
33
|
+
text: { content: 'italic' },
|
|
34
|
+
annotations: { italic: true },
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe('strikethrough', () => {
|
|
39
|
+
it('should parse ~~strikethrough~~ text', () => {
|
|
40
|
+
const result = parseInlineMarkdown('This is ~~deleted~~ text');
|
|
41
|
+
expect(result).toHaveLength(3);
|
|
42
|
+
expect(result[1]).toEqual({
|
|
43
|
+
type: 'text',
|
|
44
|
+
text: { content: 'deleted' },
|
|
45
|
+
annotations: { strikethrough: true },
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
describe('code', () => {
|
|
50
|
+
it('should parse `code` text', () => {
|
|
51
|
+
const result = parseInlineMarkdown('Use `console.log()` here');
|
|
52
|
+
expect(result).toHaveLength(3);
|
|
53
|
+
expect(result[1]).toEqual({
|
|
54
|
+
type: 'text',
|
|
55
|
+
text: { content: 'console.log()' },
|
|
56
|
+
annotations: { code: true },
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe('links', () => {
|
|
61
|
+
it('should parse [text](url) links', () => {
|
|
62
|
+
const result = parseInlineMarkdown('Visit [Google](https://google.com) now');
|
|
63
|
+
expect(result).toHaveLength(3);
|
|
64
|
+
expect(result[1]).toEqual({
|
|
65
|
+
type: 'text',
|
|
66
|
+
text: { content: 'Google', link: { url: 'https://google.com' } },
|
|
67
|
+
annotations: {},
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe('markdownToBlocks', () => {
|
|
73
|
+
describe('paragraphs', () => {
|
|
74
|
+
it('should convert plain text to paragraph block', () => {
|
|
75
|
+
const result = markdownToBlocks('Hello world');
|
|
76
|
+
expect(result).toHaveLength(1);
|
|
77
|
+
expect(result[0].type).toBe('paragraph');
|
|
78
|
+
expect(result[0].paragraph).toEqual({
|
|
79
|
+
rich_text: [{ type: 'text', text: { content: 'Hello world' } }],
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
it('should skip empty lines', () => {
|
|
83
|
+
const result = markdownToBlocks('Line 1\n\nLine 2');
|
|
84
|
+
expect(result).toHaveLength(2);
|
|
85
|
+
expect(result[0].type).toBe('paragraph');
|
|
86
|
+
expect(result[1].type).toBe('paragraph');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe('headings', () => {
|
|
90
|
+
it('should convert # to heading_1', () => {
|
|
91
|
+
const result = markdownToBlocks('# Heading 1');
|
|
92
|
+
expect(result).toHaveLength(1);
|
|
93
|
+
expect(result[0].type).toBe('heading_1');
|
|
94
|
+
expect(result[0].heading_1).toEqual({
|
|
95
|
+
rich_text: [{ type: 'text', text: { content: 'Heading 1' } }],
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
it('should convert ## to heading_2', () => {
|
|
99
|
+
const result = markdownToBlocks('## Heading 2');
|
|
100
|
+
expect(result).toHaveLength(1);
|
|
101
|
+
expect(result[0].type).toBe('heading_2');
|
|
102
|
+
});
|
|
103
|
+
it('should convert ### to heading_3', () => {
|
|
104
|
+
const result = markdownToBlocks('### Heading 3');
|
|
105
|
+
expect(result).toHaveLength(1);
|
|
106
|
+
expect(result[0].type).toBe('heading_3');
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
describe('lists', () => {
|
|
110
|
+
it('should convert - to bulleted_list_item', () => {
|
|
111
|
+
const result = markdownToBlocks('- Item 1\n- Item 2');
|
|
112
|
+
expect(result).toHaveLength(2);
|
|
113
|
+
expect(result[0].type).toBe('bulleted_list_item');
|
|
114
|
+
expect(result[1].type).toBe('bulleted_list_item');
|
|
115
|
+
});
|
|
116
|
+
it('should convert * to bulleted_list_item', () => {
|
|
117
|
+
const result = markdownToBlocks('* Item 1');
|
|
118
|
+
expect(result).toHaveLength(1);
|
|
119
|
+
expect(result[0].type).toBe('bulleted_list_item');
|
|
120
|
+
});
|
|
121
|
+
it('should convert 1. to numbered_list_item', () => {
|
|
122
|
+
const result = markdownToBlocks('1. First\n2. Second');
|
|
123
|
+
expect(result).toHaveLength(2);
|
|
124
|
+
expect(result[0].type).toBe('numbered_list_item');
|
|
125
|
+
expect(result[1].type).toBe('numbered_list_item');
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
describe('checkboxes', () => {
|
|
129
|
+
it('should convert - [ ] to unchecked to_do', () => {
|
|
130
|
+
const result = markdownToBlocks('- [ ] Task');
|
|
131
|
+
expect(result).toHaveLength(1);
|
|
132
|
+
expect(result[0].type).toBe('to_do');
|
|
133
|
+
expect(result[0].to_do).toEqual({
|
|
134
|
+
rich_text: [{ type: 'text', text: { content: 'Task' } }],
|
|
135
|
+
checked: false,
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
it('should convert - [x] to checked to_do', () => {
|
|
139
|
+
const result = markdownToBlocks('- [x] Done');
|
|
140
|
+
expect(result).toHaveLength(1);
|
|
141
|
+
expect(result[0].type).toBe('to_do');
|
|
142
|
+
expect(result[0].to_do.checked).toBe(true);
|
|
143
|
+
});
|
|
144
|
+
it('should handle - [X] (uppercase)', () => {
|
|
145
|
+
const result = markdownToBlocks('- [X] Done');
|
|
146
|
+
expect(result[0].to_do.checked).toBe(true);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
describe('code blocks', () => {
|
|
150
|
+
it('should convert ``` to code block', () => {
|
|
151
|
+
const result = markdownToBlocks('```\nconst x = 1\n```');
|
|
152
|
+
expect(result).toHaveLength(1);
|
|
153
|
+
expect(result[0].type).toBe('code');
|
|
154
|
+
expect(result[0].code).toEqual({
|
|
155
|
+
rich_text: [{ type: 'text', text: { content: 'const x = 1' } }],
|
|
156
|
+
language: 'plain text',
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
it('should parse language from code fence', () => {
|
|
160
|
+
const result = markdownToBlocks('```typescript\nconst x: number = 1\n```');
|
|
161
|
+
expect(result[0].code.language).toBe('typescript');
|
|
162
|
+
});
|
|
163
|
+
it('should handle multi-line code', () => {
|
|
164
|
+
const result = markdownToBlocks('```js\nline1\nline2\nline3\n```');
|
|
165
|
+
const code = result[0].code;
|
|
166
|
+
expect(code.rich_text[0].text.content).toBe('line1\nline2\nline3');
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
describe('quotes', () => {
|
|
170
|
+
it('should convert > to quote block', () => {
|
|
171
|
+
const result = markdownToBlocks('> This is a quote');
|
|
172
|
+
expect(result).toHaveLength(1);
|
|
173
|
+
expect(result[0].type).toBe('quote');
|
|
174
|
+
expect(result[0].quote).toEqual({
|
|
175
|
+
rich_text: [{ type: 'text', text: { content: 'This is a quote' } }],
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
it('should combine consecutive quote lines', () => {
|
|
179
|
+
const result = markdownToBlocks('> Line 1\n> Line 2');
|
|
180
|
+
expect(result).toHaveLength(1);
|
|
181
|
+
const quote = result[0].quote;
|
|
182
|
+
expect(quote.rich_text[0].text.content).toBe('Line 1\nLine 2');
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
describe('dividers', () => {
|
|
186
|
+
it('should convert --- to divider', () => {
|
|
187
|
+
const result = markdownToBlocks('---');
|
|
188
|
+
expect(result).toHaveLength(1);
|
|
189
|
+
expect(result[0].type).toBe('divider');
|
|
190
|
+
expect(result[0].divider).toEqual({});
|
|
191
|
+
});
|
|
192
|
+
it('should handle ---- (multiple dashes)', () => {
|
|
193
|
+
const result = markdownToBlocks('----');
|
|
194
|
+
expect(result[0].type).toBe('divider');
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
describe('images', () => {
|
|
198
|
+
it('should convert  to image block', () => {
|
|
199
|
+
const result = markdownToBlocks('');
|
|
200
|
+
expect(result).toHaveLength(1);
|
|
201
|
+
expect(result[0].type).toBe('image');
|
|
202
|
+
expect(result[0].image).toEqual({
|
|
203
|
+
type: 'external',
|
|
204
|
+
external: { url: 'https://example.com/image.png' },
|
|
205
|
+
caption: [{ type: 'text', text: { content: 'Alt text' } }],
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
it('should handle image without alt text', () => {
|
|
209
|
+
const result = markdownToBlocks('');
|
|
210
|
+
expect(result[0].image.caption).toEqual([]);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
describe('mixed content', () => {
|
|
214
|
+
it('should handle complex markdown document', () => {
|
|
215
|
+
const markdown = `# Title
|
|
216
|
+
|
|
217
|
+
This is a paragraph with **bold** and *italic*.
|
|
218
|
+
|
|
219
|
+
## Section
|
|
220
|
+
|
|
221
|
+
- Item 1
|
|
222
|
+
- Item 2
|
|
223
|
+
|
|
224
|
+
\`\`\`javascript
|
|
225
|
+
const x = 1
|
|
226
|
+
\`\`\`
|
|
227
|
+
|
|
228
|
+
> A quote
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
`;
|
|
233
|
+
const result = markdownToBlocks(markdown);
|
|
234
|
+
expect(result[0].type).toBe('heading_1');
|
|
235
|
+
expect(result[1].type).toBe('paragraph');
|
|
236
|
+
expect(result[2].type).toBe('heading_2');
|
|
237
|
+
expect(result[3].type).toBe('bulleted_list_item');
|
|
238
|
+
expect(result[4].type).toBe('bulleted_list_item');
|
|
239
|
+
expect(result[5].type).toBe('code');
|
|
240
|
+
expect(result[6].type).toBe('quote');
|
|
241
|
+
expect(result[7].type).toBe('divider');
|
|
242
|
+
expect(result[8].type).toBe('image');
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
});
|
|
@@ -6,4 +6,5 @@
|
|
|
6
6
|
export { blocksToMarkdown, blocksToMarkdownSync, type ConvertOptions, type NotionBlock, } from './block-to-markdown.js';
|
|
7
7
|
export { type NotionProperty, type PropertyValue, pagePropertiesToObject, pagePropertiesToSimple, pagesToSimple, pageToSimple, type SimplePage, type SimpleProperty, } from './page-to-markdown.js';
|
|
8
8
|
export { type RichTextItem, richTextToMarkdown, richTextToPlain, } from './rich-text-to-markdown.js';
|
|
9
|
+
export { markdownToBlocks, parseInlineMarkdown } from './markdown-to-blocks.js';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/converters/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,WAAW,GACjB,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,aAAa,EACb,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,cAAc,GACpB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EACL,KAAK,YAAY,EACjB,kBAAkB,EAClB,eAAe,GAChB,MAAM,4BAA4B,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/converters/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,WAAW,GACjB,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,aAAa,EACb,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,cAAc,GACpB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EACL,KAAK,YAAY,EACjB,kBAAkB,EAClB,eAAe,GAChB,MAAM,4BAA4B,CAAA;AAEnC,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA"}
|
|
@@ -9,3 +9,5 @@ export { blocksToMarkdown, blocksToMarkdownSync, } from './block-to-markdown.js'
|
|
|
9
9
|
export { pagePropertiesToObject, pagePropertiesToSimple, pagesToSimple, pageToSimple, } from './page-to-markdown.js';
|
|
10
10
|
// RichText変換
|
|
11
11
|
export { richTextToMarkdown, richTextToPlain, } from './rich-text-to-markdown.js';
|
|
12
|
+
// Markdown→ブロック変換
|
|
13
|
+
export { markdownToBlocks, parseInlineMarkdown } from './markdown-to-blocks.js';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown文字列をNotionブロック配列に変換するモジュール
|
|
3
|
+
*/
|
|
4
|
+
import type { RichTextItem } from './rich-text-to-markdown.js';
|
|
5
|
+
/**
|
|
6
|
+
* Notionブロックの型定義(簡易版)
|
|
7
|
+
*/
|
|
8
|
+
export interface NotionBlock {
|
|
9
|
+
type: string;
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* インラインMarkdown記法をRichText配列に変換
|
|
14
|
+
* サポート: **bold**, *italic*, ~~strike~~, `code`, [text](url)
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseInlineMarkdown(text: string): RichTextItem[];
|
|
17
|
+
/**
|
|
18
|
+
* Markdown文字列をNotionブロック配列に変換
|
|
19
|
+
*/
|
|
20
|
+
export declare function markdownToBlocks(markdown: string): NotionBlock[];
|
|
21
|
+
//# sourceMappingURL=markdown-to-blocks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown-to-blocks.d.ts","sourceRoot":"","sources":["../../../src/converters/markdown-to-blocks.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAO9D;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,EAAE,CA8HhE;AAmBD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,EAAE,CA2HhE"}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown文字列をNotionブロック配列に変換するモジュール
|
|
3
|
+
*/
|
|
4
|
+
// 安全性のための制限値
|
|
5
|
+
const MAX_INPUT_LENGTH = 100_000; // 100KB
|
|
6
|
+
const MAX_LINE_LENGTH = 10_000; // 10KB per line
|
|
7
|
+
const MAX_CODE_BLOCK_LINES = 1000;
|
|
8
|
+
/**
|
|
9
|
+
* インラインMarkdown記法をRichText配列に変換
|
|
10
|
+
* サポート: **bold**, *italic*, ~~strike~~, `code`, [text](url)
|
|
11
|
+
*/
|
|
12
|
+
export function parseInlineMarkdown(text) {
|
|
13
|
+
// 行長制限(ReDoS対策)
|
|
14
|
+
const safeText = text.length > MAX_LINE_LENGTH ? text.slice(0, MAX_LINE_LENGTH) : text;
|
|
15
|
+
const result = [];
|
|
16
|
+
// 正規表現パターン(優先順位順)
|
|
17
|
+
const patterns = [
|
|
18
|
+
// リンク: [text](url)
|
|
19
|
+
{ regex: /\[([^\]]+)\]\(([^)]+)\)/g, type: 'link' },
|
|
20
|
+
// 太字: **text**
|
|
21
|
+
{ regex: /\*\*([^*]+)\*\*/g, type: 'bold' },
|
|
22
|
+
// イタリック: *text* (太字でないもの)
|
|
23
|
+
{ regex: /(?<!\*)\*([^*]+)\*(?!\*)/g, type: 'italic' },
|
|
24
|
+
// 取り消し線: ~~text~~
|
|
25
|
+
{ regex: /~~([^~]+)~~/g, type: 'strikethrough' },
|
|
26
|
+
// インラインコード: `code`
|
|
27
|
+
{ regex: /`([^`]+)`/g, type: 'code' },
|
|
28
|
+
];
|
|
29
|
+
// 単純な実装: マークダウン記法を順番に処理
|
|
30
|
+
// 複雑なネストは非対応(Notion APIも完全なネストは非対応)
|
|
31
|
+
let lastIndex = 0;
|
|
32
|
+
// 全パターンをまとめて検索
|
|
33
|
+
const allMatches = [];
|
|
34
|
+
for (const { regex, type } of patterns) {
|
|
35
|
+
const re = new RegExp(regex.source, 'g');
|
|
36
|
+
let match = re.exec(safeText);
|
|
37
|
+
while (match !== null) {
|
|
38
|
+
allMatches.push({
|
|
39
|
+
index: match.index,
|
|
40
|
+
length: match[0].length,
|
|
41
|
+
content: match[1],
|
|
42
|
+
type,
|
|
43
|
+
url: type === 'link' ? match[2] : undefined,
|
|
44
|
+
});
|
|
45
|
+
match = re.exec(safeText);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// インデックス順にソート
|
|
49
|
+
allMatches.sort((a, b) => a.index - b.index);
|
|
50
|
+
// 重複を除去(先に来たものを優先)
|
|
51
|
+
const filteredMatches = [];
|
|
52
|
+
let lastEnd = 0;
|
|
53
|
+
for (const m of allMatches) {
|
|
54
|
+
if (m.index >= lastEnd) {
|
|
55
|
+
filteredMatches.push(m);
|
|
56
|
+
lastEnd = m.index + m.length;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// RichText配列を構築
|
|
60
|
+
lastIndex = 0;
|
|
61
|
+
for (const match of filteredMatches) {
|
|
62
|
+
// マッチ前のプレーンテキスト
|
|
63
|
+
if (match.index > lastIndex) {
|
|
64
|
+
const plainText = safeText.slice(lastIndex, match.index);
|
|
65
|
+
if (plainText) {
|
|
66
|
+
result.push({
|
|
67
|
+
type: 'text',
|
|
68
|
+
text: { content: plainText },
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// マッチしたテキスト
|
|
73
|
+
const richText = {
|
|
74
|
+
type: 'text',
|
|
75
|
+
text: { content: match.content },
|
|
76
|
+
annotations: {},
|
|
77
|
+
};
|
|
78
|
+
switch (match.type) {
|
|
79
|
+
case 'bold':
|
|
80
|
+
richText.annotations = { bold: true };
|
|
81
|
+
break;
|
|
82
|
+
case 'italic':
|
|
83
|
+
richText.annotations = { italic: true };
|
|
84
|
+
break;
|
|
85
|
+
case 'strikethrough':
|
|
86
|
+
richText.annotations = { strikethrough: true };
|
|
87
|
+
break;
|
|
88
|
+
case 'code':
|
|
89
|
+
richText.annotations = { code: true };
|
|
90
|
+
break;
|
|
91
|
+
case 'link':
|
|
92
|
+
if (match.url) {
|
|
93
|
+
richText.text = { content: match.content, link: { url: match.url } };
|
|
94
|
+
}
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
result.push(richText);
|
|
98
|
+
lastIndex = match.index + match.length;
|
|
99
|
+
}
|
|
100
|
+
// 残りのプレーンテキスト
|
|
101
|
+
if (lastIndex < safeText.length) {
|
|
102
|
+
const plainText = safeText.slice(lastIndex);
|
|
103
|
+
if (plainText) {
|
|
104
|
+
result.push({
|
|
105
|
+
type: 'text',
|
|
106
|
+
text: { content: plainText },
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// 何もマッチしなかった場合
|
|
111
|
+
if (result.length === 0 && safeText) {
|
|
112
|
+
result.push({
|
|
113
|
+
type: 'text',
|
|
114
|
+
text: { content: safeText },
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* RichTextを含むブロックを生成するヘルパー
|
|
121
|
+
*/
|
|
122
|
+
function createTextBlock(type, text, extra = {}) {
|
|
123
|
+
return {
|
|
124
|
+
type,
|
|
125
|
+
[type]: {
|
|
126
|
+
rich_text: parseInlineMarkdown(text),
|
|
127
|
+
...extra,
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Markdown文字列をNotionブロック配列に変換
|
|
133
|
+
*/
|
|
134
|
+
export function markdownToBlocks(markdown) {
|
|
135
|
+
// 入力長制限(ReDoS対策)
|
|
136
|
+
const safeMarkdown = markdown.length > MAX_INPUT_LENGTH ? markdown.slice(0, MAX_INPUT_LENGTH) : markdown;
|
|
137
|
+
const blocks = [];
|
|
138
|
+
const lines = safeMarkdown.split('\n');
|
|
139
|
+
let i = 0;
|
|
140
|
+
while (i < lines.length) {
|
|
141
|
+
const line = lines[i];
|
|
142
|
+
// 空行はスキップ
|
|
143
|
+
if (!line.trim()) {
|
|
144
|
+
i++;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
// コードブロック: ```language
|
|
148
|
+
if (line.startsWith('```')) {
|
|
149
|
+
const language = line.slice(3).trim() || 'plain text';
|
|
150
|
+
const codeLines = [];
|
|
151
|
+
i++;
|
|
152
|
+
while (i < lines.length && !lines[i].startsWith('```') && codeLines.length < MAX_CODE_BLOCK_LINES) {
|
|
153
|
+
codeLines.push(lines[i]);
|
|
154
|
+
i++;
|
|
155
|
+
}
|
|
156
|
+
// 残りの行をスキップ(制限超過時)
|
|
157
|
+
while (i < lines.length && !lines[i].startsWith('```')) {
|
|
158
|
+
i++;
|
|
159
|
+
}
|
|
160
|
+
blocks.push({
|
|
161
|
+
type: 'code',
|
|
162
|
+
code: {
|
|
163
|
+
rich_text: [{ type: 'text', text: { content: codeLines.join('\n') } }],
|
|
164
|
+
language,
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
i++; // closing ```
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
// 水平線: ---
|
|
171
|
+
if (/^-{3,}$/.test(line.trim())) {
|
|
172
|
+
blocks.push({ type: 'divider', divider: {} });
|
|
173
|
+
i++;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
// 見出し: # ## ### (####以上はheading_3にフォールバック)
|
|
177
|
+
const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
178
|
+
if (headingMatch) {
|
|
179
|
+
const rawLevel = headingMatch[1].length;
|
|
180
|
+
const level = Math.min(rawLevel, 3);
|
|
181
|
+
const text = headingMatch[2];
|
|
182
|
+
const type = `heading_${level}`;
|
|
183
|
+
blocks.push(createTextBlock(type, text));
|
|
184
|
+
i++;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
// チェックボックス: - [ ] or - [x]
|
|
188
|
+
const todoMatch = line.match(/^-\s*\[([ xX])\]\s*(.*)$/);
|
|
189
|
+
if (todoMatch) {
|
|
190
|
+
const checked = todoMatch[1].toLowerCase() === 'x';
|
|
191
|
+
const text = todoMatch[2];
|
|
192
|
+
blocks.push(createTextBlock('to_do', text, { checked }));
|
|
193
|
+
i++;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
// 箇条書き: - or *
|
|
197
|
+
const bulletMatch = line.match(/^[-*]\s+(.+)$/);
|
|
198
|
+
if (bulletMatch) {
|
|
199
|
+
blocks.push(createTextBlock('bulleted_list_item', bulletMatch[1]));
|
|
200
|
+
i++;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
// 番号付きリスト: 1. 2. etc
|
|
204
|
+
const numberedMatch = line.match(/^\d+\.\s+(.+)$/);
|
|
205
|
+
if (numberedMatch) {
|
|
206
|
+
blocks.push(createTextBlock('numbered_list_item', numberedMatch[1]));
|
|
207
|
+
i++;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
// 引用: >
|
|
211
|
+
const quoteMatch = line.match(/^>\s*(.*)$/);
|
|
212
|
+
if (quoteMatch) {
|
|
213
|
+
// 連続する引用行をまとめる
|
|
214
|
+
const quoteLines = [quoteMatch[1]];
|
|
215
|
+
i++;
|
|
216
|
+
while (i < lines.length && lines[i].startsWith('>')) {
|
|
217
|
+
quoteLines.push(lines[i].replace(/^>\s*/, ''));
|
|
218
|
+
i++;
|
|
219
|
+
}
|
|
220
|
+
blocks.push(createTextBlock('quote', quoteLines.join('\n')));
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
// 画像: 
|
|
224
|
+
const imageMatch = line.match(/^!\[([^\]]*)\]\(([^)]+)\)$/);
|
|
225
|
+
if (imageMatch) {
|
|
226
|
+
blocks.push({
|
|
227
|
+
type: 'image',
|
|
228
|
+
image: {
|
|
229
|
+
type: 'external',
|
|
230
|
+
external: { url: imageMatch[2] },
|
|
231
|
+
caption: imageMatch[1]
|
|
232
|
+
? [{ type: 'text', text: { content: imageMatch[1] } }]
|
|
233
|
+
: [],
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
i++;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
// デフォルト: 段落
|
|
240
|
+
blocks.push(createTextBlock('paragraph', line));
|
|
241
|
+
i++;
|
|
242
|
+
}
|
|
243
|
+
return blocks;
|
|
244
|
+
}
|
|
@@ -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 registerAppendBlocksSimple(server: McpServer, notion: NotionClient): void;
|
|
4
|
+
//# sourceMappingURL=append-blocks-simple.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"append-blocks-simple.d.ts","sourceRoot":"","sources":["../../../src/tools/append-blocks-simple.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAUvD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAqCxF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { markdownToBlocks } from '../converters/index.js';
|
|
3
|
+
import { formatResponse, handleError } from '../utils/index.js';
|
|
4
|
+
// Minimal schema for MCP
|
|
5
|
+
const inputSchema = {
|
|
6
|
+
block_id: z.string().describe('Page or block ID to append to'),
|
|
7
|
+
content: z.string().describe('Content in Markdown'),
|
|
8
|
+
after: z.string().optional().describe('Insert after this block ID'),
|
|
9
|
+
};
|
|
10
|
+
export function registerAppendBlocksSimple(server, notion) {
|
|
11
|
+
server.registerTool('append-blocks-simple', {
|
|
12
|
+
description: 'Append blocks to a page using Markdown. ' +
|
|
13
|
+
'Simpler than append-block-children: just provide markdown text. ' +
|
|
14
|
+
'Supports: headings (#), lists (- or 1.), checkboxes (- [ ]), code blocks (```), quotes (>), images (![]()), bold (**), italic (*), links ([]()), etc.',
|
|
15
|
+
inputSchema,
|
|
16
|
+
}, async ({ block_id, content, after }) => {
|
|
17
|
+
try {
|
|
18
|
+
// Convert markdown to blocks
|
|
19
|
+
const children = markdownToBlocks(content);
|
|
20
|
+
// Build params
|
|
21
|
+
const params = {
|
|
22
|
+
block_id,
|
|
23
|
+
children,
|
|
24
|
+
};
|
|
25
|
+
if (after) {
|
|
26
|
+
params.after = after;
|
|
27
|
+
}
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
+
const response = await notion.blocks.children.append(params);
|
|
30
|
+
return formatResponse(response);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
return handleError(error);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -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 registerCreatePageSimple(server: McpServer, notion: NotionClient): void;
|
|
4
|
+
//# sourceMappingURL=create-page-simple.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-page-simple.d.ts","sourceRoot":"","sources":["../../../src/tools/create-page-simple.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAYvD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CA2DtF"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { markdownToBlocks } from '../converters/index.js';
|
|
3
|
+
import { formatResponse, handleError } from '../utils/index.js';
|
|
4
|
+
// Minimal schema for MCP
|
|
5
|
+
const inputSchema = {
|
|
6
|
+
database_id: z.string().describe('Database ID'),
|
|
7
|
+
title: z.string().describe('Page title'),
|
|
8
|
+
content: z.string().optional().describe('Page content in Markdown'),
|
|
9
|
+
properties: z.record(z.string(), z.any()).optional().describe('Additional properties'),
|
|
10
|
+
icon: z.string().optional().describe('Emoji icon (e.g. "bug")'),
|
|
11
|
+
};
|
|
12
|
+
export function registerCreatePageSimple(server, notion) {
|
|
13
|
+
server.registerTool('create-page-simple', {
|
|
14
|
+
description: 'Create a Notion page with Markdown content. ' +
|
|
15
|
+
'Simpler than create-page: just provide title and markdown text. ' +
|
|
16
|
+
'Supports: headings (#), lists (- or 1.), checkboxes (- [ ]), code blocks (```), quotes (>), images (![]()), bold (**), italic (*), links ([]()), etc.',
|
|
17
|
+
inputSchema,
|
|
18
|
+
}, async ({ database_id, title, content, properties, icon }) => {
|
|
19
|
+
try {
|
|
20
|
+
// Build properties with title
|
|
21
|
+
const pageProperties = {
|
|
22
|
+
...properties,
|
|
23
|
+
};
|
|
24
|
+
// Check if any title property is already provided
|
|
25
|
+
// Title properties have the structure: { title: [...] }
|
|
26
|
+
const hasTitleProperty = Object.values(pageProperties).some((prop) => prop && typeof prop === 'object' && 'title' in prop);
|
|
27
|
+
// Only add Name if no title property exists
|
|
28
|
+
if (!hasTitleProperty) {
|
|
29
|
+
pageProperties.Name = {
|
|
30
|
+
title: [{ type: 'text', text: { content: title } }],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
// Build params
|
|
34
|
+
const params = {
|
|
35
|
+
parent: { database_id },
|
|
36
|
+
properties: pageProperties,
|
|
37
|
+
};
|
|
38
|
+
// Convert markdown to blocks if content provided
|
|
39
|
+
if (content) {
|
|
40
|
+
params.children = markdownToBlocks(content);
|
|
41
|
+
}
|
|
42
|
+
// Add icon if provided
|
|
43
|
+
if (icon) {
|
|
44
|
+
params.icon = { type: 'emoji', emoji: icon };
|
|
45
|
+
}
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
+
const response = await notion.pages.create(params);
|
|
48
|
+
return formatResponse(response);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
return handleError(error);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import type { NotionClient } from '../notion-client.js';
|
|
3
3
|
import { registerAppendBlockChildren } from './append-block-children.js';
|
|
4
|
+
import { registerAppendBlocksSimple } from './append-blocks-simple.js';
|
|
4
5
|
import { registerCreateComment } from './create-comment.js';
|
|
5
6
|
import { registerCreateDatabase } from './create-database.js';
|
|
6
7
|
import { registerCreatePage } from './create-page.js';
|
|
8
|
+
import { registerCreatePageSimple } from './create-page-simple.js';
|
|
7
9
|
import { registerGetBlockChildren } from './get-block-children.js';
|
|
8
10
|
import { registerQueryDatabase } from './query-database.js';
|
|
9
11
|
import { registerRetrievePage } from './retrieve-page.js';
|
|
@@ -11,5 +13,5 @@ import { registerSearch } from './search.js';
|
|
|
11
13
|
import { registerUpdateDatabase } from './update-database.js';
|
|
12
14
|
import { registerUpdatePage } from './update-page.js';
|
|
13
15
|
export declare function registerAllTools(server: McpServer, notion: NotionClient): void;
|
|
14
|
-
export { registerRetrievePage, registerCreatePage, registerUpdatePage, registerCreateDatabase, registerUpdateDatabase, registerQueryDatabase, registerSearch, registerGetBlockChildren, registerAppendBlockChildren, registerCreateComment, };
|
|
16
|
+
export { registerRetrievePage, registerCreatePage, registerCreatePageSimple, registerUpdatePage, registerCreateDatabase, registerUpdateDatabase, registerQueryDatabase, registerSearch, registerGetBlockChildren, registerAppendBlockChildren, registerAppendBlocksSimple, registerCreateComment, };
|
|
15
17
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAA;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAA;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAA;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAA;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAErD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAA;AACxE,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAA;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAA;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAA;AAClE,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAA;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAA;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAErD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAsB9E;AAED,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,wBAAwB,EACxB,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,cAAc,EACd,wBAAwB,EACxB,2BAA2B,EAC3B,0BAA0B,EAC1B,qBAAqB,GACtB,CAAA"}
|
package/dist/src/tools/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { registerAppendBlockChildren } from './append-block-children.js';
|
|
2
|
+
import { registerAppendBlocksSimple } from './append-blocks-simple.js';
|
|
2
3
|
import { registerCreateComment } from './create-comment.js';
|
|
3
4
|
import { registerCreateDatabase } from './create-database.js';
|
|
4
5
|
import { registerCreatePage } from './create-page.js';
|
|
6
|
+
import { registerCreatePageSimple } from './create-page-simple.js';
|
|
5
7
|
import { registerGetBlockChildren } from './get-block-children.js';
|
|
6
8
|
import { registerQueryDatabase } from './query-database.js';
|
|
7
9
|
import { registerRetrievePage } from './retrieve-page.js';
|
|
@@ -12,6 +14,7 @@ export function registerAllTools(server, notion) {
|
|
|
12
14
|
// Page operations
|
|
13
15
|
registerRetrievePage(server, notion);
|
|
14
16
|
registerCreatePage(server, notion);
|
|
17
|
+
registerCreatePageSimple(server, notion);
|
|
15
18
|
registerUpdatePage(server, notion);
|
|
16
19
|
// Database operations
|
|
17
20
|
registerCreateDatabase(server, notion);
|
|
@@ -22,7 +25,8 @@ export function registerAllTools(server, notion) {
|
|
|
22
25
|
// Block operations
|
|
23
26
|
registerGetBlockChildren(server, notion);
|
|
24
27
|
registerAppendBlockChildren(server, notion);
|
|
28
|
+
registerAppendBlocksSimple(server, notion);
|
|
25
29
|
// Comment operations
|
|
26
30
|
registerCreateComment(server, notion);
|
|
27
31
|
}
|
|
28
|
-
export { registerRetrievePage, registerCreatePage, registerUpdatePage, registerCreateDatabase, registerUpdateDatabase, registerQueryDatabase, registerSearch, registerGetBlockChildren, registerAppendBlockChildren, registerCreateComment, };
|
|
32
|
+
export { registerRetrievePage, registerCreatePage, registerCreatePageSimple, registerUpdatePage, registerCreateDatabase, registerUpdateDatabase, registerQueryDatabase, registerSearch, registerGetBlockChildren, registerAppendBlockChildren, registerAppendBlocksSimple, registerCreateComment, };
|