Adds src/http-server.ts - a Node.js HTTP entry point that exposes the
MCP implementation via Streamable HTTP transport (POST/GET /mcp) per
the MCP 2025-11-25 specification.
- Uses StreamableHTTPServerTransport (stateless mode) from the MCP SDK
- Port configurable via MCP_PORT env var (default: 3000)
- ObsidianMCPServer.connect() now accepts an optional Transport param
so both stdio (existing) and HTTP (new) modes reuse the same server
- Added 'npm run server' script to start the HTTP server
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The previous implementation prefixed base64 with "BASE64:" in a text
response. This updates the response to use the proper MCP embedded
resource format:
{ type: "resource", resource: { uri, mimeType, blob } }
Changes:
- types.ts: extend ToolOutput content union to allow resource items
- file-operations.ts:
- getMimeType() maps common extensions to MIME types, falling back
to application/octet-stream
- MIME_TYPES table covers PDF, ZIP, images, Office formats, audio/video
- Binary files are now returned as an EmbeddedResource with:
uri: obsidian://<vault>/<path>
mimeType: detected from file extension
blob: base64-encoded raw bytes from the Buffer
- Tool descriptions updated to document the resource response shape
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Previously, binary files (ZIP, images, compiled files) were read via
data.toString() which corrupted the bytes through UTF-8 decoding,
making the content unrecoverable on the client side.
Changes:
- executor.ts: add executeCommandBinary / executeObsidianCommandBinary
that collect stdout chunks as raw Buffers instead of strings
- types.ts: add optional stdoutBuffer field to CLIResult
- file-operations.ts:
- obsidian_read_note now uses executeObsidianCommandBinary so the
raw bytes are preserved before any decoding happens
- isBinaryContent() now operates on the raw Buffer (null byte check
+ >10% non-printable byte ratio on first 8KB sample)
- Binary files are returned as "BASE64:<base64string>" so the client
can reliably decode back to the original binary
- Tool descriptions updated to document the BASE64: prefix convention
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When obsidian_read_note read a binary file (ZIP, image, compiled
binary, etc.) the CLI returned raw bytes that were corrupted by text
decoding, producing an unusable response.
Added isBinaryContent() helper that checks for null bytes (definitive
binary marker) and a >10% ratio of non-printable characters in the
first 8KB of content. When binary content is detected the tool returns
a clear error message instead of garbled bytes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The tool descriptions were ambiguous — 'path' was described as a
'full file path (alternative to name)' which led callers to pass the
full path including filename in path, confusing the CLI.
Clarified semantics:
name = filename only (e.g. "My Note.md")
path = folder only (e.g. "Projects/Work") — never include filename
Added required: ['name'] to both input schemas.
Updated tool and handler descriptions accordingly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
< and > were in DANGEROUS_CHARS on the assumption they could trigger
shell redirection. However, shell redirection only applies at the
command level — inside double-quoted strings (which is how all values
are passed via formatParam) they are completely inert.
Removing them from DANGEROUS_CHARS and sanitizePath preserves:
- Mermaid diagram connectors: ->>, -->, <|, >>, etc.
- HTML tags in note content
- Any other angle-bracket syntax
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Backticks were being stripped entirely by the sanitizer, destroying
Markdown code fences (```) in note content.
The real injection risk is backtick command substitution inside
double-quoted shell strings (e.g. content=`rm -rf /`). The fix is to
escape backticks as \` in formatParam — exactly as we already do for
double quotes — so the shell never interprets them while the content
is preserved intact.
Changes:
- sanitizer.ts: remove ` from DANGEROUS_CHARS and the backtick command
substitution pattern from COMMAND_INJECTION_PATTERNS (now handled at
the quoting layer, not the stripping layer)
- cli-helpers.ts: escape backticks as \` in formatParam alongside the
existing double-quote escaping
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add offset and max_chars parameters to obsidian_read_note:
- max_chars (default 50000, max 500000): caps characters returned per call
- offset (default 0): start position for reading, enabling pagination
When content is truncated a trailer message is appended telling the
caller the total size and the exact offset to pass on the next call.
This prevents the 26MB+ responses that caused Claude to reject output
when reading large PDFs stored in an Obsidian vault.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add prominent note in Prerequisites that Obsidian must be open and running
- Expand Troubleshooting section with explanation of why the error occurs and how to fix it
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fixes#2 - Files with & in their names (e.g., 'Research & Development.md')
were being incorrectly sanitized, causing search and file-not-found errors.
Changes:
- Removed & from DANGEROUS_CHARS regex
- Single & is safe in quoted arguments passed to CLI
- Dangerous && patterns still blocked by COMMAND_INJECTION_PATTERNS
- Also allows (), [], {} which are safe in quoted args
Version: 1.1.2
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fixed obsidian_list_properties and obsidian_get_property_count tools
to properly expose their input parameters in the tools/list response.
Changes:
- obsidian_list_properties: Added 8 parameters (file, path, name, total,
sort, counts, format, active) based on 'obsidian help properties'
- obsidian_get_property_count: Added required 'name' parameter
- Fixed command names: 'property' → 'properties' (correct command)
- Added formatParam() for parameter quoting
- Changed parameter format to match Obsidian CLI: param=value
Before: Empty properties: {} meant tools appeared in list but with no
documented parameters for MCP clients.
After: Full parameter schemas with descriptions, types, and constraints
properly exposed via tools/list handler.
Build: ✅ 0 TypeScript errors
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Updated installation section to use Claude Desktop's custom desktop
extension workflow instead of generic MCP server configuration.
Changes:
1. Installation Section:
- Added step-by-step instructions for installing via Extensions UI
- Included Advanced Settings -> Extension Developer flow
- Added link to official Claude Desktop documentation
- Mentioned automatic encryption of vault_name via OS keychain
2. Configuration Section:
- Prioritized UI-based configuration approach
- Kept manual JSON config as 'advanced' option
- Clarified vault_name is the primary required setting
3. Available Tools Section:
- Updated tool count from 95+ to accurate 20 tools
- Listed actual implemented tools (File Ops: 8, Search: 11)
- Noted User Story 3 (Tasks & Properties) as planned
- Removed references to non-existent tools
Reference:
- https://support.claude.com/en/articles/10949351
Reflects current project scope (US1 and US2 complete, US4/US5 removed).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fixed parameter quoting in file operations and search tools to handle
filenames and values containing spaces correctly.
Root Cause:
- Obsidian CLI requires quoting values with spaces: name="My Note"
- Previous implementation used unquoted format: name=My Note
- Shell would split on spaces, breaking multi-word filenames
Solution:
1. Created formatParam() helper in src/utils/cli-helpers.ts
- Always quotes parameter values: param="value"
- Handles spaces and special characters safely
2. Updated file-operations.ts (8 tools):
- All file/path/content/name parameters now quoted
- create, read, append, prepend, delete, move, rename, open
3. Updated search.ts (1 tool):
- query, path, format, limit parameters now quoted
- Fixes searches with multi-word queries
Changes:
- Before: cmdArgs.push(\`file=${name}\`)
- After: cmdArgs.push(formatParam('file', name))
Files changed:
- src/utils/cli-helpers.ts (new): formatParam() and buildCmdArgs() helpers
- src/tools/file-operations.ts: Use formatParam() for all parameters
- src/tools/search.ts: Use formatParam() for all parameters
Impact:
- File operations now work with multi-word filenames
- Search queries with spaces now work correctly
- Content parameters with newlines/special chars handled safely
Known Issue:
- links.ts, tags-aliases.ts, properties.ts still need similar fixes
- These tools have additional structural issues (wrong command names)
- Will be addressed in follow-up commit
Build: 0 errors
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Created SCOPE.md to clearly document project scope changes and
provide a single source of truth for what's in/out of scope.
Contents:
- Current scope (US1-US3 with tool lists)
- Removed scope (US4-US5 with rationale)
- Impact summary (before/after comparison)
- Implementation status table
- Next steps roadmap
Benefits:
- Clear reference for scope discussions
- Tracks removed features for potential future work
- Documents decision rationale
- Provides implementation progress snapshot
File: specs/001-obsidian-mcp-bundle/SCOPE.md
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Added complete inputSchema definition for the obsidian_search tool
with all parameters matching the Obsidian CLI specification:
Parameters (per 'obsidian search help'):
- query (string, required): Search query text
- path (string, optional): Limit search to folder path
- limit (number, optional): Maximum number of results
- total (boolean, optional): Return match count instead of files
- case (boolean, optional): Case sensitive search
- format (enum, optional): Output format (text|json, default: text)
The inputSchema is now properly exposed via tools/list, enabling:
- Better parameter documentation in MCP clients
- Automatic parameter validation
- Type hints in Claude Desktop
- Improved developer experience
Previously the schema was empty { properties: {} }, now it fully
documents all available parameters with types and descriptions.
Files changed:
- src/tools/search.ts: Added complete inputSchema definition
Build: ✅ 0 errors
Validation: ✅ Manifest passes
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Issue: The registerTool method was receiving description and inputSchema
parameters but marking them as unused (_description, _inputSchema) and
not storing them with the handler. This caused tools/list to return
tools without their description and inputSchema properties.
Fix: Remove underscore prefixes and explicitly set these properties on
the handler object before storing it in the tools Map.
Now tools/list will properly return:
- name: Tool name
- description: Tool description (from registerTool call)
- inputSchema: Tool input schema (from registerTool call)
This ensures MCP clients can see proper tool documentation and schemas.
Files changed:
- src/server.ts: Fixed registerTool to preserve metadata
Build: ✅ 0 errors
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Removed obsidian_search_with_context tool (not in CLI spec)
Updated obsidian_search to use exact CLI parameter names:
- query (required) - Search query text
- path (optional) - Limit search to folder path
- limit (optional) - Max number of files to return
- total (optional) - Return match count instead of file list
- case (optional) - Case sensitive search
- format (optional) - Output format: text or json (default: text)
Changed parameter names to match CLI:
- folder → path
- caseSensitive → case
- Added: total flag for match counts
- Removed: contextLines (not in CLI)
Files updated:
- src/tools/search.ts: Simplified to single search tool
- src/validation/schemas.ts: Updated searchSchema parameters
- manifest.json: Removed search_with_context, updated description
- tasks.md: Marked T048 as REMOVED
Total tools: 20 (was 21)
- User Story 1: 9 tools
- User Story 2: 11 tools (was 12)
Build: ✅ 0 errors
Validation: ✅ Manifest passes
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>