Compare commits

47 Commits

Author SHA1 Message Date
c58449ca85 fix: add full support for OBSIDIAN_HOST env var; URL parsing helpers; central API factory; README updates (merge of upstream PR #52) 2025-08-16 18:52:25 +00:00
Markus Pfundstein
4aac5c2b87 Merge pull request #44 from shipurjan/feature/add-put-content-tool
add obsidian_put_content tool
2025-06-28 11:04:04 +02:00
Markus Pfundstein
a7469914ec Merge pull request #31 from sizhky/patch-1
Update README.md
2025-06-28 11:03:43 +02:00
Markus Pfundstein
551d3037bd Merge pull request #58 from sfedyakov/complex-query-examples
Add JsonQuery examples to the description of Complex Query
2025-06-28 11:03:24 +02:00
Markus Pfundstein
49b51323b0 Merge pull request #59 from TheEpTic/main
Update obsidian.py
2025-06-28 11:03:03 +02:00
TheEpTic
ccd3be1adf Update obsidian.py
Add protocol and remove hardcoded localhost
2025-06-24 01:35:28 +01:00
Stanislav Fediakov
4d96b36a6b Add JsonQuery examples to the description of Complex Query for it to work more reliably 2025-06-21 20:57:50 +04:00
Markus Pfundstein
306eb2b406 Merge pull request #55 from vicampuzano/Metadata-for-periodic-notes
Adding a parameter to optionally retrieve metadata for periodic notes, in addition to just the content.
2025-06-19 11:36:35 +02:00
Markus Pfundstein
7211cb0b2f Merge pull request #54 from TheConnMan/task/port-support
Add a port override env var
2025-06-19 11:36:00 +02:00
Victor Campuzano
e6a02957e1 Adding a parameter to optionally retrieve metadata for periodic notes, in addition to just the content. 2025-06-12 15:57:07 +02:00
TheConnMan
a32eb48095 Add a port override env var 2025-06-08 06:24:23 -04:00
Cyprian Zdebski
d42d7454be Update descriptions 2025-04-17 22:58:24 +02:00
Cyprian Zdebski
d7f16416dd add obsidian_put_content tool 2025-04-17 22:51:56 +02:00
Markus Pfundstein
0133bdd91f Merge pull request #37 from felipemeres/feature/add-host-env-var
feat: add host environment variable support
2025-04-14 13:24:19 +02:00
Felipe Meres
8601e44502 feat: add host environment variable support 2025-04-10 08:24:44 -04:00
sizhky
3705e37911 Update README.md
Add a helpful statement below claude server config when claude sometimes fails to discover uvx in the system
2025-04-04 11:07:33 +05:30
Markus Pfundstein
8ddc50cc2f Merge pull request #27 from thomato/add-delete-function
Implement 'Delete file' functionality
2025-04-01 21:32:22 +02:00
Markus Pfundstein
87059bd9ba Merge pull request #30 from tibbon/patch-1
Add missing parens to README.md
2025-04-01 21:31:26 +02:00
David Fisher
f563e9a43c Add missing parens to README.md
Example JSON is broken as-is. This fixes it.
2025-04-01 13:51:18 -04:00
Nando Thomassen
c33c711c68 Implement 'Delete file/directory' functionality
The MCP server now supports safe deletion of files and directories from
the Obsidian vault. A required confirmation parameter prevents accidental
deletions.
2025-03-29 22:29:01 +01:00
Markus Pfundstein
0de513e993 Merge pull request #23 from jevy/period-tools
Update openapi.yaml and adding recent and periodic notes
2025-03-26 19:30:44 +01:00
Jevin Maltais
4295a17ab8 Updating tools too 2025-03-22 08:53:59 -04:00
Jevin Maltais
4b518efea7 Simplified queries 2025-03-22 08:50:04 -04:00
Jevin Maltais
b4596a6028 Recent check works 2025-03-22 08:44:14 -04:00
Jevin Maltais
ed9bffe128 Updating openapi and creating periodic tools 2025-03-22 07:59:36 -04:00
Markus Pfundstein
f44ebaa441 Merge pull request #16 from bathrobe/main
added get batch of file contents
2025-02-09 21:52:14 +01:00
joe
6b02ecdf71 added get batch of file contents 2025-02-01 12:15:03 -05:00
Markus Pfundstein
33c931280f Merge pull request #13 from txbm/main
Update action names with obsidian_ prefix
2025-01-03 11:12:28 +01:00
Peter M. Elias
b219c45d17 Update action names with obsidian_ prefix 2024-12-26 23:12:16 -08:00
Markus Pfundstein
95de950588 Merge pull request #11 from punkpeye/patch-1
add MCP server badge
2024-12-19 13:04:46 +01:00
Markus Pfundstein
befe6fa403 Create LICENSE 2024-12-19 13:04:36 +01:00
Markus Pfundstein
c4765c3fc5 Merge pull request #8 from Csaba8472/lower-python-version
lower python version
2024-12-19 13:03:08 +01:00
Frank Fiegel
f14b4085e2 add MCP server badge
This PR adds a badge for the MCP server for Obsidian server listing in Glama MCP server directory.

<a href="https://glama.ai/mcp/servers/3wko1bhuek"><img width="380" height="200" src="https://glama.ai/mcp/servers/3wko1bhuek/badge" alt="server for Obsidian MCP server" /></a>

Glama performs regular codebase and documentation scans to:

* Confirm that the MCP server is working as expected
* Confirm that there are no obvious security issues with dependencies of the server
* Extract server characteristics such as tools, resources, prompts, and required parameters.

This badge helps your users to quickly asses that the MCP server is safe, server capabilities, and instructions for installing the server.
2024-12-17 09:26:20 -05:00
Csaba8472
07551d0b5a lower python version 2024-12-14 21:53:19 +01:00
Markus Pfundstein
1a3b173e18 more info 2024-12-05 10:15:10 +01:00
Markus Pfundstein
c7cd449c7f updated README 2024-12-05 10:11:51 +01:00
Markus Pfundstein
38845f30c8 corrected server name. added cwd logging when .env is not found 2024-12-05 10:03:00 +01:00
Markus Pfundstein
90ad7a682d bumped mcp 2024-12-04 10:25:06 +01:00
Markus Pfundstein
a27363c643 renamed to mcp-obsidian 2024-12-04 10:22:24 +01:00
Markus Pfundstein
616309c2cb added pyright and fixed type errors 2024-12-03 21:36:54 +01:00
Markus Pfundstein
e70e940c57 Merge pull request #4 from 7shi/add-timeout
Add timeout settings to HTTP requests
2024-12-01 20:01:55 +01:00
7shi
120c435723 Adjust timeout to align with MCP client timeouts 2024-12-02 02:59:21 +09:00
7shi
944819a3a1 Add timeout settings to all requests in Obsidian Local REST API 2024-12-01 22:36:06 +09:00
Markus Pfundstein
e3d7db5d20 Merge pull request #2 from 7shi/fix-quote
URL encode target header in PATCH requests
2024-12-01 10:50:13 +01:00
7shi
ab7ee87e56 URL encode target header in PATCH requests 2024-12-01 05:15:47 +09:00
Markus Pfundstein
0aca93fc62 Merge pull request #1 from eltociear/patch-1
chore: update openapi.yaml
2024-11-30 09:31:42 +01:00
Ikko Eltociear Ashimine
f3d247af0a chore: update openapi.yaml
yoru -> your
2024-11-30 13:09:11 +09:00
4 changed files with 33 additions and 93 deletions

View File

@@ -11,7 +11,6 @@ class Obsidian():
protocol: str = os.getenv('OBSIDIAN_PROTOCOL', 'https').lower(),
host: str = str(os.getenv('OBSIDIAN_HOST', '127.0.0.1')),
port: int = int(os.getenv('OBSIDIAN_PORT', '27124')),
path: str = '',
verify_ssl: bool = False,
):
self.api_key = api_key
@@ -23,7 +22,6 @@ class Obsidian():
self.host = host
self.port = port
self.path = path.rstrip('/')
self.verify_ssl = verify_ssl
self.timeout = (3, 6)
@@ -54,7 +52,6 @@ class Obsidian():
protocol = parsed.scheme
host = parsed.hostname
port = parsed.port
path = parsed.path
# Set default ports based on protocol if not specified
if port is None:
@@ -65,21 +62,20 @@ class Obsidian():
protocol=protocol,
host=host,
port=port,
path=path,
verify_ssl=verify_ssl
)
except Exception as e:
raise ValueError(f"Failed to parse OBSIDIAN_HOST URL '{url}': {str(e)}")
@staticmethod
def parse_host_config(host_config: str) -> Tuple[str, str, int, str]:
def parse_host_config(host_config: str) -> Tuple[str, str, int]:
"""Parse host configuration string.
Args:
host_config: Either a full URL (http://host:port) or just hostname/IP
Returns:
Tuple of (protocol, host, port, path)
Tuple of (protocol, host, port)
"""
if '://' in host_config:
# Full URL format
@@ -87,28 +83,16 @@ class Obsidian():
protocol = parsed.scheme or 'https'
host = parsed.hostname or '127.0.0.1'
port = parsed.port or (27124 if protocol == 'https' else 27123)
path = parsed.path
else:
# Support legacy formats
# 1) hostname/IP only
# 2) hostname:port (no protocol)
if ':' in host_config:
# Treat as host:port and default protocol to https
parsed = urlparse(f'https://{host_config}')
protocol = 'https'
host = parsed.hostname or '127.0.0.1'
port = parsed.port or 27124
path = ''
else:
# Legacy hostname/IP only format
protocol = 'https'
host = host_config
port = 27124
path = ''
return protocol, host, port, path
return protocol, host, port
def get_base_url(self) -> str:
return f'{self.protocol}://{self.host}:{self.port}{self.path}'
return f'{self.protocol}://{self.host}:{self.port}'
def _get_headers(self) -> dict:
headers = {
@@ -313,7 +297,7 @@ class Obsidian():
Returns:
List of recent periodic notes
"""
url = f"{self.get_base_url()}/periodic/{period}/recent/"
url = f"{self.get_base_url()}/periodic/{period}/recent"
params = {
"limit": limit,
"includeContent": include_content

View File

@@ -5,8 +5,7 @@ from functools import lru_cache
from typing import Any
import os
from dotenv import load_dotenv
from mcp.server import Server as MCPServer
from contextlib import asynccontextmanager
from mcp.server import Server
from mcp.types import (
Tool,
TextContent,
@@ -16,7 +15,6 @@ from mcp.types import (
load_dotenv()
from . import obsidian
from . import tools
# Load environment variables
@@ -25,17 +23,11 @@ from . import tools
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("mcp-obsidian")
@asynccontextmanager
async def lifespan(app: MCPServer):
api_key = os.getenv("OBSIDIAN_API_KEY")
if not api_key:
raise ValueError(f"OBSIDSIAN_API_KEY environment variable required. Working directory: {os.getcwd()}")
yield
api_key = os.getenv("OBSIDIAN_API_KEY")
if not api_key:
raise ValueError(f"OBSIDIAN_API_KEY environment variable required. Working directory: {os.getcwd()}")
app = MCPServer(
"mcp-obsidian",
lifespan=lifespan,
)
app = Server("mcp-obsidian")
tool_handlers = {}
def add_tool_handler(tool_class: tools.ToolHandler):

View File

@@ -9,6 +9,19 @@ import json
import os
from . import obsidian
# Load environment variables
api_key = os.getenv("OBSIDIAN_API_KEY", "")
obsidian_host = os.getenv("OBSIDIAN_HOST", "https://127.0.0.1:27124")
if api_key == "":
raise ValueError(f"OBSIDIAN_API_KEY environment variable required. Working directory: {os.getcwd()}")
# Parse the OBSIDIAN_HOST configuration at module level for validation
try:
protocol, host, port = obsidian.Obsidian.parse_host_config(obsidian_host)
except ValueError as e:
raise ValueError(f"Invalid OBSIDIAN_HOST configuration: {str(e)}")
def create_obsidian_api() -> obsidian.Obsidian:
"""Factory function to create Obsidian API instances.
@@ -21,26 +34,12 @@ def create_obsidian_api() -> obsidian.Obsidian:
Raises:
Exception: If configuration is invalid or instance creation fails
"""
# Load environment variables
api_key = os.getenv("OBSIDIAN_API_KEY", "")
obsidian_host = os.getenv("OBSIDIAN_HOST", "https://127.0.0.1:27124")
if api_key == "":
raise ValueError(f"OBSIDIAN_API_KEY environment variable required. Working directory: {os.getcwd()}")
# Parse the OBSIDIAN_HOST configuration at module level for validation
try:
protocol, host, port, path = obsidian.Obsidian.parse_host_config(obsidian_host)
except ValueError as e:
raise ValueError(f"Invalid OBSIDIAN_HOST configuration: {str(e)}")
try:
return obsidian.Obsidian(
api_key=api_key,
protocol=protocol,
host=host,
port=port,
path=path,
verify_ssl=False # Default to False for local development
)
except Exception as e:
@@ -344,7 +343,7 @@ class PutContentToolHandler(ToolHandler):
if "filepath" not in args or "content" not in args:
raise RuntimeError("filepath and content arguments required")
api = create_obsidian_api()
api = obsidian.Obsidian(api_key=api_key, host=obsidian_host)
api.put_content(args.get("filepath", ""), args["content"])
return [
@@ -542,8 +541,13 @@ class PeriodicNotesToolHandler(ToolHandler):
if type not in valid_types:
raise RuntimeError(f"Invalid type: {type}. Must be one of: {', '.join(valid_types)}")
<<<<<<< ours
api = create_obsidian_api()
content = api.get_periodic_note(period)
=======
api = create_obsidian_api()
content = api.get_periodic_note(period)
>>>>>>> theirs
return [
TextContent(

View File

@@ -1,40 +0,0 @@
import os
import sys
import unittest
# Add the src directory to the Python path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../src')))
from mcp_obsidian import obsidian
class TestObsidianIntegration(unittest.TestCase):
def setUp(self):
"""Set up the environment variables for the test."""
os.environ['OBSIDIAN_API_KEY'] = 'REDACTED_API_KEY'
os.environ['OBSIDIAN_HOST'] = 'http://obsidian.obsidian.svc.cluster.local:27123'
def test_connection(self):
"""Test the connection to the Obsidian API."""
try:
protocol, host, port, path = obsidian.Obsidian.parse_host_config(os.environ['OBSIDIAN_HOST'])
api = obsidian.Obsidian(
api_key=os.environ['OBSIDIAN_API_KEY'],
protocol=protocol,
host=host,
port=port,
path=path,
verify_ssl=False
)
# Use a basic API call to verify the connection
files = api.list_files_in_vault()
self.assertIsNotNone(files)
print("Successfully connected to Obsidian API and listed files.")
except Exception as e:
self.fail(f"Failed to connect to Obsidian API: {e}")
if __name__ == '__main__':
unittest.main()