@fromsko/obsidian-mcp-server 1.1.7 → 1.1.8

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 (2) hide show
  1. package/dist/index.js +187 -14
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2,10 +2,27 @@
2
2
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
+ import { exec } from "child_process";
5
6
  import * as fs from "fs/promises";
6
7
  import { glob } from "glob";
7
8
  import matter from "gray-matter";
8
9
  import * as path from "path";
10
+ import { promisify } from "util";
11
+ const execAsync = promisify(exec);
12
+ // Git Commit 类型定义
13
+ const COMMIT_TYPES = {
14
+ feat: { emoji: "✨", description: "新功能" },
15
+ fix: { emoji: "🐛", description: "Bug 修复" },
16
+ docs: { emoji: "📝", description: "文档更新" },
17
+ style: { emoji: "🎨", description: "代码格式" },
18
+ refactor: { emoji: "♻️", description: "重构" },
19
+ perf: { emoji: "⚡", description: "性能优化" },
20
+ test: { emoji: "✅", description: "测试" },
21
+ build: { emoji: "📦", description: "构建/依赖" },
22
+ ci: { emoji: "👷", description: "CI/CD" },
23
+ chore: { emoji: "🔧", description: "杂项" },
24
+ revert: { emoji: "⏪", description: "回滚" },
25
+ };
9
26
  // 解析命令行参数
