4 Commits

Author SHA1 Message Date
3948cd6966 chore: bump version to 1.1.9
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-30 19:32:52 -05:00
cef658ce19 fix: return images as MCP image content type per spec
Images (PNG, JPG, GIF, WEBP, SVG) now returned as { type: 'image', data, mimeType }
per https://modelcontextprotocol.io/specification/2025-11-25/server/tools#image-content
Other binary files continue using embedded resource format { type: 'resource', ... }
Added audio content type to ToolOutput union for future use

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-30 19:31:47 -05:00
31744d01a1 chore: bump version to 1.1.8 and update CHANGELOG
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-30 18:53:50 -05:00
6ee26f1ad8 fix: return binary files as MCP embedded resource instead of BASE64 prefix fixes #9
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>
2026-04-30 18:52:57 -05:00
5 changed files with 82 additions and 11 deletions

View File

@@ -73,6 +73,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Comprehensive input schema definitions - Comprehensive input schema definitions
- Security audit of parameter handling - Security audit of parameter handling
## [1.1.9] - 2026-04-30
### Fixed
- **Images returned as MCP image content type**: Updated binary file handling so image files (PNG, JPG, JPEG, GIF, WEBP, SVG) are returned as `{ type: "image", data: "<base64>", mimeType: "..." }` per the MCP 2025-11-25 spec for image content in tool results
- Non-image binary files (PDF, ZIP, DOCX, etc.) continue to use the embedded resource format `{ type: "resource", resource: { uri, mimeType, blob } }`
## [1.1.8] - 2026-04-30
### Fixed
- **Binary files returned as MCP embedded resource**: Updated issue #9 fix to use the proper MCP `EmbeddedResource` format instead of a `BASE64:` text prefix
- Binary files are now returned as `{ type: "resource", resource: { uri, mimeType, blob } }`
- `uri` is constructed as `obsidian://<vault>/<path>`
- `mimeType` is detected from the file extension (PDF, ZIP, images, Office formats, audio/video; defaults to `application/octet-stream`)
- `blob` contains the base64-encoded raw bytes
## [1.1.7] - 2026-04-30 ## [1.1.7] - 2026-04-30
### Fixed ### Fixed
@@ -160,6 +175,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Version History ## Version History
- **1.1.8** - Bug fix release: Binary files returned as MCP embedded resource (fixes #9)
- **1.1.7** - Bug fix release: Binary files returned as base64 in `obsidian_read_note` (fixes #9) - **1.1.7** - Bug fix release: Binary files returned as base64 in `obsidian_read_note` (fixes #9)
- **1.1.6** - Bug fix release: Clarify `name` vs `path` semantics in `obsidian_create_note` (fixes #8) - **1.1.6** - Bug fix release: Clarify `name` vs `path` semantics in `obsidian_create_note` (fixes #8)
- **1.1.5** - Bug fix release: Preserve `<` and `>` in note content for Mermaid/HTML (fixes #7) - **1.1.5** - Bug fix release: Preserve `<` and `>` in note content for Mermaid/HTML (fixes #7)
@@ -173,6 +189,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Search & Discovery (12 tools) - Search & Discovery (12 tools)
- Task & Property Management (8 tools) - Task & Property Management (8 tools)
[1.1.8]: https://git.mortons.site/Peter.Morton/obsidian-mcp/releases/tag/v1.1.8
[1.1.7]: https://git.mortons.site/Peter.Morton/obsidian-mcp/releases/tag/v1.1.7 [1.1.7]: https://git.mortons.site/Peter.Morton/obsidian-mcp/releases/tag/v1.1.7
[1.1.6]: https://git.mortons.site/Peter.Morton/obsidian-mcp/releases/tag/v1.1.6 [1.1.6]: https://git.mortons.site/Peter.Morton/obsidian-mcp/releases/tag/v1.1.6
[1.1.5]: https://git.mortons.site/Peter.Morton/obsidian-mcp/releases/tag/v1.1.5 [1.1.5]: https://git.mortons.site/Peter.Morton/obsidian-mcp/releases/tag/v1.1.5

View File

@@ -1,7 +1,7 @@
{ {
"manifest_version": "0.3", "manifest_version": "0.3",
"name": "obsidian-mcp", "name": "obsidian-mcp",
"version": "1.1.7", "version": "1.1.9",
"display_name": "Obsidian CLI Bundle", "display_name": "Obsidian CLI Bundle",
"description": "MCP Bundle for Obsidian CLI - Enable AI assistants to manage Obsidian vaults through conversational interface", "description": "MCP Bundle for Obsidian CLI - Enable AI assistants to manage Obsidian vaults through conversational interface",
"long_description": "This MCP bundle provides a comprehensive set of tools for AI assistants to interact with and manage Obsidian vaults. It includes capabilities for creating, reading, updating, and deleting notes, managing links and tags, handling tasks, and more. With this bundle, AI assistants can seamlessly integrate with Obsidian to help users organize their knowledge and workflows.", "long_description": "This MCP bundle provides a comprehensive set of tools for AI assistants to interact with and manage Obsidian vaults. It includes capabilities for creating, reading, updating, and deleting notes, managing links and tags, handling tasks, and more. With this bundle, AI assistants can seamlessly integrate with Obsidian to help users organize their knowledge and workflows.",

View File

@@ -1,6 +1,6 @@
{ {
"name": "obsidian-mcp", "name": "obsidian-mcp",
"version": "1.1.7", "version": "1.1.9",
"description": "MCP Bundle for Obsidian CLI - Enable AI assistants to manage Obsidian vaults through Model Context Protocol", "description": "MCP Bundle for Obsidian CLI - Enable AI assistants to manage Obsidian vaults through Model Context Protocol",
"type": "module", "type": "module",
"main": "dist/index.js", "main": "dist/index.js",

View File

@@ -40,6 +40,34 @@ function isBinaryContent(buf: Buffer): boolean {
return nonPrintable / sample.length > 0.1; return nonPrintable / sample.length > 0.1;
} }
/** Map common file extensions to MIME types */
const MIME_TYPES: Record<string, string> = {
pdf: 'application/pdf',
zip: 'application/zip',
gz: 'application/gzip',
tar: 'application/x-tar',
png: 'image/png',
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
gif: 'image/gif',
webp: 'image/webp',
svg: 'image/svg+xml',
mp3: 'audio/mpeg',
mp4: 'video/mp4',
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
};
function getMimeType(filePath: string): string {
const ext = filePath.split('.').pop()?.toLowerCase() ?? '';
return MIME_TYPES[ext] ?? 'application/octet-stream';
}
function isImageMimeType(mimeType: string): boolean {
return mimeType.startsWith('image/');
}
/** /**
* Register all file operation tools * Register all file operation tools
*/ */
@@ -151,7 +179,7 @@ export async function registerFileOperationTools(server: ObsidianMCPServer): Pro
// T031: Read note tool // T031: Read note tool
server.registerTool( server.registerTool(
'obsidian_read_note', 'obsidian_read_note',
'Read the content of a note from the Obsidian vault. Specify either the note name (file) or full path (path). For large files (e.g. PDFs), use max_chars and offset to read in chunks and avoid exceeding context limits. Binary files (ZIP, images, compiled files, etc.) are automatically detected and returned as a base64-encoded string prefixed with "BASE64:" — the client must base64-decode the value to recover the original binary content.', 'Read the content of a note from the Obsidian vault. Specify either the note name (file) or full path (path). For large files (e.g. PDFs), use max_chars and offset to read in chunks and avoid exceeding context limits. Binary files are automatically detected: images (PNG, JPG, GIF, WEBP, SVG) are returned as MCP image content (type: "image") with base64-encoded data and mimeType; all other binary files (ZIP, PDF, DOCX, etc.) are returned as MCP embedded resource content (type: "resource") with uri, mimeType, and base64-encoded blob.',
{ {
type: 'object', type: 'object',
properties: { properties: {
@@ -174,7 +202,7 @@ export async function registerFileOperationTools(server: ObsidianMCPServer): Pro
}, },
}, },
createToolHandler( createToolHandler(
'Read the content of a note. Binary files are returned as a base64-encoded string prefixed with "BASE64:" — decode it to recover the original binary content.', 'Read the content of a note. Images (PNG, JPG, GIF, WEBP, SVG) are returned as MCP image content (type: "image") with base64-encoded data. Other binary files are returned as MCP embedded resource content (type: "resource") with uri, mimeType, and base64-encoded blob.',
{ {
type: 'object', type: 'object',
properties: { properties: {
@@ -209,13 +237,37 @@ export async function registerFileOperationTools(server: ObsidianMCPServer): Pro
const result = await executeObsidianCommandBinary('read', cmdArgs); const result = await executeObsidianCommandBinary('read', cmdArgs);
handleCLIResult(result, { operation: 'read_note', identifier: sanitized.file || sanitized.path }); handleCLIResult(result, { operation: 'read_note', identifier: sanitized.file || sanitized.path });
// Detect binary content from the raw buffer and return as base64 // Detect binary content from the raw buffer and return as spec-appropriate MCP content
if (result.stdoutBuffer && isBinaryContent(result.stdoutBuffer)) { if (result.stdoutBuffer && isBinaryContent(result.stdoutBuffer)) {
const identifier = sanitized.file || sanitized.path as string;
const mimeType = getMimeType(identifier);
const base64 = result.stdoutBuffer.toString('base64');
// Images use the MCP image content type per spec
if (isImageMimeType(mimeType)) {
return {
content: [
{
type: 'image' as const,
data: base64,
mimeType,
},
],
};
}
// All other binary files use the embedded resource content type per spec
const vaultName = process.env.OBSIDIAN_VAULT ?? 'vault';
const uri = `obsidian://${encodeURIComponent(vaultName)}/${encodeURIComponent(identifier)}`;
return { return {
content: [ content: [
{ {
type: 'text', type: 'resource' as const,
text: `BASE64:${result.stdoutBuffer.toString('base64')}`, resource: {
uri,
mimeType,
blob: base64,
},
}, },
], ],
}; };

View File

@@ -16,10 +16,12 @@ export interface ToolInput {
* Successful tool output * Successful tool output
*/ */
export interface ToolOutput { export interface ToolOutput {
content: Array<{ content: Array<
type: 'text'; | { type: 'text'; text: string }
text: string; | { type: 'image'; data: string; mimeType: string }
}>; | { type: 'audio'; data: string; mimeType: string }
| { type: 'resource'; resource: { uri: string; mimeType?: string; blob: string } }
>;
isError?: false; isError?: false;
} }