obsidian-mcp-server 1.3.0 → 1.4.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 (77) hide show
  1. package/build/index.js +12 -3
  2. package/build/mcp/handlers.js +138 -0
  3. package/build/mcp/index.js +7 -0
  4. package/build/mcp/server.js +131 -0
  5. package/build/mcp/types.js +7 -0
  6. package/build/{obsidian.js → obsidian/client.js} +116 -96
  7. package/build/obsidian/errors.js +75 -0
  8. package/build/obsidian/index.js +7 -0
  9. package/build/obsidian/types.js +12 -0
  10. package/build/resources/index.js +15 -0
  11. package/build/{resources.js → resources/tags.js} +31 -5
  12. package/build/resources/types.js +5 -0
  13. package/build/tools/base.js +78 -0
  14. package/build/tools/files/content.js +171 -0
  15. package/build/tools/files/index.js +22 -0
  16. package/build/tools/files/list.js +133 -0
  17. package/build/tools/index.js +31 -0
  18. package/build/tools/properties/index.js +19 -0
  19. package/build/{properties.js → tools/properties/manager.js} +40 -8
  20. package/build/{propertyTools.js → tools/properties/tools.js} +22 -6
  21. package/build/{propertyTypes.js → tools/properties/types.js} +17 -5
  22. package/build/tools/search/complex.js +203 -0
  23. package/build/tools/search/index.js +20 -0
  24. package/build/tools/search/simple.js +127 -0
  25. package/build/utils/errors.js +59 -0
  26. package/build/utils/index.js +9 -0
  27. package/build/utils/logging.js +119 -0
  28. package/build/utils/rate-limiting.js +94 -0
  29. package/build/utils/tokenization.js +62 -0
  30. package/build/utils/validation.js +88 -0
  31. package/examples/README.md +48 -0
  32. package/examples/append-content.md +63 -0
  33. package/examples/complex-search.md +117 -0
  34. package/examples/find-in-file.md +94 -0
  35. package/examples/get-file-contents.md +72 -0
  36. package/examples/get-properties.md +89 -0
  37. package/examples/list-files-in-dir.md +55 -0
  38. package/examples/list-files-in-vault.md +53 -0
  39. package/examples/patch-content.md +60 -0
  40. package/examples/update-properties.md +126 -0
  41. package/package.json +1 -1
  42. package/src/index.ts +13 -3
  43. package/src/mcp/handlers.ts +183 -0
  44. package/src/mcp/index.ts +6 -0
  45. package/src/mcp/server.ts +162 -0
  46. package/src/mcp/types.ts +46 -0
  47. package/src/{obsidian.ts → obsidian/client.ts} +132 -125
  48. package/src/obsidian/errors.ts +105 -0
  49. package/src/obsidian/index.ts +6 -0
  50. package/src/obsidian/types.ts +124 -0
  51. package/src/resources/index.ts +17 -0
  52. package/src/{resources.ts → resources/tags.ts} +38 -6
  53. package/src/resources/types.ts +30 -0
  54. package/src/tools/base.ts +112 -0
  55. package/src/tools/files/content.ts +206 -0
  56. package/src/tools/files/index.ts +31 -0
  57. package/src/tools/files/list.ts +150 -0
  58. package/src/tools/index.ts +38 -0
  59. package/src/tools/properties/index.ts +21 -0
  60. package/src/{properties.ts → tools/properties/manager.ts} +41 -8
  61. package/src/{propertyTools.ts → tools/properties/tools.ts} +33 -7
  62. package/src/{propertyTypes.ts → tools/properties/types.ts} +29 -5
  63. package/src/tools/search/complex.ts +231 -0
  64. package/src/tools/search/index.ts +22 -0
  65. package/src/tools/search/simple.ts +147 -0
  66. package/src/utils/errors.ts +69 -0
  67. package/src/utils/index.ts +8 -0
  68. package/src/utils/logging.ts +146 -0
  69. package/src/utils/rate-limiting.ts +114 -0
  70. package/src/utils/tokenization.ts +71 -0
  71. package/src/utils/validation.ts +95 -0
  72. package/build/server.js +0 -263
  73. package/build/tools.js +0 -845
  74. package/build/types.js +0 -37
  75. package/src/server.ts +0 -335
  76. package/src/tools.ts +0 -926
  77. package/src/types.ts +0 -184
