2 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
5 changed files with 35 additions and 7 deletions

View File

@@ -73,6 +73,12 @@ 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 ## [1.1.8] - 2026-04-30
### Fixed ### Fixed

View File

@@ -1,7 +1,7 @@
{ {
"manifest_version": "0.3", "manifest_version": "0.3",
"name": "obsidian-mcp", "name": "obsidian-mcp",
"version": "1.1.8", "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.8", "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

@@ -64,6 +64,10 @@ function getMimeType(filePath: string): string {
return MIME_TYPES[ext] ?? 'application/octet-stream'; 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
*/ */
@@ -175,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, PDFs, etc.) are automatically detected and returned as an MCP embedded resource with a uri, mimeType, and base64-encoded blob field instead of plain text.', '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: {
@@ -198,7 +202,7 @@ export async function registerFileOperationTools(server: ObsidianMCPServer): Pro
}, },
}, },
createToolHandler( createToolHandler(
'Read the content of a note. Binary files are returned as an MCP embedded resource (type: "resource") with uri, mimeType, and a base64-encoded blob field.', '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: {
@@ -233,12 +237,28 @@ 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 MCP resource // 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 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 vaultName = process.env.OBSIDIAN_VAULT ?? 'vault';
const uri = `obsidian://${encodeURIComponent(vaultName)}/${encodeURIComponent(identifier)}`; const uri = `obsidian://${encodeURIComponent(vaultName)}/${encodeURIComponent(identifier)}`;
const mimeType = getMimeType(identifier);
return { return {
content: [ content: [
{ {
@@ -246,7 +266,7 @@ export async function registerFileOperationTools(server: ObsidianMCPServer): Pro
resource: { resource: {
uri, uri,
mimeType, mimeType,
blob: result.stdoutBuffer.toString('base64'), blob: base64,
}, },
}, },
], ],

View File

@@ -18,6 +18,8 @@ export interface ToolInput {
export interface ToolOutput { export interface ToolOutput {
content: Array< content: Array<
| { type: 'text'; text: string } | { type: 'text'; text: string }
| { type: 'image'; data: string; mimeType: string }
| { type: 'audio'; data: string; mimeType: string }
| { type: 'resource'; resource: { uri: string; mimeType?: string; blob: string } } | { type: 'resource'; resource: { uri: string; mimeType?: string; blob: string } }
>; >;
isError?: false; isError?: false;