diff --git a/src/mcp_obsidian/obsidian.py b/src/mcp_obsidian/obsidian.py index a01b3aa..024145a 100644 --- a/src/mcp_obsidian/obsidian.py +++ b/src/mcp_obsidian/obsidian.py @@ -145,6 +145,22 @@ class Obsidian(): return None return self._safe_call(call_fn) + + def put_content(self, filepath: str, content: str) -> Any: + url = f"{self.get_base_url()}/vault/{filepath}" + + def call_fn(): + response = requests.put( + url, + headers=self._get_headers() | {'Content-Type': 'text/markdown'}, + data=content, + verify=self.verify_ssl, + timeout=self.timeout + ) + response.raise_for_status() + return None + + return self._safe_call(call_fn) def delete_file(self, filepath: str) -> Any: """Delete a file or directory from the vault. diff --git a/src/mcp_obsidian/server.py b/src/mcp_obsidian/server.py index 79cb2a6..41a4f76 100644 --- a/src/mcp_obsidian/server.py +++ b/src/mcp_obsidian/server.py @@ -47,6 +47,7 @@ add_tool_handler(tools.GetFileContentsToolHandler()) add_tool_handler(tools.SearchToolHandler()) add_tool_handler(tools.PatchContentToolHandler()) add_tool_handler(tools.AppendContentToolHandler()) +add_tool_handler(tools.PutContentToolHandler()) add_tool_handler(tools.DeleteFileToolHandler()) add_tool_handler(tools.ComplexSearchToolHandler()) add_tool_handler(tools.BatchGetFileContentsToolHandler()) diff --git a/src/mcp_obsidian/tools.py b/src/mcp_obsidian/tools.py index ab6654f..f942cae 100644 --- a/src/mcp_obsidian/tools.py +++ b/src/mcp_obsidian/tools.py @@ -286,6 +286,46 @@ class PatchContentToolHandler(ToolHandler): text=f"Successfully patched content in {args['filepath']}" ) ] + +class PutContentToolHandler(ToolHandler): + def __init__(self): + super().__init__("obsidian_put_content") + + def get_tool_description(self): + return Tool( + name=self.name, + description="Create a new file in your vault or update the content of an existing one in your vault.", + inputSchema={ + "type": "object", + "properties": { + "filepath": { + "type": "string", + "description": "Path to the relevant file (relative to your vault root)", + "format": "path" + }, + "content": { + "type": "string", + "description": "Content of the file you would like to upload" + } + }, + "required": ["filepath", "content"] + } + ) + + def run_tool(self, args: dict) -> Sequence[TextContent | ImageContent | EmbeddedResource]: + if "filepath" not in args or "content" not in args: + raise RuntimeError("filepath and content arguments required") + + api = obsidian.Obsidian(api_key=api_key, host=obsidian_host) + api.put_content(args.get("filepath", ""), args["content"]) + + return [ + TextContent( + type="text", + text=f"Successfully uploaded content to {args['filepath']}" + ) + ] + class DeleteFileToolHandler(ToolHandler): def __init__(self):