@@ -0,0 +1,78 @@
1
+ import { ObsidianError } from "../utils/errors.js";
2
+ import { tokenCounter } from "../utils/tokenization.js";
3
+ import { createLogger } from "../utils/logging.js";
4
+ // Create a logger for tool operations
5
+ const logger = createLogger('Tools');
6
+ /**
7
+ * Base class for all tool handlers with common functionality
8
+ */
9
+ export class BaseToolHandler {
10
+ name;
11
+ client;
12
+ /**
13
+ * Create a new tool handler
14
+ * @param name The name of the tool
15
+ * @param client The ObsidianClient instance
16
+ */
17
+ constructor(name, client) {
18
+ this.name = name;
19
+ this.client = client;
20
+ }
21
+ /**
22
+ * Create a standardized response from any content type
23
+ * @param content The content to format for response
24
+ * @returns An array of TextContent objects
25
+ */
26
+ createResponse(content) {
27
+ let text;
28
+ // Handle different content types
29
+ if (typeof content === 'string') {
30
+ text = content;
31
+ }
32
+ else if (content instanceof Buffer) {
33
+ text = content.toString('utf-8');
34
+ }
35
+ else if (Array.isArray(content) && content.every(item => typeof item === 'string')) {
36
+ text = content.join('\n');
37
+ }
38
+ else if (content instanceof Error) {
39
+ text = `Error: ${content.message}\n${content.stack || ''}`;
40
+ }
41
+ else {
42
+ try {
43
+ text = JSON.stringify(content, null, 2);
44
+ }
45
+ catch (error) {
46
+ text = String(content);
47
+ }
48
+ }
49
+ // Count tokens and truncate if necessary
50
+ const originalTokenCount = tokenCounter.countTokens(text);
51
+ const truncatedText = tokenCounter.truncateToTokenLimit(text);
52
+ const finalTokenCount = tokenCounter.countTokens(truncatedText);
53
+ if (originalTokenCount > finalTokenCount) {
54
+ logger.debug(`[${this.name}] Response truncated:`, `original tokens=${originalTokenCount}`, `truncated tokens=${finalTokenCount}`);
55
+ }
56
+ return [{
57
+ type: "text",
58
+ text: truncatedText
59
+ }];
60
+ }
61
+ /**
62
+ * Standard error handling for tool execution
63
+ * @param error The error to handle
64
+ * @throws ObsidianError
65
+ */
66
+ handleError(error) {
67
+ if (error instanceof ObsidianError) {
68
+ throw error;
69
+ }
70
+ if (error instanceof Error) {
71
+ throw new ObsidianError(`Tool '${this.name}' execution failed: ${error.message}`, 50000, // Internal server error
72
+ { originalError: error.stack });
73
+ }
74
+ throw new ObsidianError(`Tool '${this.name}' execution failed with unknown error`, 50000, // Internal server error
75
+ { error });
76
+ }
77
+ }
78
+ //# sourceMappingURL=base.js.map
@@ -0,0 +1,171 @@
1
+ import { BaseToolHandler } from "../base.js";
2
+ import { createLogger } from "../../utils/logging.js";
3
+ // Create a logger for file content operations
4
+ const logger = createLogger('FileContentTools');
5
+ /**
6
+ * Tool names for file content operations
7
+ */
8
+ export const FILE_CONTENT_TOOL_NAMES = {
9
+ GET_FILE_CONTENTS: "obsidian_get_file_contents",
10
+ APPEND_CONTENT: "obsidian_append_content",
11
+ PATCH_CONTENT: "obsidian_patch_content"
12
+ };
13
+ /**
14
+ * Tool handler for getting file contents
15
+ */
16
+ export class GetFileContentsToolHandler extends BaseToolHandler {
17
+ constructor(client) {
18
+ super(FILE_CONTENT_TOOL_NAMES.GET_FILE_CONTENTS, client);
19
+ }
20
+ getToolDescription() {
21
+ return {
22
+ name: this.name,
23
+ description: "Return the content of a single file in your vault. Supports markdown files, text files, and other readable formats. Returns the raw content including any YAML frontmatter.",
24
+ examples: [
25
+ {
26
+ description: "Get content of a markdown note",
27
+ args: {
28
+ filepath: "Projects/research.md"
29
+ }
30
+ },
31
+ {
32
+ description: "Get content of a configuration file",
33
+ args: {
34
+ filepath: "configs/settings.yml"
35
+ }
36
+ }
37
+ ],
38
+ inputSchema: {
39
+ type: "object",
40
+ properties: {
41
+ filepath: {
42
+ type: "string",
43
+ description: "Path to the relevant file (relative to your vault root).",
44
+ format: "path"
45
+ }
46
+ },
47
+ required: ["filepath"]
48
+ }
49
+ };
50
+ }
51
+ async runTool(args) {
52
+ try {
53
+ logger.debug(`Getting contents of file: ${args.filepath}`);
54
+ const content = await this.client.getFileContents(args.filepath);
55
+ return this.createResponse(content);
56
+ }
57
+ catch (error) {
58
+ return this.handleError(error);
59
+ }
60
+ }
61
+ }
62
+ /**
63
+ * Tool handler for appending content to a file
64
+ */
65
+ export class AppendContentToolHandler extends BaseToolHandler {
66
+ constructor(client) {
67
+ super(FILE_CONTENT_TOOL_NAMES.APPEND_CONTENT, client);
68
+ }
69
+ getToolDescription() {
70
+ return {
71
+ name: this.name,
72
+ description: "Append content to a new or existing file in the vault.",
73
+ examples: [
74
+ {
75
+ description: "Append a new task",
76
+ args: {
77
+ filepath: "tasks.md",
78
+ content: "- [ ] New task to complete"
79
+ }
80
+ },
81
+ {
82
+ description: "Append meeting notes",
83
+ args: {
84
+ filepath: "meetings/2025-01-23.md",
85
+ content: "## Meeting Notes\n\n- Discussed project timeline\n- Assigned tasks"
86
+ }
87
+ }
88
+ ],
89
+ inputSchema: {
90
+ type: "object",
91
+ properties: {
92
+ filepath: {
93
+ type: "string",
94
+ description: "Path to the file (relative to vault root)",
95
+ format: "path"
96
+ },
97
+ content: {
98
+ type: "string",
99
+ description: "Content to append to the file"
100
+ }
101
+ },
102
+ required: ["filepath", "content"]
103
+ }
104
+ };
105
+ }
106
+ async runTool(args) {
107
+ try {
108
+ logger.debug(`Appending content to file: ${args.filepath}`);
109
+ await this.client.appendContent(args.filepath, args.content);
110
+ return this.createResponse({
111
+ message: `Successfully appended content to ${args.filepath}`,
112
+ success: true
113
+ });
114
+ }
115
+ catch (error) {
116
+ return this.handleError(error);
117
+ }
118
+ }
119
+ }
120
+ /**
121
+ * Tool handler for updating file content
122
+ */
123
+ export class PatchContentToolHandler extends BaseToolHandler {
124
+ constructor(client) {
125
+ super(FILE_CONTENT_TOOL_NAMES.PATCH_CONTENT, client);
126
+ }
127
+ getToolDescription() {
128
+ return {
129
+ name: this.name,
130
+ description: "Update the entire content of an existing note or create a new one.",
131
+ examples: [
132
+ {
133
+ description: "Update a note's content",
134
+ args: {
135
+ filepath: "project.md",
136
+ content: "# Project Notes\n\nThis will replace the entire content of the note."
137
+ }
138
+ }
139
+ ],
140
+ inputSchema: {
141
+ type: "object",
142
+ properties: {
143
+ filepath: {
144
+ type: "string",
145
+ description: "Path to the file (relative to vault root)",
146
+ format: "path"
147
+ },
148
+ content: {
149
+ type: "string",
150
+ description: "New content for the note (replaces existing content)"
151
+ }
152
+ },
153
+ required: ["filepath", "content"]
154
+ }
155
+ };
156
+ }
157
+ async runTool(args) {
158
+ try {
159
+ logger.debug(`Updating content of file: ${args.filepath}`);
160
+ await this.client.updateContent(args.filepath, args.content);
161
+ return this.createResponse({
162
+ message: `Successfully updated content in ${args.filepath}`,
163
+ success: true
164
+ });
165
+ }
166
+ catch (error) {
167
+ return this.handleError(error);
168
+ }
169
+ }
170
+ }
171
+ //# sourceMappingURL=content.js.map
@@ -0,0 +1,22 @@
1
+ /**
2
+ * File operation tools exports
3
+ */
4
+ export * from './list.js';
5
+ export * from './content.js';
6
+ import { ListFilesInVaultToolHandler, ListFilesInDirToolHandler } from './list.js';
7
+ import { GetFileContentsToolHandler, AppendContentToolHandler, PatchContentToolHandler } from './content.js';
8
+ /**
9
+ * Create all file-related tool handlers
10
+ * @param client The ObsidianClient instance
11
+ * @returns Array of file tool handlers
12
+ */
13
+ export function createFileToolHandlers(client) {
14
+ return [
15
+ new ListFilesInVaultToolHandler(client),
16
+ new ListFilesInDirToolHandler(client),
17
+ new GetFileContentsToolHandler(client),
18
+ new AppendContentToolHandler(client),
19
+ new PatchContentToolHandler(client)
20
+ ];
21
+ }
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,133 @@
1
+ import { BaseToolHandler } from "../base.js";
2
+ import { createLogger } from "../../utils/logging.js";
3
+ // Create a logger for file operations
4
+ const logger = createLogger('FileTools');
5
+ /**
6
+ * Tool names for file listing operations
7
+ */
8
+ export const FILE_TOOL_NAMES = {
9
+ LIST_FILES_IN_VAULT: "obsidian_list_files_in_vault",
10
+ LIST_FILES_IN_DIR: "obsidian_list_files_in_dir"
11
+ };
12
+ /**
13
+ * Tool handler for listing all files in the vault
14
+ */
15
+ export class ListFilesInVaultToolHandler extends BaseToolHandler {
16
+ constructor(client) {
17
+ super(FILE_TOOL_NAMES.LIST_FILES_IN_VAULT, client);
18
+ }
19
+ getToolDescription() {
20
+ return {
21
+ name: this.name,
22
+ description: "Lists all files and directories in the root directory of your Obsidian vault. Returns a hierarchical structure of files and folders, including metadata like file type.",
23
+ examples: [
24
+ {
25
+ description: "List all files in vault",
26
+ args: {}
27
+ },
28
+ {
29
+ description: "Example response",
30
+ args: {},
31
+ response: [
32
+ {
33
+ "path": "Daily Notes",
34
+ "type": "folder",
35
+ "children": [
36
+ { "path": "Daily Notes/2025-01-24.md", "type": "file" }
37
+ ]
38
+ },
39
+ {
40
+ "path": "Projects",
41
+ "type": "folder",
42
+ "children": [
43
+ { "path": "Projects/MCP.md", "type": "file" }
44
+ ]
45
+ }
46
+ ]
47
+ }
48
+ ],
49
+ inputSchema: {
50
+ type: "object",
51
+ properties: {},
52
+ required: []
53
+ }
54
+ };
55
+ }
56
+ async runTool() {
57
+ try {
58
+ logger.debug('Listing all files in vault');
59
+ const files = await this.client.listFilesInVault();
60
+ return this.createResponse(files);
61
+ }
62
+ catch (error) {
63
+ return this.handleError(error);
64
+ }
65
+ }
66
+ }
67
+ /**
68
+ * Tool handler for listing files in a specific directory
69
+ */
70
+ export class ListFilesInDirToolHandler extends BaseToolHandler {
71
+ constructor(client) {
72
+ super(FILE_TOOL_NAMES.LIST_FILES_IN_DIR, client);
73
+ }
74
+ getToolDescription() {
75
+ return {
76
+ name: this.name,
77
+ description: "Lists all files and directories that exist in a specific Obsidian directory. Returns a hierarchical structure showing files, folders, and their relationships. Useful for exploring vault organization and finding specific files.",
78
+ examples: [
79
+ {
80
+ description: "List files in Documents folder",
81
+ args: {
82
+ dirpath: "Documents"
83
+ }
84
+ },
85
+ {
86
+ description: "Example response structure",
87
+ args: {
88
+ dirpath: "Projects"
89
+ },
90
+ response: [
91
+ {
92
+ "path": "Projects/Active",
93
+ "type": "folder",
94
+ "children": [
95
+ { "path": "Projects/Active/ProjectA.md", "type": "file" },
96
+ { "path": "Projects/Active/ProjectB.md", "type": "file" }
97
+ ]
98
+ },
99
+ {
100
+ "path": "Projects/Archive",
101
+ "type": "folder",
102
+ "children": [
103
+ { "path": "Projects/Archive/OldProject.md", "type": "file" }
104
+ ]
105
+ }
106
+ ]
107
+ }
108
+ ],
109
+ inputSchema: {
110
+ type: "object",
111
+ properties: {
112
+ dirpath: {
113
+ type: "string",
114
+ description: "Path to list files from (relative to your vault root). Note that empty directories will not be returned.",
115
+ format: "path"
116
+ }
117
+ },
118
+ required: ["dirpath"]
119
+ }
120
+ };
121
+ }
122
+ async runTool(args) {
123
+ try {
124
+ logger.debug(`Listing files in directory: ${args.dirpath}`);
125
+ const files = await this.client.listFilesInDir(args.dirpath);
126
+ return this.createResponse(files);
127
+ }
128
+ catch (error) {
129
+ return this.handleError(error);
130
+ }
131
+ }
132
+ }
133
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1,31 @@
1
+ import { createFileToolHandlers } from './files/index.js';
2
+ import { createSearchToolHandlers } from './search/index.js';
3
+ import { createPropertyToolHandlers } from './properties/index.js';
4
+ // Export the base handler and all submodules
5
+ export * from './base.js';
6
+ export * from './files/index.js';
7
+ export * from './search/index.js';
8
+ export * from './properties/index.js';
9
+ /**
10
+ * Create all tool handlers
11
+ * @param client The ObsidianClient instance
12
+ * @returns Array of all tool handlers
13
+ */
14
+ export function createToolHandlers(client) {
15
+ return [
16
+ ...createFileToolHandlers(client),
17
+ ...createSearchToolHandlers(client),
18
+ ...createPropertyToolHandlers(client)
19
+ ];
20
+ }
21
+ /**
22
+ * Get a tool handler map by name
23
+ * @param handlers Array of tool handlers
24
+ * @returns Map of tool names to handlers
25
+ */
26
+ export function createToolHandlerMap(handlers) {
27
+ const toolHandlerMap = new Map();
28
+ handlers.forEach(handler => toolHandlerMap.set(handler.name, handler));
29
+ return toolHandlerMap;
30
+ }
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Properties module exports
3
+ */
4
+ export * from './types.js';
5
+ export * from './manager.js';
6
+ export * from './tools.js';
7
+ import { GetPropertiesToolHandler, UpdatePropertiesToolHandler } from './tools.js';
8
+ /**
9
+ * Create all property-related tool handlers
10
+ * @param client The ObsidianClient instance
11
+ * @returns Array of property tool handlers
12
+ */
13
+ export function createPropertyToolHandlers(client) {
14
+ return [
15
+ new GetPropertiesToolHandler(client),
16
+ new UpdatePropertiesToolHandler(client)
17
+ ];
18
+ }
19
+ //# sourceMappingURL=index.js.map
@@ -1,6 +1,15 @@
1
+ /**
2
+ * Property manager for Obsidian notes
3
+ */
1
4
  import { parse, stringify } from 'yaml';
