obsidian-mcp-server 1.2.1 → 1.2.3
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.md +18 -30
- package/build/properties.js +5 -4
- package/build/propertyTools.js +2 -3
- package/build/propertyTypes.js +25 -2
- package/package.json +1 -1
- package/src/properties.ts +10 -8
- package/src/propertyTools.ts +2 -3
- package/src/propertyTypes.ts +32 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.typescriptlang.org/)
|
|
4
4
|
[](https://modelcontextprotocol.io/)
|
|
5
|
-
[]()
|
|
6
6
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
7
7
|
[]()
|
|
8
8
|
[](https://github.com/cyanheads/obsidian-mcp-server)
|
|
@@ -16,25 +16,21 @@ Requires the Local REST API plugin in Obsidian.
|
|
|
16
16
|
## Features
|
|
17
17
|
|
|
18
18
|
### File Operations
|
|
19
|
-
-
|
|
20
|
-
- Content read/write operations with validation
|
|
19
|
+
- Atomic file/directory operations with validation
|
|
21
20
|
- Resource monitoring and cleanup
|
|
22
21
|
|
|
23
22
|
### Search System
|
|
24
|
-
- Full-text and JsonLogic
|
|
25
|
-
-
|
|
26
|
-
- Optimized query processing
|
|
23
|
+
- Full-text and JsonLogic search with context control
|
|
24
|
+
- Optimized query processing with token limits
|
|
27
25
|
|
|
28
26
|
### Property Management
|
|
29
|
-
- YAML frontmatter parsing and
|
|
30
|
-
-
|
|
31
|
-
- Automatic timestamp management
|
|
27
|
+
- YAML frontmatter parsing and intelligent merging
|
|
28
|
+
- Automatic timestamps (created by Obsidian, modified by server)
|
|
32
29
|
- Custom field support
|
|
33
30
|
|
|
34
31
|
### Security & Performance
|
|
35
|
-
- API key
|
|
36
|
-
-
|
|
37
|
-
- Resource management and health monitoring
|
|
32
|
+
- API key auth with rate limiting and SSL options
|
|
33
|
+
- Resource monitoring and health checks
|
|
38
34
|
|
|
39
35
|
## Installation
|
|
40
36
|
|
|
@@ -138,9 +134,8 @@ obsidian_update_properties: {
|
|
|
138
134
|
filepath: string, // Path relative to vault root
|
|
139
135
|
properties: {
|
|
140
136
|
title?: string,
|
|
141
|
-
created?: string, // ISO date
|
|
142
|
-
modified?: string, // ISO date (auto-updated)
|
|
143
137
|
author?: string,
|
|
138
|
+
// Note: created/modified timestamps are managed automatically
|
|
144
139
|
type?: Array<"concept" | "architecture" | "specification" |
|
|
145
140
|
"protocol" | "api" | "research" | "implementation" |
|
|
146
141
|
"guide" | "reference">,
|
|
@@ -161,28 +156,21 @@ obsidian_update_properties: {
|
|
|
161
156
|
## Best Practices
|
|
162
157
|
|
|
163
158
|
### File Operations
|
|
164
|
-
- Use atomic operations
|
|
165
|
-
-
|
|
166
|
-
- Implement proper error handling
|
|
167
|
-
- Monitor operation performance
|
|
159
|
+
- Use atomic operations with validation
|
|
160
|
+
- Handle errors and monitor performance
|
|
168
161
|
|
|
169
162
|
### Search Implementation
|
|
170
|
-
- Optimize
|
|
171
|
-
-
|
|
172
|
-
- Handle large result sets
|
|
173
|
-
- Consider token limits
|
|
163
|
+
- Optimize queries and control context size
|
|
164
|
+
- Handle large results within token limits
|
|
174
165
|
|
|
175
166
|
### Property Management
|
|
176
|
-
-
|
|
177
|
-
-
|
|
178
|
-
-
|
|
179
|
-
- Consider custom field implications
|
|
167
|
+
- Use appropriate types and validate updates
|
|
168
|
+
- Handle arrays and custom fields properly
|
|
169
|
+
- Never set timestamps (managed automatically)
|
|
180
170
|
|
|
181
171
|
### Error Prevention
|
|
182
|
-
- Validate inputs
|
|
183
|
-
-
|
|
184
|
-
- Monitor error patterns
|
|
185
|
-
- Check rate limits
|
|
172
|
+
- Validate inputs and handle errors gracefully
|
|
173
|
+
- Monitor patterns and respect rate limits
|
|
186
174
|
|
|
187
175
|
## Contributing
|
|
188
176
|
|
package/build/properties.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parse, stringify } from 'yaml';
|
|
2
|
-
import { ObsidianPropertiesSchema } from './propertyTypes.js';
|
|
2
|
+
import { ObsidianPropertiesSchema, PropertyUpdateSchema } from './propertyTypes.js';
|
|
3
3
|
export class PropertyManager {
|
|
4
4
|
client;
|
|
5
5
|
constructor(client) {
|
|
@@ -51,7 +51,7 @@ export class PropertyManager {
|
|
|
51
51
|
* Validate property values
|
|
52
52
|
*/
|
|
53
53
|
validateProperties(properties) {
|
|
54
|
-
const result =
|
|
54
|
+
const result = PropertyUpdateSchema.safeParse(properties);
|
|
55
55
|
if (result.success) {
|
|
56
56
|
return { valid: true, errors: [] };
|
|
57
57
|
}
|
|
@@ -66,7 +66,8 @@ export class PropertyManager {
|
|
|
66
66
|
mergeProperties(existing, updates) {
|
|
67
67
|
const merged = { ...existing };
|
|
68
68
|
for (const [key, value] of Object.entries(updates)) {
|
|
69
|
-
|
|
69
|
+
// Skip undefined values and timestamp fields
|
|
70
|
+
if (value === undefined || key === 'created' || key === 'modified')
|
|
70
71
|
continue;
|
|
71
72
|
const currentValue = merged[key];
|
|
72
73
|
// Special handling for arrays - merge rather than replace
|
|
@@ -87,7 +88,7 @@ export class PropertyManager {
|
|
|
87
88
|
merged[key] = value;
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
|
-
// Always update modified date
|
|
91
|
+
// Always update modified date (this is the only place we set it)
|
|
91
92
|
merged.modified = new Date().toISOString();
|
|
92
93
|
return merged;
|
|
93
94
|
}
|
package/build/propertyTools.js
CHANGED
|
@@ -60,7 +60,7 @@ export class UpdatePropertiesToolHandler extends BaseToolHandler {
|
|
|
60
60
|
getToolDescription() {
|
|
61
61
|
return {
|
|
62
62
|
name: this.name,
|
|
63
|
-
description: "Update properties in an Obsidian note's YAML frontmatter. Intelligently merges arrays (tags, type, status), handles custom fields, and automatically
|
|
63
|
+
description: "Update properties in an Obsidian note's YAML frontmatter. Intelligently merges arrays (tags, type, status), handles custom fields, and automatically manages timestamps (created by Obsidian, modified by MCP server). Existing properties not included in the update are preserved.",
|
|
64
64
|
examples: [
|
|
65
65
|
{
|
|
66
66
|
description: "Update basic metadata",
|
|
@@ -111,9 +111,8 @@ export class UpdatePropertiesToolHandler extends BaseToolHandler {
|
|
|
111
111
|
description: "Properties to update",
|
|
112
112
|
properties: {
|
|
113
113
|
title: { type: "string" },
|
|
114
|
-
created: { type: "string", format: "date-time" },
|
|
115
|
-
modified: { type: "string", format: "date-time" },
|
|
116
114
|
author: { type: "string" },
|
|
115
|
+
// Note: created and modified timestamps are managed automatically
|
|
117
116
|
type: {
|
|
118
117
|
type: "array",
|
|
119
118
|
items: {
|
package/build/propertyTypes.js
CHANGED
|
@@ -17,11 +17,34 @@ export const StatusEnum = z.enum([
|
|
|
17
17
|
"review",
|
|
18
18
|
"complete"
|
|
19
19
|
]);
|
|
20
|
+
// Schema for reading properties (includes timestamps)
|
|
20
21
|
export const ObsidianPropertiesSchema = z.object({
|
|
22
|
+
// Basic Metadata
|
|
23
|
+
// Note: Timestamps are managed automatically
|
|
24
|
+
title: z.string().optional(),
|
|
25
|
+
modified: z.string().datetime().optional(), // Read-only, managed by MCP server
|
|
26
|
+
author: z.string().optional(),
|
|
27
|
+
// Classification
|
|
28
|
+
type: z.array(PropertyTypeEnum).optional(),
|
|
29
|
+
// Organization
|
|
30
|
+
tags: z.array(z.string().startsWith("#")).optional(),
|
|
31
|
+
// Technical Metadata
|
|
32
|
+
status: z.array(StatusEnum).optional(),
|
|
33
|
+
version: z.string().optional(),
|
|
34
|
+
platform: z.string().optional(),
|
|
35
|
+
repository: z.string().url().optional(),
|
|
36
|
+
dependencies: z.array(z.string()).optional(),
|
|
37
|
+
// References
|
|
38
|
+
sources: z.array(z.string()).optional(),
|
|
39
|
+
urls: z.array(z.string().url()).optional(),
|
|
40
|
+
papers: z.array(z.string()).optional(),
|
|
41
|
+
// Custom Fields
|
|
42
|
+
custom: z.record(z.unknown()).optional()
|
|
43
|
+
});
|
|
44
|
+
// Schema for validating property updates (excludes timestamps)
|
|
45
|
+
export const PropertyUpdateSchema = z.object({
|
|
21
46
|
// Basic Metadata
|
|
22
47
|
title: z.string().optional(),
|
|
23
|
-
created: z.string().datetime().optional(),
|
|
24
|
-
modified: z.string().datetime().optional(),
|
|
25
48
|
author: z.string().optional(),
|
|
26
49
|
// Classification
|
|
27
50
|
type: z.array(PropertyTypeEnum).optional(),
|
package/package.json
CHANGED
package/src/properties.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { parse, stringify } from 'yaml';
|
|
2
2
|
import { ObsidianClient } from './obsidian.js';
|
|
3
|
-
import {
|
|
4
|
-
ObsidianProperties,
|
|
5
|
-
ObsidianPropertiesSchema,
|
|
3
|
+
import {
|
|
4
|
+
ObsidianProperties,
|
|
5
|
+
ObsidianPropertiesSchema,
|
|
6
|
+
PropertyUpdateSchema,
|
|
6
7
|
PropertyManagerResult,
|
|
7
|
-
ValidationResult
|
|
8
|
+
ValidationResult
|
|
8
9
|
} from './propertyTypes.js';
|
|
9
10
|
|
|
10
11
|
export class PropertyManager {
|
|
@@ -62,7 +63,7 @@ export class PropertyManager {
|
|
|
62
63
|
* Validate property values
|
|
63
64
|
*/
|
|
64
65
|
validateProperties(properties: Partial<ObsidianProperties>): ValidationResult {
|
|
65
|
-
const result =
|
|
66
|
+
const result = PropertyUpdateSchema.safeParse(properties);
|
|
66
67
|
|
|
67
68
|
if (result.success) {
|
|
68
69
|
return { valid: true, errors: [] };
|
|
@@ -70,7 +71,7 @@ export class PropertyManager {
|
|
|
70
71
|
|
|
71
72
|
return {
|
|
72
73
|
valid: false,
|
|
73
|
-
errors: result.error.errors.map(err =>
|
|
74
|
+
errors: result.error.errors.map(err =>
|
|
74
75
|
`${err.path.join('.')}: ${err.message}`
|
|
75
76
|
)
|
|
76
77
|
};
|
|
@@ -86,7 +87,8 @@ export class PropertyManager {
|
|
|
86
87
|
const merged = { ...existing };
|
|
87
88
|
|
|
88
89
|
for (const [key, value] of Object.entries(updates)) {
|
|
89
|
-
|
|
90
|
+
// Skip undefined values and timestamp fields
|
|
91
|
+
if (value === undefined || key === 'created' || key === 'modified') continue;
|
|
90
92
|
|
|
91
93
|
const currentValue = merged[key as keyof ObsidianProperties];
|
|
92
94
|
|
|
@@ -109,7 +111,7 @@ export class PropertyManager {
|
|
|
109
111
|
}
|
|
110
112
|
}
|
|
111
113
|
|
|
112
|
-
// Always update modified date
|
|
114
|
+
// Always update modified date (this is the only place we set it)
|
|
113
115
|
merged.modified = new Date().toISOString();
|
|
114
116
|
|
|
115
117
|
return merged;
|
package/src/propertyTools.ts
CHANGED
|
@@ -79,7 +79,7 @@ export class UpdatePropertiesToolHandler extends BaseToolHandler<UpdatePropertie
|
|
|
79
79
|
getToolDescription(): Tool {
|
|
80
80
|
return {
|
|
81
81
|
name: this.name,
|
|
82
|
-
description: "Update properties in an Obsidian note's YAML frontmatter. Intelligently merges arrays (tags, type, status), handles custom fields, and automatically
|
|
82
|
+
description: "Update properties in an Obsidian note's YAML frontmatter. Intelligently merges arrays (tags, type, status), handles custom fields, and automatically manages timestamps (created by Obsidian, modified by MCP server). Existing properties not included in the update are preserved.",
|
|
83
83
|
examples: [
|
|
84
84
|
{
|
|
85
85
|
description: "Update basic metadata",
|
|
@@ -130,9 +130,8 @@ export class UpdatePropertiesToolHandler extends BaseToolHandler<UpdatePropertie
|
|
|
130
130
|
description: "Properties to update",
|
|
131
131
|
properties: {
|
|
132
132
|
title: { type: "string" },
|
|
133
|
-
created: { type: "string", format: "date-time" },
|
|
134
|
-
modified: { type: "string", format: "date-time" },
|
|
135
133
|
author: { type: "string" },
|
|
134
|
+
// Note: created and modified timestamps are managed automatically
|
|
136
135
|
type: {
|
|
137
136
|
type: "array",
|
|
138
137
|
items: {
|
package/src/propertyTypes.ts
CHANGED
|
@@ -20,11 +20,40 @@ export const StatusEnum = z.enum([
|
|
|
20
20
|
"complete"
|
|
21
21
|
]);
|
|
22
22
|
|
|
23
|
+
// Schema for reading properties (includes timestamps)
|
|
23
24
|
export const ObsidianPropertiesSchema = z.object({
|
|
25
|
+
// Basic Metadata
|
|
26
|
+
// Note: Timestamps are managed automatically
|
|
27
|
+
title: z.string().optional(),
|
|
28
|
+
modified: z.string().datetime().optional(), // Read-only, managed by MCP server
|
|
29
|
+
author: z.string().optional(),
|
|
30
|
+
|
|
31
|
+
// Classification
|
|
32
|
+
type: z.array(PropertyTypeEnum).optional(),
|
|
33
|
+
|
|
34
|
+
// Organization
|
|
35
|
+
tags: z.array(z.string().startsWith("#")).optional(),
|
|
36
|
+
|
|
37
|
+
// Technical Metadata
|
|
38
|
+
status: z.array(StatusEnum).optional(),
|
|
39
|
+
version: z.string().optional(),
|
|
40
|
+
platform: z.string().optional(),
|
|
41
|
+
repository: z.string().url().optional(),
|
|
42
|
+
dependencies: z.array(z.string()).optional(),
|
|
43
|
+
|
|
44
|
+
// References
|
|
45
|
+
sources: z.array(z.string()).optional(),
|
|
46
|
+
urls: z.array(z.string().url()).optional(),
|
|
47
|
+
papers: z.array(z.string()).optional(),
|
|
48
|
+
|
|
49
|
+
// Custom Fields
|
|
50
|
+
custom: z.record(z.unknown()).optional()
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Schema for validating property updates (excludes timestamps)
|
|
54
|
+
export const PropertyUpdateSchema = z.object({
|
|
24
55
|
// Basic Metadata
|
|
25
56
|
title: z.string().optional(),
|
|
26
|
-
created: z.string().datetime().optional(),
|
|
27
|
-
modified: z.string().datetime().optional(),
|
|
28
57
|
author: z.string().optional(),
|
|
29
58
|
|
|
30
59
|
// Classification
|
|
@@ -50,6 +79,7 @@ export const ObsidianPropertiesSchema = z.object({
|
|
|
50
79
|
});
|
|
51
80
|
|
|
52
81
|
export type ObsidianProperties = z.infer<typeof ObsidianPropertiesSchema>;
|
|
82
|
+
export type PropertyUpdate = z.infer<typeof PropertyUpdateSchema>;
|
|
53
83
|
|
|
54
84
|
export interface PropertyOperation {
|
|
55
85
|
operation: 'get' | 'update' | 'patch';
|