vault-bridge 1.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,122 @@
1
+ Metadata-Version: 2.4
2
+ Name: vault-bridge
3
+ Version: 1.0.3
4
+ Summary: Bridge your AI assistant to local knowledge vaults — read, write, and search notes without cloud dependencies
5
+ Requires-Python: >=3.11
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: fastmcp-slim[server]<4.0,>=3.4
8
+ Requires-Dist: pyyaml>=6.0
9
+ Requires-Dist: structlog>=24.1
10
+ Requires-Dist: pydantic-settings>=2.3
11
+ Provides-Extra: dev
12
+ Requires-Dist: pytest>=8.0; extra == "dev"
13
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
14
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
15
+ Requires-Dist: ruff>=0.4; extra == "dev"
16
+ Requires-Dist: mypy>=1.10; extra == "dev"
17
+ Requires-Dist: types-PyYAML; extra == "dev"
18
+
19
+ # Vault Bridge
20
+
21
+ Bridge your AI assistant to local knowledge vaults — read, write, search, and manage notes without cloud dependencies.
22
+
23
+ ## Features
24
+
25
+ - **read_note** — Read a note with optional frontmatter parsing
26
+ - **write_note** — Write notes with YAML frontmatter support
27
+ - **list_notes** — List notes with glob filtering and recursion
28
+ - **search_notes** — Regex search across vault content
29
+ - **search_frontmatter** — Query notes by frontmatter key/value
30
+ - **get_daily_note** — Get or create daily notes with templates
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ pip install vault-bridge
36
+ ```
37
+
38
+ ## Usage
39
+
40
+ ```bash
41
+ vault-bridge
42
+ ```
43
+
44
+ Or configure in your MCP client:
45
+
46
+ ```json
47
+ {
48
+ "mcpServers": {
49
+ "vault": {
50
+ "command": "vault-bridge",
51
+ "env": {
52
+ "VAULT_BRIDGE_VAULT_PATH": "C:/path/to/vault"
53
+ }
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ ## Configuration
60
+
61
+ | Environment Variable | Default | Description |
62
+ |---------------------|---------|-------------|
63
+ | `VAULT_BRIDGE_VAULT_PATH` | Current directory | Path to vault root |
64
+ | `VAULT_BRIDGE_MAX_FILE_SIZE` | 10MB | Max file size for operations |
65
+ | `VAULT_BRIDGE_DEFAULT_ENCODING` | utf-8 | Text encoding |
66
+ | `VAULT_BRIDGE_INDEX_FRONTMATTER` | true | Parse YAML frontmatter |
67
+ | `VAULT_BRIDGE_FOLLOW_SYMLINKS` | false | Follow symlinks |
68
+
69
+ ## Tool Reference
70
+
71
+ ### read_note
72
+ ```json
73
+ {
74
+ "path": "Projects/roadmap.md",
75
+ "include_frontmatter": true
76
+ }
77
+ ```
78
+
79
+ ### write_note
80
+ ```json
81
+ {
82
+ "path": "Projects/new-idea.md",
83
+ "content": "# New Idea\n\nDetails here...",
84
+ "frontmatter": { "tags": ["idea", "draft"], "status": "wip" }
85
+ }
86
+ ```
87
+
88
+ ### list_notes
89
+ ```json
90
+ {
91
+ "path": "Projects",
92
+ "glob_pattern": "**/*.md",
93
+ "recursive": true
94
+ }
95
+ ```
96
+
97
+ ### search_notes
98
+ ```json
99
+ {
100
+ "pattern": "MCP",
101
+ "path": ".",
102
+ "max_results": 50
103
+ }
104
+ ```
105
+
106
+ ### search_frontmatter
107
+ ```json
108
+ {
109
+ "key": "status",
110
+ "value": "done",
111
+ "operator": "eq"
112
+ }
113
+ ```
114
+
115
+ ### get_daily_note
116
+ ```json
117
+ {
118
+ "date": "2026-08-21",
119
+ "folder": "Daily Notes",
120
+ "create_if_missing": true
121
+ }
122
+ ```
@@ -0,0 +1,104 @@
1
+ # Vault Bridge
2
+
3
+ Bridge your AI assistant to local knowledge vaults — read, write, search, and manage notes without cloud dependencies.
4
+
5
+ ## Features
6
+
7
+ - **read_note** — Read a note with optional frontmatter parsing
8
+ - **write_note** — Write notes with YAML frontmatter support
9
+ - **list_notes** — List notes with glob filtering and recursion
10
+ - **search_notes** — Regex search across vault content
11
+ - **search_frontmatter** — Query notes by frontmatter key/value
12
+ - **get_daily_note** — Get or create daily notes with templates
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ pip install vault-bridge
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ```bash
23
+ vault-bridge
24
+ ```
25
+
26
+ Or configure in your MCP client:
27
+
28
+ ```json
29
+ {
30
+ "mcpServers": {
31
+ "vault": {
32
+ "command": "vault-bridge",
33
+ "env": {
34
+ "VAULT_BRIDGE_VAULT_PATH": "C:/path/to/vault"
35
+ }
36
+ }
37
+ }
38
+ }
39
+ ```
40
+
41
+ ## Configuration
42
+
43
+ | Environment Variable | Default | Description |
44
+ |---------------------|---------|-------------|
45
+ | `VAULT_BRIDGE_VAULT_PATH` | Current directory | Path to vault root |
46
+ | `VAULT_BRIDGE_MAX_FILE_SIZE` | 10MB | Max file size for operations |
47
+ | `VAULT_BRIDGE_DEFAULT_ENCODING` | utf-8 | Text encoding |
48
+ | `VAULT_BRIDGE_INDEX_FRONTMATTER` | true | Parse YAML frontmatter |
49
+ | `VAULT_BRIDGE_FOLLOW_SYMLINKS` | false | Follow symlinks |
50
+
51
+ ## Tool Reference
52
+
53
+ ### read_note
54
+ ```json
55
+ {
56
+ "path": "Projects/roadmap.md",
57
+ "include_frontmatter": true
58
+ }
59
+ ```
60
+
61
+ ### write_note
62
+ ```json
63
+ {
64
+ "path": "Projects/new-idea.md",
65
+ "content": "# New Idea\n\nDetails here...",
66
+ "frontmatter": { "tags": ["idea", "draft"], "status": "wip" }
67
+ }
68
+ ```
69
+
70
+ ### list_notes
71
+ ```json
72
+ {
73
+ "path": "Projects",
74
+ "glob_pattern": "**/*.md",
75
+ "recursive": true
76
+ }
77
+ ```
78
+
79
+ ### search_notes
80
+ ```json
81
+ {
82
+ "pattern": "MCP",
83
+ "path": ".",
84
+ "max_results": 50
85
+ }
86
+ ```
87
+
88
+ ### search_frontmatter
89
+ ```json
90
+ {
91
+ "key": "status",
92
+ "value": "done",
93
+ "operator": "eq"
94
+ }
95
+ ```
96
+
97
+ ### get_daily_note
98
+ ```json
99
+ {
100
+ "date": "2026-08-21",
101
+ "folder": "Daily Notes",
102
+ "create_if_missing": true
103
+ }
104
+ ```
@@ -0,0 +1,70 @@
1
+ [project]
2
+ name = "vault-bridge"
3
+ version = "1.0.3"
4
+ description = "Bridge your AI assistant to local knowledge vaults — read, write, and search notes without cloud dependencies"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ dependencies = [
8
+ "fastmcp-slim[server]>=3.4,<4.0",
9
+ "pyyaml>=6.0",
10
+ "structlog>=24.1",
11
+ "pydantic-settings>=2.3",
12
+ ]
13
+
14
+ [project.scripts]
15
+ vault-bridge = "obsidian_mcp.server:main"
16
+
17
+ [project.optional-dependencies]
18
+ dev = [
19
+ "pytest>=8.0",
20
+ "pytest-asyncio>=0.23",
21
+ "pytest-cov>=5.0",
22
+ "ruff>=0.4",
23
+ "mypy>=1.10",
24
+ "types-PyYAML",
25
+ ]
26
+
27
+ [tool.ruff]
28
+ target-version = "py311"
29
+ line-length = 100
30
+ select = ["E", "F", "I", "UP", "W", "C4", "PT", "PTH", "SIM", "T10", "ARG", "RUF"]
31
+ ignore = [
32
+ "S101",
33
+ "PTH123",
34
+ "ARG001",
35
+ "ARG002",
36
+ "TRY003",
37
+ "PLC0415",
38
+ "PLR2004",
39
+ "ARG002",
40
+ "SIM102",
41
+ "INP001",
42
+ "F401",
43
+ "W292",
44
+ "S603",
45
+ "PLW1510",
46
+ "PLW0603",
47
+ "W293",
48
+ "W291",
49
+ "E501",
50
+ "UP015",
51
+ "RUF022",
52
+ "I001",
53
+ "BLE001",
54
+ ]
55
+ fixable = ["ALL"]
56
+ unfixable = []
57
+
58
+ [tool.ruff.format]
59
+ quote-style = "double"
60
+ indent-style = "space"
61
+
62
+ [tool.mypy]
63
+ python_version = "3.11"
64
+ warn_return_any = true
65
+ warn_unused_configs = true
66
+ disallow_untyped_defs = true
67
+ strict = true
68
+
69
+ [tool.hatch.build.targets.wheel]
70
+ packages = ["src/obsidian_mcp"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,8 @@
1
+ """Obsidian MCP Server package."""
2
+
3
+ from .core import ObsidianCore
4
+ from .models import ObsidianConfig
5
+ from .server import create_mcp_server
6
+
7
+ __all__ = ["ObsidianConfig", "ObsidianCore", "create_mcp_server"]
8
+ __version__ = "1.0.2"
@@ -0,0 +1,169 @@
1
+ """Auto-configure CLI for vault-bridge — one-command setup like natestrong."""
2
+
3
+ import json
4
+ import sys
5
+ from pathlib import Path
6
+ from typing import Any, TypedDict
7
+
8
+ import click
9
+
10
+ from .logging import get_logger
11
+
12
+ logger = get_logger(__name__)
13
+
14
+
15
+ class ClientConfig(TypedDict):
16
+ config_path: Path
17
+ server_key: str
18
+ template: dict[str, Any]
19
+
20
+
21
+ CLIENT_CONFIGS: dict[str, ClientConfig] = {
22
+ "claude_desktop": {
23
+ "config_path": Path.home() / "AppData" / "Roaming" / "Claude" / "claude_desktop_config.json",
24
+ "server_key": "vault-bridge",
25
+ "template": {
26
+ "mcpServers": {
27
+ "vault-bridge": {
28
+ "command": "uvx",
29
+ "args": ["vault-bridge", "{vault_path}"],
30
+ }
31
+ }
32
+ },
33
+ },
34
+ "cursor": {
35
+ "config_path": Path.home() / ".cursor" / "mcp.json",
36
+ "server_key": "vault-bridge",
37
+ "template": {
38
+ "mcpServers": {
39
+ "vault-bridge": {
40
+ "command": "uvx",
41
+ "args": ["vault-bridge", "{vault_path}"],
42
+ }
43
+ }
44
+ },
45
+ },
46
+ "windsurf": {
47
+ "config_path": Path.home() / ".codeium" / "windsurf" / "mcp_config.json",
48
+ "server_key": "vault-bridge",
49
+ "template": {
50
+ "mcpServers": {
51
+ "vault-bridge": {
52
+ "command": "uvx",
53
+ "args": ["vault-bridge", "{vault_path}"],
54
+ }
55
+ }
56
+ },
57
+ },
58
+ }
59
+
60
+
61
+ def load_existing_config(config_path: Path) -> dict[str, Any]:
62
+ """Load existing JSON config or return empty."""
63
+ if config_path.exists():
64
+ try:
65
+ with open(config_path, encoding="utf-8") as f:
66
+ data: dict[str, Any] = json.load(f)
67
+ return data
68
+ except json.JSONDecodeError:
69
+ return {}
70
+ return {}
71
+
72
+
73
+ def save_config(config_path: Path, config: dict[str, Any]) -> None:
74
+ """Save JSON config with pretty formatting."""
75
+ config_path.parent.mkdir(parents=True, exist_ok=True)
76
+ with open(config_path, "w", encoding="utf-8") as f:
77
+ json.dump(config, f, indent=2)
78
+
79
+
80
+ def update_config_for_client(config: dict[str, Any], server_key: str, vault_path: str) -> dict[str, Any]:
81
+ """Update config with vault-bridge server entry."""
82
+ if "mcpServers" not in config:
83
+ config["mcpServers"] = {}
84
+
85
+ config["mcpServers"][server_key] = {
86
+ "command": "uvx",
87
+ "args": ["vault-bridge", vault_path],
88
+ }
89
+ return config
90
+
91
+
92
+ @click.command()
93
+ @click.option(
94
+ "--vault-path",
95
+ "-v",
96
+ required=True,
97
+ type=click.Path(exists=True, file_okay=False, dir_okay=True, path_type=Path),
98
+ help="Path to the Obsidian vault root directory.",
99
+ )
100
+ @click.option(
101
+ "--client",
102
+ "-c",
103
+ type=click.Choice(list(CLIENT_CONFIGS.keys())),
104
+ default="claude_desktop",
105
+ help="Target client to configure.",
106
+ )
107
+ @click.option(
108
+ "--force",
109
+ "-f",
110
+ is_flag=True,
111
+ help="Overwrite existing vault-bridge configuration.",
112
+ )
113
+ @click.option(
114
+ "--dry-run",
115
+ is_flag=True,
116
+ help="Show what would be changed without writing.",
117
+ )
118
+ def main(vault_path: Path, client: str, force: bool, dry_run: bool) -> int:
119
+ """Auto-configure vault-bridge for your AI client.
120
+
121
+ Example:
122
+ vault-bridge-configure -v "/path/to/vault" -c claude_desktop
123
+ """
124
+ vault_path = vault_path.resolve()
125
+
126
+ # Validate vault
127
+ if not (vault_path / ".obsidian").exists() and not any(vault_path.glob("*.md")):
128
+ logger.warning("configure.not_vault", path=str(vault_path))
129
+ click.echo(f"Warning: {vault_path} doesn't appear to be an Obsidian vault (.obsidian folder missing, no .md files)", err=True)
130
+ if not force:
131
+ return 1
132
+
133
+ client_config = CLIENT_CONFIGS[client]
134
+ config_path = client_config["config_path"]
135
+ server_key = client_config["server_key"]
136
+
137
+ logger.info("configure.start", vault_path=str(vault_path), client=client)
138
+
139
+ # Load existing config
140
+ existing: dict[str, Any] = load_existing_config(config_path)
141
+
142
+ # Check for existing entry
143
+ if server_key in existing.get("mcpServers", {}) and not force:
144
+ logger.warning("configure.exists", config_path=str(config_path))
145
+ click.echo(f"vault-bridge already configured in {config_path}. Use --force to overwrite.", err=True)
146
+ return 1
147
+
148
+ # Update config
149
+ updated: dict[str, Any] = update_config_for_client(existing, server_key, str(vault_path))
150
+
151
+ if dry_run:
152
+ click.echo(f"Would update {config_path}:")
153
+ click.echo(json.dumps(updated, indent=2))
154
+ return 0
155
+
156
+ # Save
157
+ save_config(config_path, updated)
158
+
159
+ logger.info("configure.success", config_path=str(config_path))
160
+ click.echo(f"✓ Configured vault-bridge for {client}")
161
+ click.echo(f" Config: {config_path}")
162
+ click.echo(f" Vault: {vault_path}")
163
+ click.echo(f" Restart {client} to apply changes.")
164
+
165
+ return 0
166
+
167
+
168
+ if __name__ == "__main__":
169
+ sys.exit(main())