2
5
  import { EOL } from 'os';
3
- import { ObsidianPropertiesSchema, PropertyUpdateSchema } from './propertyTypes.js';
6
+ import { createLogger } from '../../utils/logging.js';
7
+ import { ObsidianPropertiesSchema, PropertyUpdateSchema } from './types.js';
8
+ // Create a logger for property operations
9
+ const logger = createLogger('PropertyManager');
10
+ /**
11
+ * Manages YAML frontmatter properties in Obsidian notes
12
+ */
4
13
  export class PropertyManager {
5
14
  client;
6
15
  constructor(client) {
@@ -8,15 +17,18 @@ export class PropertyManager {
8
17
  }
9
18
  /**
10
19
  * Parse YAML frontmatter from note content
20
+ * @param content The note content
21
+ * @returns Extracted properties
11
22
  */
12
23
  parseProperties(content) {
13
24
  try {
14
25
  // Extract frontmatter between --- markers (handles both \n and \r\n)
15
- const match = content.match(/^---(\r?\n)([\s\S]*?)\r?\n---/);
26
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
16
27
  if (!match) {
28
+ logger.debug('No frontmatter found in content');
17
29
  return {};
18
30
  }
19
- const frontmatter = match[2];
31
+ const frontmatter = match[1];
20
32
  const properties = parse(frontmatter);
21
33
  // Handle tags - don't add # prefix in frontmatter
22
34
  if (properties.tags && Array.isArray(properties.tags)) {
@@ -25,19 +37,21 @@ export class PropertyManager {
25
37
  // Validate against schema
26
38
  const result = ObsidianPropertiesSchema.safeParse(properties);
27
39
  if (!result.success) {
28
- console.warn('Property validation warnings:', result.error);
40
+ logger.warn('Property validation warnings:', result.error);
29
41
  // Return the properties with fixed tags
30
42
  return properties;
31
43
  }
32
44
  return result.data;
33
45
  }
34
46
  catch (error) {
35
- console.error('Error parsing properties:', error);
47
+ logger.error('Error parsing properties:', error);
36
48
  return {};
37
49
  }
38
50
  }
39
51
  /**
40
52
  * Generate YAML frontmatter from properties
53
+ * @param properties The properties to convert to YAML
54
+ * @returns YAML frontmatter string
41
55
  */
42
56
  generateProperties(properties) {
43
57
  try {
@@ -48,12 +62,14 @@ export class PropertyManager {
48
62
  return `---${EOL}${yaml}---${EOL}`;
49
63
  }
50
64
  catch (error) {
51
- console.error('Error generating properties:', error);
65
+ logger.error('Error generating properties:', error);
52
66
  throw error;
53
67
  }
54
68
  }
55
69
  /**
56
- * Validate property values
70
+ * Validate property values against schema
71
+ * @param properties The properties to validate
72
+ * @returns Validation result
57
73
  */
58
74
  validateProperties(properties) {
59
75
  const result = PropertyUpdateSchema.safeParse(properties);
@@ -67,6 +83,10 @@ export class PropertyManager {
67
83
  }
68
84
  /**
69
85
  * Merge new properties with existing ones
86
+ * @param existing The existing properties
87
+ * @param updates The new properties to merge
88
+ * @param replace Whether to replace arrays instead of merging them
89
+ * @returns The merged properties
70
90
  */
71
91
  mergeProperties(existing, updates, replace = false) {
72
92
  const merged = { ...existing };
@@ -99,9 +119,12 @@ export class PropertyManager {
99
119
  }
100
120
  /**
101
121
  * Get properties from a note
122
+ * @param filepath Path to the note
123
+ * @returns The properties from the note
102
124
  */
103
125
  async getProperties(filepath) {
104
126
  try {
127
+ logger.debug(`Getting properties from file: ${filepath}`);
105
128
  const content = await this.client.getFileContents(filepath);
106
129
  const properties = this.parseProperties(content);
107
130
  return {
@@ -111,6 +134,7 @@ export class PropertyManager {
111
134
  };
112
135
  }
113
136
  catch (error) {
137
+ logger.error(`Failed to get properties from ${filepath}:`, error);
114
138
  return {
115
139
  success: false,
116
140
  message: `Failed to get properties: ${error instanceof Error ? error.message : String(error)}`,
@@ -120,12 +144,17 @@ export class PropertyManager {
120
144
  }
121
145
  /**
122
146
  * Update properties of a note
147
+ * @param filepath Path to the note
148
+ * @param newProperties The new properties to apply
149
+ * @param replace Whether to replace arrays instead of merging them
150
+ * @returns The result of the update operation
123
151
  */
124
152
  async updateProperties(filepath, newProperties, replace = false) {
125
153
  try {
126
154
  // Validate new properties
127
155
  const validation = this.validateProperties(newProperties);
128
156
  if (!validation.valid) {
157
+ logger.warn(`Invalid properties for ${filepath}:`, validation.errors);
129
158
  return {
130
159
  success: false,
131
160
  message: 'Invalid properties',
@@ -133,6 +162,7 @@ export class PropertyManager {
133
162
  };
134
163
  }
135
164
  // Get existing content and properties
165
+ logger.debug(`Updating properties for file: ${filepath}`);
136
166
  const content = await this.client.getFileContents(filepath);
137
167
  const existingProperties = this.parseProperties(content);
138
168
  // Merge properties
@@ -144,6 +174,7 @@ export class PropertyManager {
144
174
  const updatedContent = newFrontmatter + newContent;
145
175
  // Update file
146
176
  await this.client.updateContent(filepath, updatedContent);
177
+ logger.debug(`Successfully updated properties for ${filepath}`);
147
178
  return {
148
179
  success: true,
149
180
  message: 'Properties updated successfully',
@@ -151,6 +182,7 @@ export class PropertyManager {
151
182
  };
152
183
  }
153
184
  catch (error) {
185
+ logger.error(`Failed to update properties for ${filepath}:`, error);
154
186
  return {
155
187
  success: false,
156
188
  message: `Failed to update properties: ${error instanceof Error ? error.message : String(error)}`,
@@ -159,4 +191,4 @@ export class PropertyManager {
159
191
  }
160
192
  }
161
193
  }
162
- //# sourceMappingURL=properties.js.map
194
+ //# sourceMappingURL=manager.js.map
@@ -1,13 +1,22 @@
1
- import { BaseToolHandler } from "./tools.js";
2
- import { PropertyManager } from "./properties.js";
3
- const TOOL_NAMES = {
1
+ import { BaseToolHandler } from "../base.js";
2
+ import { PropertyManager } from "./manager.js";
3
+ import { createLogger } from "../../utils/logging.js";
4
+ // Create a logger for property tools
5
+ const logger = createLogger('PropertyTools');
6
+ /**
7
+ * Tool names for property operations
8
+ */
9
+ export const PROPERTY_TOOL_NAMES = {
4
10
  GET_PROPERTIES: "obsidian_get_properties",
5
11
  UPDATE_PROPERTIES: "obsidian_update_properties"
6
12
  };
13
+ /**
14
+ * Tool handler for getting properties from a note
15
+ */
7
16
  export class GetPropertiesToolHandler extends BaseToolHandler {
8
17
  propertyManager;
9
18
  constructor(client) {
10
- super(TOOL_NAMES.GET_PROPERTIES, client);
19
+ super(PROPERTY_TOOL_NAMES.GET_PROPERTIES, client);
11
20
  this.propertyManager = new PropertyManager(client);
12
21
  }
13
22
  getToolDescription() {
@@ -43,18 +52,23 @@ export class GetPropertiesToolHandler extends BaseToolHandler {
43
52
  }
44
53
  async runTool(args) {
45
54
  try {
55
+ logger.debug(`Getting properties from: ${args.filepath}`);
46
56
  const result = await this.propertyManager.getProperties(args.filepath);
47
57
  return this.createResponse(result);
48
58
  }
49
59
  catch (error) {
60
+ logger.error(`Error getting properties from ${args.filepath}:`, error);
50
61
  return this.handleError(error);
51
62
  }
52
63
  }
53
64
  }
65
+ /**
66
+ * Tool handler for updating properties in a note
67
+ */
54
68
  export class UpdatePropertiesToolHandler extends BaseToolHandler {
55
69
  propertyManager;
56
70
  constructor(client) {
57
- super(TOOL_NAMES.UPDATE_PROPERTIES, client);
71
+ super(PROPERTY_TOOL_NAMES.UPDATE_PROPERTIES, client);
58
72
  this.propertyManager = new PropertyManager(client);
59
73
  }
60
74
  getToolDescription() {
@@ -164,12 +178,14 @@ export class UpdatePropertiesToolHandler extends BaseToolHandler {
164
178
  }
165
179
  async runTool(args) {
166
180
  try {
181
+ logger.debug(`Updating properties for: ${args.filepath}`);
167
182
  const result = await this.propertyManager.updateProperties(args.filepath, args.properties, args.replace);
168
183
  return this.createResponse(result);
169
184
  }
170
185
  catch (error) {
186
+ logger.error(`Error updating properties for ${args.filepath}:`, error);
171
187
  return this.handleError(error);
172
188
  }
173
189
  }
174
190
  }
175
- //# sourceMappingURL=propertyTools.js.map
191
+ //# sourceMappingURL=tools.js.map