obsidian-mcp-server 1.2.6 → 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 (79) hide show
  1. package/README.md +54 -10
  2. package/build/index.js +12 -3
  3. package/build/mcp/handlers.js +138 -0
  4. package/build/mcp/index.js +7 -0
  5. package/build/mcp/server.js +131 -0
  6. package/build/mcp/types.js +7 -0
  7. package/build/{obsidian.js → obsidian/client.js} +156 -104
  8. package/build/obsidian/errors.js +75 -0
  9. package/build/obsidian/index.js +7 -0
  10. package/build/obsidian/types.js +12 -0
  11. package/build/resources/index.js +15 -0
  12. package/build/{resources.js → resources/tags.js} +35 -14
  13. package/build/resources/types.js +5 -0
  14. package/build/tools/base.js +78 -0
  15. package/build/tools/files/content.js +171 -0
  16. package/build/tools/files/index.js +22 -0
  17. package/build/tools/files/list.js +133 -0
  18. package/build/tools/index.js +31 -0
  19. package/build/tools/properties/index.js +19 -0
  20. package/build/{properties.js → tools/properties/manager.js} +50 -13
  21. package/build/{propertyTools.js → tools/properties/tools.js} +22 -6
  22. package/build/{propertyTypes.js → tools/properties/types.js} +19 -7
  23. package/build/tools/search/complex.js +203 -0
  24. package/build/tools/search/index.js +20 -0
  25. package/build/tools/search/simple.js +127 -0
  26. package/build/utils/errors.js +59 -0
  27. package/build/utils/index.js +9 -0
  28. package/build/utils/logging.js +119 -0
  29. package/build/utils/rate-limiting.js +94 -0
  30. package/build/utils/tokenization.js +62 -0
  31. package/build/utils/validation.js +88 -0
  32. package/examples/README.md +48 -0
  33. package/examples/append-content.md +63 -0
  34. package/examples/complex-search.md +117 -0
  35. package/examples/find-in-file.md +94 -0
  36. package/examples/get-file-contents.md +72 -0
  37. package/examples/get-properties.md +89 -0
  38. package/examples/list-files-in-dir.md +55 -0
  39. package/examples/list-files-in-vault.md +53 -0
  40. package/examples/patch-content.md +60 -0
  41. package/examples/update-properties.md +126 -0
  42. package/mcp-client-config.example.json +23 -0
  43. package/package.json +4 -3
  44. package/src/index.ts +13 -3
  45. package/src/mcp/handlers.ts +183 -0
  46. package/src/mcp/index.ts +6 -0
  47. package/src/mcp/server.ts +162 -0
  48. package/src/mcp/types.ts +46 -0
  49. package/src/{obsidian.ts → obsidian/client.ts} +174 -134
  50. package/src/obsidian/errors.ts +105 -0
  51. package/src/obsidian/index.ts +6 -0
  52. package/src/obsidian/types.ts +124 -0
  53. package/src/resources/index.ts +17 -0
  54. package/src/{resources.ts → resources/tags.ts} +42 -16
  55. package/src/resources/types.ts +30 -0
  56. package/src/tools/base.ts +112 -0
  57. package/src/tools/files/content.ts +206 -0
  58. package/src/tools/files/index.ts +31 -0
  59. package/src/tools/files/list.ts +150 -0
  60. package/src/tools/index.ts +38 -0
  61. package/src/tools/properties/index.ts +21 -0
  62. package/src/{properties.ts → tools/properties/manager.ts} +54 -13
  63. package/src/{propertyTools.ts → tools/properties/tools.ts} +33 -7
  64. package/src/{propertyTypes.ts → tools/properties/types.ts} +31 -7
  65. package/src/tools/search/complex.ts +231 -0
  66. package/src/tools/search/index.ts +22 -0
  67. package/src/tools/search/simple.ts +147 -0
  68. package/src/utils/errors.ts +69 -0
  69. package/src/utils/index.ts +8 -0
  70. package/src/utils/logging.ts +146 -0
  71. package/src/utils/rate-limiting.ts +114 -0
  72. package/src/utils/tokenization.ts +71 -0
  73. package/src/utils/validation.ts +95 -0
  74. package/build/server.js +0 -238
  75. package/build/tools.js +0 -863
  76. package/build/types.js +0 -37
  77. package/src/server.ts +0 -308
  78. package/src/tools.ts +0 -946
  79. package/src/types.ts +0 -184
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Error handling utilities for the Obsidian MCP Server
3
+ */
4
+ /**
5
+ * Error class for Obsidian MCP Server specific errors
6
+ */
7
+ export class ObsidianError extends Error {
8
+ details;
9
+ errorCode;
10
+ constructor(message, errorCode = 50000, // Default server error code
11
+ details) {
12
+ super(message);
13
+ this.details = details;
14
+ this.name = "ObsidianError";
15
+ // Ensure 5-digit error code
16
+ if (errorCode < 10000 || errorCode > 99999) {
17
+ // Convert HTTP status codes to 5-digit codes
18
+ // 4xx -> 4xxxx
19
+ // 5xx -> 5xxxx
20
+ this.errorCode = errorCode < 1000 ? errorCode * 100 : 50000;
21
+ }
22
+ else {
23
+ this.errorCode = errorCode;
24
+ }
25
+ }
26
+ // Convert to API error format
27
+ toApiError() {
28
+ return {
29
+ errorCode: this.errorCode,
30
+ message: this.message
31
+ };
32
+ }
33
+ }
34
+ /**
35
+ * Maps HTTP status codes to internal error codes
36
+ */
37
+ export function getErrorCodeFromStatus(status) {
38
+ switch (status) {
39
+ case 400: return 40000; // Bad request
40
+ case 401: return 40100; // Unauthorized
41
+ case 403: return 40300; // Forbidden
42
+ case 404: return 40400; // Not found
43
+ case 405: return 40500; // Method not allowed
44
+ case 409: return 40900; // Conflict
45
+ case 429: return 42900; // Too many requests
46
+ case 500: return 50000; // Internal server error
47
+ case 501: return 50100; // Not implemented
48
+ case 502: return 50200; // Bad gateway
49
+ case 503: return 50300; // Service unavailable
50
+ case 504: return 50400; // Gateway timeout
51
+ default:
52
+ if (status >= 400 && status < 500)
53
+ return 40000 + (status - 400) * 100;
54
+ if (status >= 500 && status < 600)
55
+ return 50000 + (status - 500) * 100;
56
+ return 50000;
57
+ }
58
+ }
59
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Exports all utility functions and classes
3
+ */
4
+ export * from './errors.js';
5
+ export * from './logging.js';
6
+ export * from './rate-limiting.js';
7
+ export * from './tokenization.js';
8
+ export * from './validation.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Logging utilities for the Obsidian MCP Server
3
+ */
4
+ /**
5
+ * Log levels
6
+ */
7
+ export var LogLevel;
8
+ (function (LogLevel) {
9
+ LogLevel[LogLevel["ERROR"] = 0] = "ERROR";
10
+ LogLevel[LogLevel["WARN"] = 1] = "WARN";
11
+ LogLevel[LogLevel["INFO"] = 2] = "INFO";
12
+ LogLevel[LogLevel["DEBUG"] = 3] = "DEBUG";
13
+ LogLevel[LogLevel["TRACE"] = 4] = "TRACE";
14
+ })(LogLevel || (LogLevel = {}));
15
+ /**
16
+ * Default logger configuration
17
+ */
18
+ const DEFAULT_CONFIG = {
19
+ level: process.env.NODE_ENV === 'production'
20
+ ? LogLevel.INFO
21
+ : LogLevel.DEBUG,
22
+ includeTimestamps: true,
23
+ includeLevel: true
24
+ };
25
+ /**
26
+ * Simple logger for MCP server operations
27
+ */
28
+ export class Logger {
29
+ name;
30
+ config;
31
+ constructor(name, config = {}) {
32
+ this.name = name;
33
+ this.config = { ...DEFAULT_CONFIG, ...config };
34
+ }
35
+ /**
36
+ * Internal method to format and output a log message
37
+ */
38
+ log(level, message, ...args) {
39
+ if (level > this.config.level)
40
+ return;
41
+ const parts = [];
42
+ // Add timestamp if configured
43
+ if (this.config.includeTimestamps) {
44
+ parts.push(`[${new Date().toISOString()}]`);
45
+ }
46
+ // Add level if configured
47
+ if (this.config.includeLevel) {
48
+ const levelStr = LogLevel[level] || 'UNKNOWN';
49
+ parts.push(`[${levelStr}]`);
50
+ }
51
+ // Add component name
52
+ parts.push(`[${this.name}]`);
53
+ // Add message
54
+ parts.push(message);
55
+ // Output to console
56
+ const output = parts.join(' ');
57
+ switch (level) {
58
+ case LogLevel.ERROR:
59
+ console.error(output, ...args);
60
+ break;
61
+ case LogLevel.WARN:
62
+ console.warn(output, ...args);
63
+ break;
64
+ case LogLevel.INFO:
65
+ console.info(output, ...args);
66
+ break;
67
+ case LogLevel.DEBUG:
68
+ case LogLevel.TRACE:
69
+ default:
70
+ console.debug(output, ...args);
71
+ break;
72
+ }
73
+ }
74
+ /**
75
+ * Log an error message
76
+ */
77
+ error(message, ...args) {
78
+ this.log(LogLevel.ERROR, message, ...args);
79
+ }
80
+ /**
81
+ * Log a warning message
82
+ */
83
+ warn(message, ...args) {
84
+ this.log(LogLevel.WARN, message, ...args);
85
+ }
86
+ /**
87
+ * Log an info message
88
+ */
89
+ info(message, ...args) {
90
+ this.log(LogLevel.INFO, message, ...args);
91
+ }
92
+ /**
93
+ * Log a debug message
94
+ */
95
+ debug(message, ...args) {
96
+ this.log(LogLevel.DEBUG, message, ...args);
97
+ }
98
+ /**
99
+ * Log a trace message
100
+ */
101
+ trace(message, ...args) {
102
+ this.log(LogLevel.TRACE, message, ...args);
103
+ }
104
+ /**
105
+ * Set the log level
106
+ */
107
+ setLevel(level) {
108
+ this.config.level = level;
109
+ }
110
+ }
111
+ /**
112
+ * Create and return a namespaced logger
113
+ */
114
+ export function createLogger(name, config) {
115
+ return new Logger(name, config);
116
+ }
117
+ // Create a global root logger
118
+ export const rootLogger = createLogger('mcp-server');
119
+ //# sourceMappingURL=logging.js.map
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Rate limiting utilities for the Obsidian MCP Server
3
+ */
4
+ import { ObsidianError } from './errors.js';
5
+ /**
6
+ * Default rate limit configuration
7
+ */
8
+ export const DEFAULT_RATE_LIMIT_CONFIG = {
9
+ windowMs: 15 * 60 * 1000, // 15 minutes
10
+ maxRequests: 200
11
+ };
12
+ /**
13
+ * RateLimit manages request rate limiting for API endpoints
14
+ */
15
+ export class RateLimiter {
16
+ config;
17
+ requestCounts = new Map();
18
+ cleanupInterval;
19
+ constructor(config = DEFAULT_RATE_LIMIT_CONFIG) {
20
+ this.config = config;
21
+ // Clean up expired rate limit entries periodically
22
+ this.cleanupInterval = setInterval(() => this.cleanup(), 60000); // Clean up every minute
23
+ }
24
+ /**
25
+ * Check if a request is within rate limits
26
+ * @param key Identifier for the rate limit bucket (e.g., toolName)
27
+ * @returns Whether the request is allowed
28
+ */
29
+ checkRateLimit(key) {
30
+ const now = Date.now();
31
+ const requestInfo = this.requestCounts.get(key);
32
+ if (!requestInfo || now > requestInfo.resetTime) {
33
+ // Reset counter for new window
34
+ this.requestCounts.set(key, {
35
+ count: 1,
36
+ resetTime: now + this.config.windowMs
37
+ });
38
+ return true;
39
+ }
40
+ if (requestInfo.count >= this.config.maxRequests) {
41
+ return false;
42
+ }
43
+ requestInfo.count++;
44
+ return true;
45
+ }
46
+ /**
47
+ * Check rate limit and throw an error if exceeded
48
+ * @param key Identifier for the rate limit bucket
49
+ * @throws ObsidianError if rate limit is exceeded
50
+ */
51
+ enforceRateLimit(key) {
52
+ if (!this.checkRateLimit(key)) {
53
+ throw new ObsidianError(`Rate limit exceeded for ${key}. Please try again later.`, 42900 // 42900 = Rate limit exceeded
54
+ );
55
+ }
56
+ }
57
+ /**
58
+ * Get information about current rate limit status
59
+ * @param key Identifier for the rate limit bucket
60
+ * @returns Rate limit information or null if no requests have been made
61
+ */
62
+ getRateLimitInfo(key) {
63
+ const requestInfo = this.requestCounts.get(key);
64
+ if (!requestInfo) {
65
+ return null;
66
+ }
67
+ return {
68
+ remaining: Math.max(0, this.config.maxRequests - requestInfo.count),
69
+ resetTime: requestInfo.resetTime
70
+ };
71
+ }
72
+ /**
73
+ * Clean up expired rate limit entries
74
+ */
75
+ cleanup() {
76
+ const now = Date.now();
77
+ for (const [key, info] of this.requestCounts.entries()) {
78
+ if (now > info.resetTime) {
79
+ this.requestCounts.delete(key);
80
+ }
81
+ }
82
+ }
83
+ /**
84
+ * Clean up resources (e.g., when shutting down)
85
+ */
86
+ dispose() {
87
+ if (this.cleanupInterval) {
88
+ clearInterval(this.cleanupInterval);
89
+ }
90
+ }
91
+ }
92
+ // Export a singleton instance with default configuration
93
+ export const rateLimiter = new RateLimiter();
94
+ //# sourceMappingURL=rate-limiting.js.map
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Token counting utilities for the Obsidian MCP Server
3
+ */
4
+ import { encoding_for_model } from "tiktoken";
5
+ // Load token limits from environment or use defaults
6
+ export const MAX_TOKENS = parseInt(process.env.MAX_TOKENS ?? '20000');
7
+ export const TRUNCATION_MESSAGE = "\n\n[Response truncated due to length]";
8
+ /**
9
+ * Handles token counting and text truncation to stay within token limits
10
+ */
11
+ export class TokenCounter {
12
+ tokenizer = encoding_for_model("gpt-4"); // This is strictly for token counting, not for LLM inference
13
+ isShuttingDown = false;
14
+ constructor() {
15
+ // Clean up tokenizer when process exits
16
+ const cleanup = () => {
17
+ if (!this.isShuttingDown) {
18
+ this.isShuttingDown = true;
19
+ if (this.tokenizer) {
20
+ this.tokenizer.free();
21
+ }
22
+ }
23
+ };
24
+ process.on('exit', cleanup);
25
+ process.on('SIGINT', cleanup);
26
+ process.on('SIGTERM', cleanup);
27
+ process.on('uncaughtException', cleanup);
28
+ }
29
+ /**
30
+ * Count the number of tokens in a string
31
+ */
32
+ countTokens(text) {
33
+ return this.tokenizer.encode(text).length;
34
+ }
35
+ /**
36
+ * Truncate text to stay within token limit
37
+ */
38
+ truncateToTokenLimit(text, limit = MAX_TOKENS) {
39
+ const tokens = this.tokenizer.encode(text);
40
+ if (tokens.length <= limit) {
41
+ return text;
42
+ }
43
+ // Reserve tokens for truncation message
44
+ const messageTokens = this.tokenizer.encode(TRUNCATION_MESSAGE);
45
+ const availableTokens = limit - messageTokens.length;
46
+ // Decode truncated tokens back to text
47
+ const truncatedText = this.tokenizer.decode(tokens.slice(0, availableTokens));
48
+ return truncatedText + TRUNCATION_MESSAGE;
49
+ }
50
+ /**
51
+ * Clean up resources
52
+ */
53
+ cleanup() {
54
+ if (this.tokenizer && !this.isShuttingDown) {
55
+ this.isShuttingDown = true;
56
+ this.tokenizer.free();
57
+ }
58
+ }
59
+ }
60
+ // Export a singleton instance
61
+ export const tokenCounter = new TokenCounter();
62
+ //# sourceMappingURL=tokenization.js.map
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Validation utilities for the Obsidian MCP Server
3
+ */
4
+ /**
5
+ * Validates a file path to prevent path traversal attacks and other security issues
6
+ * @param filepath The path to validate
7
+ * @throws Error if the path is invalid
8
+ */
9
+ export function validateFilePath(filepath) {
10
+ // Prevent path traversal attacks
11
+ const normalizedPath = filepath.replace(/\\/g, '/');
12
+ if (normalizedPath.includes('../') || normalizedPath.includes('..\\')) {
13
+ throw new Error('Invalid file path: Path traversal not allowed');
14
+ }
15
+ // Additional path validations
16
+ if (normalizedPath.startsWith('/') || /^[a-zA-Z]:/.test(normalizedPath)) {
17
+ throw new Error('Invalid file path: Absolute paths not allowed');
18
+ }
19
+ }
20
+ /**
21
+ * Sanitizes a header value to prevent header injection attacks
22
+ * @param value The header value to sanitize
23
+ * @returns The sanitized header value
24
+ */
25
+ export function sanitizeHeader(value) {
26
+ // Remove any potentially harmful characters from header values
27
+ return value.replace(/[^\w\s\-\._~:/?#\[\]@!$&'()*+,;=]/g, '');
28
+ }
29
+ /**
30
+ * Validates tool arguments against a JSON schema
31
+ * @param args The arguments to validate
32
+ * @param schema The JSON schema to validate against
33
+ * @returns Validation result with errors if any
34
+ */
35
+ export function validateToolArguments(args, schema) {
36
+ if (typeof args !== 'object' || args === null) {
37
+ return { valid: false, errors: ['Arguments must be an object'] };
38
+ }
39
+ const errors = [];
40
+ const required = schema.required || [];
41
+ // Check required fields
42
+ for (const field of required) {
43
+ if (!(field in args)) {
44
+ errors.push(`Missing required field: ${field}`);
45
+ }
46
+ }
47
+ // Check field types
48
+ const properties = schema.properties || {};
49
+ for (const [key, value] of Object.entries(args)) {
50
+ const propSchema = properties[key];
51
+ if (!propSchema) {
52
+ errors.push(`Unknown field: ${key}`);
53
+ continue;
54
+ }
55
+ // Skip validation for undefined optional fields
56
+ if (value === undefined && !required.includes(key)) {
57
+ continue;
58
+ }
59
+ // Type validation
60
+ if (propSchema.type === 'string' && typeof value !== 'string') {
61
+ errors.push(`Field ${key} must be a string`);
62
+ }
63
+ else if (propSchema.type === 'number' && typeof value !== 'number') {
64
+ errors.push(`Field ${key} must be a number`);
65
+ }
66
+ else if (propSchema.type === 'boolean' && typeof value !== 'boolean') {
67
+ errors.push(`Field ${key} must be a boolean`);
68
+ }
69
+ else if (propSchema.type === 'array' && !Array.isArray(value)) {
70
+ errors.push(`Field ${key} must be an array`);
71
+ }
72
+ // Enum validation
73
+ if (propSchema.enum && value !== undefined && !propSchema.enum.includes(value)) {
74
+ errors.push(`Field ${key} must be one of: ${propSchema.enum.join(', ')}`);
75
+ }
76
+ // Format validation for paths
77
+ if (propSchema.format === 'path' && typeof value === 'string') {
78
+ try {
79
+ validateFilePath(value);
80
+ }
81
+ catch (error) {
82
+ errors.push(`Field ${key}: ${error.message}`);
83
+ }
84
+ }
85
+ }
86
+ return { valid: errors.length === 0, errors };
87
+ }
88
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1,48 @@
1
+ # Obsidian MCP Server Tool Examples
2
+
3
+ This directory contains example requests and responses for each tool provided by the obsidian-mcp-server. These examples demonstrate the capabilities and expected output format of each tool.
4
+
5
+ ## Tools
6
+
7
+ ### 1. [List Files in Vault](list-files-in-vault.md)
8
+ 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.
9
+
10
+ ### 2. [List Files in Directory](list-files-in-dir.md)
11
+ Lists all files and directories that exist in a specific Obsidian directory. Returns a hierarchical structure showing files, folders, and their relationships.
12
+
13
+ ### 3. [Get File Contents](get-file-contents.md)
14
+ Return the content of a single file in your vault. Supports markdown files, text files, and other readable formats.
15
+
16
+ ### 4. [Find in File](find-in-file.md)
17
+ Full-text search across all files in the vault. Returns matching files with surrounding context for each match.
18
+
19
+ ### 5. [Append Content](append-content.md)
20
+ Append content to a new or existing file in the vault. Useful for adding new sections, notes, or updates to existing documents.
21
+
22
+ ### 6. [Patch Content](patch-content.md)
23
+ Update the entire content of an existing note or create a new one. Provides complete control over file contents.
24
+
25
+ ### 7. [Complex Search](complex-search.md)
26
+ File path pattern matching using JsonLogic queries. Supports operations like glob pattern matching and variable access.
27
+
28
+ ### 8. [Get Properties](get-properties.md)
29
+ Get properties (title, tags, status, etc.) from an Obsidian note's YAML frontmatter. Returns all available properties including custom fields.
30
+
31
+ ### 9. [Update Properties](update-properties.md)
32
+ Update properties in an Obsidian note's YAML frontmatter. Intelligently merges arrays and handles custom fields.
33
+
34
+ ## Resources
35
+
36
+ ### Direct Resources
37
+ - obsidian://tags - List of all tags used across the Obsidian vault with their usage counts
38
+
39
+ ## Production Readiness
40
+
41
+ All tools have been tested and demonstrate:
42
+ - Proper input validation and error handling
43
+ - Comprehensive and well-structured responses
44
+ - Consistent output formatting
45
+ - Practical applicability to real-world Obsidian vault management
46
+ - Deep integration with Obsidian's features and capabilities
47
+
48
+ The examples serve as both documentation and test cases, showing the expected behavior and quality of responses for each tool.
@@ -0,0 +1,63 @@
1
+ # Append Content Tool Example
2
+
3
+ ## Request 1 (Append to Existing Note)
4
+ ```json
5
+ {
6
+ "filepath": "Projects/Project Alpha/Notes/Meeting Notes.md",
7
+ "content": "\n\n## Team Meeting - 2024-01-25\n\n### Discussion Points\n- Reviewed project timeline\n- Discussed technical challenges\n- Assigned new tasks\n\n### Action Items\n- [ ] Update documentation\n- [ ] Schedule follow-up meeting\n- [ ] Share progress report"
8
+ }
9
+ ```
10
+
11
+ ## Response 1
12
+ ```json
13
+ {
14
+ "success": true,
15
+ "message": "Content appended successfully"
16
+ }
17
+ ```
18
+
19
+ ## Request 2 (Create New Note)
20
+ ```json
21
+ {
22
+ "filepath": "Daily Notes/2024-01-25.md",
23
+ "content": "---\ntitle: Daily Note - January 25, 2024\ntags: [daily-notes]\ndate: 2024-01-25\n---\n\n# Daily Notes\n\n## Tasks\n- [ ] Review project updates\n- [ ] Team meeting at 2 PM\n- [ ] Update documentation\n\n## Notes\n- Started work on new feature\n- Discussed timeline with team\n- Reviewed technical specifications"
24
+ }
25
+ ```
26
+
27
+ ## Response 2
28
+ ```json
29
+ {
30
+ "success": true,
31
+ "message": "File created and content appended successfully"
32
+ }
33
+ ```
34
+
35
+ ## Example Use Cases
36
+
37
+ 1. **Meeting Notes**
38
+ - Add new meeting minutes to existing notes
39
+ - Create structured meeting summaries
40
+ - Track action items and decisions
41
+
42
+ 2. **Daily Notes**
43
+ - Create daily journal entries
44
+ - Add to existing daily logs
45
+ - Maintain consistent note structure
46
+
47
+ 3. **Project Documentation**
48
+ - Add new sections to documentation
49
+ - Update progress logs
50
+ - Append new requirements or specifications
51
+
52
+ ## Notes
53
+ - Can append to existing files or create new ones
54
+ - Preserves existing content in the file
55
+ - Maintains proper markdown formatting
56
+ - Supports YAML frontmatter
57
+ - Handles various content types:
58
+ * Meeting notes
59
+ * Daily logs
60
+ * Task lists
61
+ * Documentation updates
62
+ - File path is relative to vault root
63
+ - Creates parent directories if needed
@@ -0,0 +1,117 @@
1
+ # Complex Search Tool Example
2
+
3
+ ## Request 1 (Simple Glob Pattern)
4
+ ```json
5
+ {
6
+ "query": {
7
+ "glob": ["*.md", {"var": "path"}]
8
+ }
9
+ }
10
+ ```
11
+
12
+ ## Response 1
13
+ ```json
14
+ {
15
+ "matches": [
16
+ "README.md",
17
+ "Projects/Project Alpha/Documentation/Requirements.md",
18
+ "Daily Notes/2024-01-25.md",
19
+ "Templates/Meeting Note.md"
20
+ ]
21
+ }
22
+ ```
23
+
24
+ ## Request 2 (Complex Pattern)
25
+ ```json
26
+ {
27
+ "query": {
28
+ "and": [
29
+ {
30
+ "glob": ["Projects/**/*.md", {"var": "path"}]
31
+ },
32
+ {
33
+ "in": [
34
+ "Documentation",
35
+ {"var": "path"}
36
+ ]
37
+ }
38
+ ]
39
+ }
40
+ }
41
+ ```
42
+
43
+ ## Response 2
44
+ ```json
45
+ {
46
+ "matches": [
47
+ "Projects/Project Alpha/Documentation/Requirements.md",
48
+ "Projects/Project Alpha/Documentation/Architecture.md",
49
+ "Projects/Project Beta/Documentation/API.md"
50
+ ]
51
+ }
52
+ ```
53
+
54
+ ## Example Use Cases
55
+
56
+ 1. **File Organization**
57
+ - Find files by pattern matching
58
+ - Filter by path components
59
+ - Combine multiple search criteria
60
+
61
+ 2. **Content Management**
62
+ - Locate files in specific directories
63
+ - Filter by file extensions
64
+ - Find files matching complex patterns
65
+
66
+ 3. **Project Navigation**
67
+ - Search within project directories
68
+ - Find documentation files
69
+ - Filter by file location
70
+
71
+ ## Notes
72
+ - Uses JsonLogic for query construction
73
+ - Supports glob pattern matching
74
+ - Can combine multiple conditions
75
+ - Available operations:
76
+ * glob: Pattern matching for paths
77
+ * var: Variable access (path)
78
+ * and/or: Logical operations
79
+ * in: Membership testing
80
+ - Path is relative to vault root
81
+ - Returns matching file paths
82
+ - Useful for complex file filtering
83
+
84
+ ## Common Patterns
85
+
86
+ 1. **Find All Markdown Files**
87
+ ```json
88
+ {
89
+ "glob": ["**/*.md", {"var": "path"}]
90
+ }
91
+ ```
92
+
93
+ 2. **Files in Specific Directory**
94
+ ```json
95
+ {
96
+ "glob": ["Projects/Project Alpha/**/*", {"var": "path"}]
97
+ }
98
+ ```
99
+
100
+ 3. **Multiple Extensions**
101
+ ```json
102
+ {
103
+ "or": [
104
+ {"glob": ["**/*.md", {"var": "path"}]},
105
+ {"glob": ["**/*.txt", {"var": "path"}]}
106
+ ]
107
+ }
108
+ ```
109
+
110
+ 4. **Exclude Pattern**
111
+ ```json
112
+ {
113
+ "and": [
114
+ {"glob": ["**/*.md", {"var": "path"}]},
115
+ {"not": {"glob": ["**/Archive/**", {"var": "path"}]}}
116
+ ]
117
+ }