10
27
  function parseArgs() {
11
28
  const args = process.argv.slice(2);
@@ -18,7 +35,7 @@ function parseArgs() {
18
35
  }
19
36
  if (!vaultPath) {
20
37
  console.error("错误: 请使用 --vault 参数指定 Obsidian 笔记库路径");
21
- console.error("用法: node dist/index.js --vault \"/path/to/your/vault\"");
38
+ console.error('用法: node dist/index.js --vault "/path/to/your/vault"');
22
39
  process.exit(1);
23
40
  }
24
41
  return { vaultPath };
@@ -103,13 +120,15 @@ async function listFolder(folderPath = "") {
103
120
  // 获取笔记库结构
104
121
  async function getNoteStructure() {
105
122
  const rootContent = await listFolder("");
106
- const structure = { _notes: rootContent.notes.map(n => n.name) };
123
+ const structure = {
124
+ _notes: rootContent.notes.map((n) => n.name),
125
+ };
107
126
  for (const folder of rootContent.folders) {
108
127
  try {
109
128
  const subContent = await listFolder(folder);
110
129
  structure[folder] = {
111
130
  folders: subContent.folders,
112
- notes: subContent.notes.map(n => n.name),
131
+ notes: subContent.notes.map((n) => n.name),
113
132
  };
114
133
  }
115
134
  catch {
@@ -130,11 +149,11 @@ async function createNote(notePath, content) {
130
149
  throw new Error(`笔记已存在: ${notePath}`);
131
150
  }
132
151
  catch (err) {
133
- if (err.code !== 'ENOENT')
152
+ if (err.code !== "ENOENT")
134
153
  throw err;
135
154
  }
136
155
  // 写入文件
137
- await fs.writeFile(fullPath, content, 'utf-8');
156
+ await fs.writeFile(fullPath, content, "utf-8");
138
157
  return `笔记创建成功: ${notePath}`;
139
158
  }
140
159
  // 更新笔记
@@ -148,7 +167,7 @@ async function updateNote(notePath, content) {
148
167
  throw new Error(`笔记不存在: ${notePath}`);
149
168
  }
150
169
  // 写入文件
151
- await fs.writeFile(fullPath, content, 'utf-8');
170
+ await fs.writeFile(fullPath, content, "utf-8");
152
171
  return `笔记更新成功: ${notePath}`;
153
172
  }
154
173
  // 创建文件夹
@@ -165,7 +184,7 @@ async function createFolder(folderPath) {
165
184
  }
166
185
  }
167
186
  catch (err) {
168
- if (err.code !== 'ENOENT')
187
+ if (err.code !== "ENOENT")
169
188
  throw err;
170
189
  }
171
190
  // 创建文件夹
@@ -350,6 +369,60 @@ created: 2024-12-26
350
369
  `;
351
370
  return guide;
352
371
  }
372
+ // Git 相关函数
373
+ async function execGit(command, cwd) {
374
+ try {
375
+ return await execAsync(`git ${command}`, { cwd: cwd || VAULT_PATH });
376
+ }
377
+ catch (error) {
378
+ throw new Error(`Git 命令执行失败: ${error.message}`);
379
+ }
380
+ }
381
+ // Git 提交
382
+ async function gitCommit(type, description, files, cwd) {
383
+ const workDir = cwd || VAULT_PATH;
384
+ // 验证提交类型
385
+ if (!COMMIT_TYPES[type]) {
386
+ const validTypes = Object.entries(COMMIT_TYPES)
387
+ .map(([k, v]) => `${v.emoji} ${k}: ${v.description}`)
388
+ .join("\n");
389
+ throw new Error(`无效的提交类型: ${type}\n\n可用类型:\n${validTypes}`);
390
+ }
391
+ const { emoji } = COMMIT_TYPES[type];
392
+ const commitMessage = `${emoji} ${type}: ${description}`;
393
+ // 添加文件
394
+ if (files && files.length > 0) {
395
+ for (const file of files) {
396
+ await execGit(`add "${file}"`, workDir);
397
+ }
398
+ }
399
+ else {
400
+ // 添加所有更改
401
+ await execGit("add -A", workDir);
402
+ }
403
+ // 检查是否有待提交的更改
404
+ const { stdout: status } = await execGit("status --porcelain", workDir);
405
+ if (!status.trim()) {
406
+ return "没有需要提交的更改";
407
+ }
408
+ // 执行提交
409
+ await execGit(`commit -m "${commitMessage}"`, workDir);
410
+ return `提交成功: ${commitMessage}`;
411
+ }
412
+ // 获取 Git 状态
413
+ async function gitStatus(cwd) {
414
+ const { stdout } = await execGit("status --short", cwd || VAULT_PATH);
415
+ if (!stdout.trim()) {
416
+ return "工作区干净,没有待提交的更改";
417
+ }
418
+ return stdout;
419
+ }
420
+ // 获取提交类型列表
421
+ function getCommitTypes() {
422
+ return Object.entries(COMMIT_TYPES)
423
+ .map(([type, { emoji, description }]) => `${emoji} ${type}: ${description}`)
424
+ .join("\n");
425
+ }
353
426
  // 全文搜索
354
427
  async function fullTextSearch(keyword) {
355
428
  const files = await glob("**/*.md", {
@@ -395,9 +468,15 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
395
468
  inputSchema: {
396
469
  type: "object",
397
470
  properties: {
398
- query: { type: "string", description: "搜索关键词(匹配笔记名称和摘要)" },
471
+ query: {
472
+ type: "string",
473
+ description: "搜索关键词(匹配笔记名称和摘要)",
474
+ },
399
475
  tag: { type: "string", description: "按标签过滤" },
400
- category: { type: "string", description: "按分类过滤(如 hardware, ai, backend 等)" },
476
+ category: {
477
+ type: "string",
478
+ description: "按分类过滤(如 hardware, ai, backend 等)",
479
+ },
401
480
  },
402
481
  },
403
482
  },
@@ -407,7 +486,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
407
486
  inputSchema: {
408
487
  type: "object",
409
488
  properties: {
410
- path: { type: "string", description: "笔记的相对路径(如 知识点/03-硬件学习/STM32系列选型速查.md)" },
489
+ path: {
490
+ type: "string",
491
+ description: "笔记的相对路径(如 知识点/03-硬件学习/STM32系列选型速查.md)",
492
+ },
411
493
  },
412
494
  required: ["path"],
413
495
  },
@@ -418,7 +500,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
418
500
  inputSchema: {
419
501
  type: "object",
420
502
  properties: {
421
- folder: { type: "string", description: "文件夹路径(留空则列出根目录)" },
503
+ folder: {
504
+ type: "string",
505
+ description: "文件夹路径(留空则列出根目录)",
506
+ },
422
507
  },
423
508
  },
424
509
  },
@@ -444,8 +529,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
444
529
  inputSchema: {
445
530
  type: "object",
446
531
  properties: {
447
- path: { type: "string", description: "笔记的相对路径(如 知识点/04-人工智能/MCP/自己制作的MCP/新笔记.md)" },
448
- content: { type: "string", description: "笔记内容(支持 Markdown 和 Frontmatter)" },
532
+ path: {
533
+ type: "string",
534
+ description: "笔记的相对路径(如 知识点/04-人工智能/MCP/自己制作的MCP/新笔记.md)",
535
+ },
536
+ content: {
537
+ type: "string",
538
+ description: "笔记内容(支持 Markdown 和 Frontmatter)",
539
+ },
449
540
  },
450
541
  required: ["path", "content"],
451
542
  },
@@ -479,7 +570,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
479
570
  inputSchema: {
480
571
  type: "object",
481
572
  properties: {
482
- path: { type: "string", description: "文件夹的相对路径(如 知识点/04-人工智能/MCP/新文件夹)" },
573
+ path: {
574
+ type: "string",
575
+ description: "文件夹的相对路径(如 知识点/04-人工智能/MCP/新文件夹)",
576
+ },
483
577
  },
484
578
  required: ["path"],
485
579
  },
@@ -492,6 +586,67 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
492
586
  properties: {},
493
587
  },
494
588
  },
589
+ {
590
+ name: "git_commit",
591
+ description: "按照规范格式提交 Git 更改。格式: <emoji> <type>: <description>",
592
+ inputSchema: {
593
+ type: "object",
594
+ properties: {
595
+ type: {
596
+ type: "string",
597
+ description: "提交类型: feat(新功能), fix(Bug修复), docs(文档), style(格式), refactor(重构), perf(性能), test(测试), build(构建), ci(CI/CD), chore(杂项), revert(回滚)",
598
+ enum: [
599
+ "feat",
600
+ "fix",
601
+ "docs",
602
+ "style",
603
+ "refactor",
604
+ "perf",
605
+ "test",
606
+ "build",
607
+ "ci",
608
+ "chore",
609
+ "revert",
610
+ ],
611
+ },
612
+ description: {
613
+ type: "string",
614
+ description: "提交描述(简洁明了,使用祈使语气)",
615
+ },
616
+ files: {
617
+ type: "array",
618
+ items: { type: "string" },
619
+ description: "要提交的文件列表(可选,留空则提交所有更改)",
620
+ },
621
+ cwd: {
622
+ type: "string",
623
+ description: "工作目录(可选,默认为笔记库路径)",
624
+ },
625
+ },
626
+ required: ["type", "description"],
627
+ },
628
+ },
629
+ {
630
+ name: "git_status",
631
+ description: "获取 Git 工作区状态",
632
+ inputSchema: {
633
+ type: "object",
634
+ properties: {
635
+ cwd: {
636
+ type: "string",
637
+ description: "工作目录(可选,默认为笔记库路径)",
638
+ },
639
+ },
640
+ },
641
+ },
642
+ {
643
+ name: "git_commit_types",
644
+ description: "获取所有可用的 Git 提交类型及其说明",
645
+ inputSchema: {
646
+ type: "object",
647
+ properties: {},
648
+ },
649
+ },
495
650
  ],
496
651
  }));
497
652
  // 处理工具调用
@@ -559,6 +714,24 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
559
714
  content: [{ type: "text", text: guide }],
560
715
  };
561
716
  }
717
+ case "git_commit": {
718
+ const result = await gitCommit(args?.type, args?.description, args?.files, args?.cwd);
719
+ return {
720
+ content: [{ type: "text", text: result }],
721
+ };
722
+ }
723
+ case "git_status": {
724
+ const status = await gitStatus(args?.cwd);
725
+ return {
726
+ content: [{ type: "text", text: status }],
727
+ };
728
+ }
729
+ case "git_commit_types": {
730
+ const types = getCommitTypes();
731
+ return {
732
+ content: [{ type: "text", text: types }],
733
+ };
734
+ }
562
735
  default:
563
736
  throw new Error(`未知工具: ${name}`);
564
737
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fromsko/obsidian-mcp-server",
3
- "version": "1.1.7",
3
+ "version": "1.1.8",
4
4
  "description": "MCP server for Obsidian notes - search and read your Obsidian vault",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",