@tk_wfl/o2n-mcp-server 0.1.2
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/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +148 -0
- package/dist/index.js.map +1 -0
- package/dist/jobs.d.ts +13 -0
- package/dist/jobs.d.ts.map +1 -0
- package/dist/jobs.js +8 -0
- package/dist/jobs.js.map +1 -0
- package/dist/plan-store.d.ts +4 -0
- package/dist/plan-store.d.ts.map +1 -0
- package/dist/plan-store.js +27 -0
- package/dist/plan-store.js.map +1 -0
- package/package.json +46 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { promises as fs } from 'node:fs';
|
|
4
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
5
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { scanVault, runMigration, NotionClient, NotionApi, StateStore, writeReport, buildReport, reportPath, statePath, loadCredentials, } from '@tk_wfl/o2n-core';
|
|
8
|
+
import { loadOrCreatePlan, savePlan } from './plan-store.js';
|
|
9
|
+
import { getJob, setJob } from './jobs.js';
|
|
10
|
+
const server = new McpServer({ name: 'o2n-mcp-server', version: '0.1.0' });
|
|
11
|
+
function text(content) {
|
|
12
|
+
return { content: [{ type: 'text', text: content }] };
|
|
13
|
+
}
|
|
14
|
+
server.tool('scan_vault', 'Obsidian vaultを走査してインベントリ要約を返す。副作用なし(読み取りのみ)。', { vaultPath: z.string().describe('Obsidian vaultの絶対パス') }, async ({ vaultPath }) => {
|
|
15
|
+
const inventory = await scanVault(vaultPath);
|
|
16
|
+
const summary = {
|
|
17
|
+
vaultPath: inventory.vaultPath,
|
|
18
|
+
noteCount: inventory.notes.length,
|
|
19
|
+
attachmentCount: inventory.attachments.length,
|
|
20
|
+
wikiLinkCount: inventory.wikiLinks.length,
|
|
21
|
+
skippedCount: inventory.skipped.length,
|
|
22
|
+
warningCount: inventory.warnings.length,
|
|
23
|
+
folders: Object.fromEntries(Object.entries(inventory.folderTree).map(([k, v]) => [k || '(root)', v.length])),
|
|
24
|
+
frontmatterKeyStats: inventory.frontmatterKeyStats,
|
|
25
|
+
};
|
|
26
|
+
return text(JSON.stringify(summary, null, 2));
|
|
27
|
+
});
|
|
28
|
+
server.tool('get_plan', '現在の移行計画を返す。存在しなければ自動提案から新規生成する。副作用: 計画ファイルが未作成の場合は作成する。', {
|
|
29
|
+
vaultPath: z.string().describe('Obsidian vaultの絶対パス'),
|
|
30
|
+
parentPageId: z.string().optional().describe('移行先のNotion親ページID(未指定なら既存計画の値を使う)'),
|
|
31
|
+
}, async ({ vaultPath, parentPageId }) => {
|
|
32
|
+
const plan = await loadOrCreatePlan(vaultPath, parentPageId);
|
|
33
|
+
return text(JSON.stringify(plan, null, 2));
|
|
34
|
+
});
|
|
35
|
+
const folderPlanSchema = z.object({
|
|
36
|
+
folderPath: z.string(),
|
|
37
|
+
mode: z.enum(['page_tree', 'database']),
|
|
38
|
+
suggestionReason: z.string().optional(),
|
|
39
|
+
});
|
|
40
|
+
server.tool('update_plan', '移行計画を部分更新する(folders・parentPageId・skipListなど)。', {
|
|
41
|
+
vaultPath: z.string().describe('Obsidian vaultの絶対パス'),
|
|
42
|
+
patch: z
|
|
43
|
+
.object({
|
|
44
|
+
parentPageId: z.string().optional(),
|
|
45
|
+
folders: z.array(folderPlanSchema).optional(),
|
|
46
|
+
skipList: z.array(z.string()).optional(),
|
|
47
|
+
})
|
|
48
|
+
.describe('計画への部分更新'),
|
|
49
|
+
}, async ({ vaultPath, patch }) => {
|
|
50
|
+
const plan = await loadOrCreatePlan(vaultPath);
|
|
51
|
+
if (patch.parentPageId)
|
|
52
|
+
plan.parentPageId = patch.parentPageId;
|
|
53
|
+
if (patch.folders)
|
|
54
|
+
plan.folders = patch.folders;
|
|
55
|
+
if (patch.skipList)
|
|
56
|
+
plan.skipList = patch.skipList;
|
|
57
|
+
await savePlan(vaultPath, plan);
|
|
58
|
+
return text(JSON.stringify(plan, null, 2));
|
|
59
|
+
});
|
|
60
|
+
server.tool('start_migration', '移行を開始する(バックグラウンド実行)。呼び出し前に必ずユーザーへ移行内容(対象vault・移行先ページ・dry-runか否か)を提示し、' +
|
|
61
|
+
'明示的な確認を得ること。ユーザーの明示的な確認なしに呼び出さないこと。進捗は migration_status で確認する。', {
|
|
62
|
+
vaultPath: z.string().describe('Obsidian vaultの絶対パス'),
|
|
63
|
+
parentPageId: z.string().describe('移行先のNotion親ページID'),
|
|
64
|
+
dryRun: z.boolean().default(false).describe('trueの場合、書き込みAPIを呼ばずシミュレーションのみ行う'),
|
|
65
|
+
}, async ({ vaultPath, parentPageId, dryRun }) => {
|
|
66
|
+
const resolved = path.resolve(vaultPath);
|
|
67
|
+
const existing = getJob(resolved);
|
|
68
|
+
if (existing?.status === 'running') {
|
|
69
|
+
return text(`既に移行が実行中です(進捗 ${existing.done}/${existing.total})。migration_status で確認してください。`);
|
|
70
|
+
}
|
|
71
|
+
const inventory = await scanVault(resolved);
|
|
72
|
+
const plan = await loadOrCreatePlan(resolved, parentPageId);
|
|
73
|
+
const estimateSeconds = Math.ceil((inventory.notes.length + inventory.attachments.length) * 3 * 0.4);
|
|
74
|
+
setJob(resolved, { status: 'running', done: 0, total: inventory.notes.length, currentPath: '', startedAt: Date.now() });
|
|
75
|
+
void (async () => {
|
|
76
|
+
try {
|
|
77
|
+
const token = process.env.NOTION_TOKEN ?? (await loadCredentials())?.token ?? (dryRun ? 'dry-run-placeholder-token' : '');
|
|
78
|
+
if (!token)
|
|
79
|
+
throw new Error('Notionと連携されていません。CLIで `o2n login` を実行するか、NOTION_TOKEN を設定してください。');
|
|
80
|
+
const client = new NotionClient({ token, dryRun });
|
|
81
|
+
const api = new NotionApi(client);
|
|
82
|
+
const state = await StateStore.load(resolved, plan.parentPageId, { readOnly: dryRun });
|
|
83
|
+
const entries = await runMigration({
|
|
84
|
+
vaultPath: resolved,
|
|
85
|
+
plan,
|
|
86
|
+
inventory,
|
|
87
|
+
api,
|
|
88
|
+
state,
|
|
89
|
+
dryRun,
|
|
90
|
+
onProgress: (done, total, currentPath) => {
|
|
91
|
+
setJob(resolved, { status: 'running', done, total, currentPath, startedAt: getJob(resolved)?.startedAt ?? Date.now() });
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
const report = buildReport(state.snapshot, entries);
|
|
95
|
+
await writeReport(resolved, report, state.snapshot);
|
|
96
|
+
setJob(resolved, {
|
|
97
|
+
status: 'done',
|
|
98
|
+
done: inventory.notes.length,
|
|
99
|
+
total: inventory.notes.length,
|
|
100
|
+
currentPath: '',
|
|
101
|
+
startedAt: getJob(resolved)?.startedAt ?? Date.now(),
|
|
102
|
+
finishedAt: Date.now(),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
setJob(resolved, {
|
|
107
|
+
status: 'error',
|
|
108
|
+
done: getJob(resolved)?.done ?? 0,
|
|
109
|
+
total: inventory.notes.length,
|
|
110
|
+
currentPath: '',
|
|
111
|
+
error: String(err),
|
|
112
|
+
startedAt: getJob(resolved)?.startedAt ?? Date.now(),
|
|
113
|
+
finishedAt: Date.now(),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
})();
|
|
117
|
+
return text(`移行を開始しました(${dryRun ? 'dry-run' : '本実行'})。対象: ${inventory.notes.length}ノート・${inventory.attachments.length}添付。` +
|
|
118
|
+
`推定所要時間: 約${Math.ceil(estimateSeconds / 60)}分。migration_status で進捗を確認してください。`);
|
|
119
|
+
});
|
|
120
|
+
server.tool('migration_status', '進行中/完了した移行の進捗(done / failed / pending件数、現在のパス)を返す。', { vaultPath: z.string().describe('Obsidian vaultの絶対パス') }, async ({ vaultPath }) => {
|
|
121
|
+
const resolved = path.resolve(vaultPath);
|
|
122
|
+
const job = getJob(resolved);
|
|
123
|
+
let stateSummary = {};
|
|
124
|
+
try {
|
|
125
|
+
const raw = await fs.readFile(statePath(resolved), 'utf-8');
|
|
126
|
+
const state = JSON.parse(raw);
|
|
127
|
+
stateSummary = Object.values(state.notes).reduce((acc, n) => {
|
|
128
|
+
acc[n.status] = (acc[n.status] ?? 0) + 1;
|
|
129
|
+
return acc;
|
|
130
|
+
}, {});
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// state.json未作成
|
|
134
|
+
}
|
|
135
|
+
return text(JSON.stringify({ job: job ?? { status: 'not_started' }, noteStatusCounts: stateSummary }, null, 2));
|
|
136
|
+
});
|
|
137
|
+
server.tool('get_report', 'レポート(.o2n/report.md)の内容を返す。', { vaultPath: z.string().describe('Obsidian vaultの絶対パス') }, async ({ vaultPath }) => {
|
|
138
|
+
try {
|
|
139
|
+
const content = await fs.readFile(reportPath(vaultPath), 'utf-8');
|
|
140
|
+
return text(content);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return text('レポートがまだ生成されていません。start_migration の完了後に再度お試しください。');
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
const transport = new StdioServerTransport();
|
|
147
|
+
await server.connect(transport);
|
|
148
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/jobs.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type JobStatus = 'running' | 'done' | 'error';
|
|
2
|
+
export interface MigrationJob {
|
|
3
|
+
status: JobStatus;
|
|
4
|
+
done: number;
|
|
5
|
+
total: number;
|
|
6
|
+
currentPath: string;
|
|
7
|
+
error?: string;
|
|
8
|
+
startedAt: number;
|
|
9
|
+
finishedAt?: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function getJob(vaultPath: string): MigrationJob | undefined;
|
|
12
|
+
export declare function setJob(vaultPath: string, job: MigrationJob): void;
|
|
13
|
+
//# sourceMappingURL=jobs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../src/jobs.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAErD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,SAAS,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAID,wBAAgB,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAElE;AAED,wBAAgB,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,IAAI,CAEjE"}
|
package/dist/jobs.js
ADDED
package/dist/jobs.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobs.js","sourceRoot":"","sources":["../src/jobs.ts"],"names":[],"mappings":"AAYA,MAAM,IAAI,GAAG,IAAI,GAAG,EAAwB,CAAC;AAE7C,MAAM,UAAU,MAAM,CAAC,SAAiB;IACtC,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,SAAiB,EAAE,GAAiB;IACzD,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type MigrationPlan } from '@tk_wfl/o2n-core';
|
|
2
|
+
export declare function loadOrCreatePlan(vaultPath: string, parentPageId?: string): Promise<MigrationPlan>;
|
|
3
|
+
export declare function savePlan(vaultPath: string, plan: MigrationPlan): Promise<void>;
|
|
4
|
+
//# sourceMappingURL=plan-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-store.d.ts","sourceRoot":"","sources":["../src/plan-store.ts"],"names":[],"mappings":"AAEA,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAM5E,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAYvG;AAED,wBAAsB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAIpF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { scanVault, buildPlan } from '@tk_wfl/o2n-core';
|
|
4
|
+
function planPath(vaultPath) {
|
|
5
|
+
return path.join(vaultPath, '.o2n', 'plan.json');
|
|
6
|
+
}
|
|
7
|
+
export async function loadOrCreatePlan(vaultPath, parentPageId) {
|
|
8
|
+
try {
|
|
9
|
+
const raw = await fs.readFile(planPath(vaultPath), 'utf-8');
|
|
10
|
+
const plan = JSON.parse(raw);
|
|
11
|
+
if (parentPageId)
|
|
12
|
+
plan.parentPageId = parentPageId;
|
|
13
|
+
return plan;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
const inventory = await scanVault(vaultPath);
|
|
17
|
+
const plan = buildPlan(inventory, { parentPageId: parentPageId ?? '' });
|
|
18
|
+
await savePlan(vaultPath, plan);
|
|
19
|
+
return plan;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export async function savePlan(vaultPath, plan) {
|
|
23
|
+
const p = planPath(vaultPath);
|
|
24
|
+
await fs.mkdir(path.dirname(p), { recursive: true });
|
|
25
|
+
await fs.writeFile(p, JSON.stringify(plan, null, 2), 'utf-8');
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=plan-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-store.js","sourceRoot":"","sources":["../src/plan-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,SAAS,EAAsB,MAAM,kBAAkB,CAAC;AAE5E,SAAS,QAAQ,CAAC,SAAiB;IACjC,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,SAAiB,EAAE,YAAqB;IAC7E,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,CAAC;QAC5D,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAkB,CAAC;QAC9C,IAAI,YAAY;YAAE,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACnD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,SAAS,CAAC,SAAS,EAAE,EAAE,YAAY,EAAE,YAAY,IAAI,EAAE,EAAE,CAAC,CAAC;QACxE,MAAM,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,SAAiB,EAAE,IAAmB;IACnE,MAAM,CAAC,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC9B,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,MAAM,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AAChE,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tk_wfl/o2n-mcp-server",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "o2n(Obsidian→Notion移行ツール)のローカルMCPサーバー",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/TK-WFL/o2n.git",
|
|
10
|
+
"directory": "packages/mcp-server"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/TK-WFL/o2n#readme",
|
|
13
|
+
"bugs": "https://github.com/TK-WFL/o2n/issues",
|
|
14
|
+
"keywords": [
|
|
15
|
+
"obsidian",
|
|
16
|
+
"notion",
|
|
17
|
+
"migration",
|
|
18
|
+
"mcp"
|
|
19
|
+
],
|
|
20
|
+
"bin": {
|
|
21
|
+
"o2n-mcp-server": "dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
"main": "./dist/index.js",
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc -p tsconfig.json && chmod +x dist/index.js",
|
|
32
|
+
"prepublishOnly": "npm run build",
|
|
33
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@tk_wfl/o2n-core": "^0.1.0",
|
|
37
|
+
"@modelcontextprotocol/sdk": "^1.9.0",
|
|
38
|
+
"zod": "^4.4.3"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"typescript": "^5.7.0"
|
|
42
|
+
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=20"
|
|
45
|
+
}
|
|
46
|
+
}
|