scry-mcp 0.2.0__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.
Files changed (46) hide show
  1. scry_mcp-0.2.0/LICENSE +21 -0
  2. scry_mcp-0.2.0/PKG-INFO +173 -0
  3. scry_mcp-0.2.0/README.md +143 -0
  4. scry_mcp-0.2.0/pyproject.toml +49 -0
  5. scry_mcp-0.2.0/setup.cfg +4 -0
  6. scry_mcp-0.2.0/src/scry/__init__.py +3 -0
  7. scry_mcp-0.2.0/src/scry/__main__.py +10 -0
  8. scry_mcp-0.2.0/src/scry/cli.py +142 -0
  9. scry_mcp-0.2.0/src/scry/config.py +54 -0
  10. scry_mcp-0.2.0/src/scry/domain/__init__.py +0 -0
  11. scry_mcp-0.2.0/src/scry/domain/markers.py +289 -0
  12. scry_mcp-0.2.0/src/scry/migrations/001_initial.sql +146 -0
  13. scry_mcp-0.2.0/src/scry/scripts/__init__.py +0 -0
  14. scry_mcp-0.2.0/src/scry/scripts/validate_coverage.py +54 -0
  15. scry_mcp-0.2.0/src/scry/server.py +24 -0
  16. scry_mcp-0.2.0/src/scry/service/__init__.py +0 -0
  17. scry_mcp-0.2.0/src/scry/service/migration.py +58 -0
  18. scry_mcp-0.2.0/src/scry/service/mint.py +125 -0
  19. scry_mcp-0.2.0/src/scry/service/query.py +44 -0
  20. scry_mcp-0.2.0/src/scry/service/relationship.py +46 -0
  21. scry_mcp-0.2.0/src/scry/service/script.py +98 -0
  22. scry_mcp-0.2.0/src/scry/service/scrub.py +72 -0
  23. scry_mcp-0.2.0/src/scry/service/surface.py +343 -0
  24. scry_mcp-0.2.0/src/scry/service/watcher.py +226 -0
  25. scry_mcp-0.2.0/src/scry/tools/__init__.py +16 -0
  26. scry_mcp-0.2.0/src/scry/tools/_common.py +25 -0
  27. scry_mcp-0.2.0/src/scry/tools/scry_mint.py +25 -0
  28. scry_mcp-0.2.0/src/scry/tools/scry_script.py +36 -0
  29. scry_mcp-0.2.0/src/scry/tools/scry_scrub.py +16 -0
  30. scry_mcp-0.2.0/src/scry/tools/scry_sql.py +23 -0
  31. scry_mcp-0.2.0/src/scry/tools/scry_surface.py +23 -0
  32. scry_mcp-0.2.0/src/scry/util/__init__.py +0 -0
  33. scry_mcp-0.2.0/src/scry/util/comments.py +47 -0
  34. scry_mcp-0.2.0/src/scry_mcp.egg-info/PKG-INFO +173 -0
  35. scry_mcp-0.2.0/src/scry_mcp.egg-info/SOURCES.txt +44 -0
  36. scry_mcp-0.2.0/src/scry_mcp.egg-info/dependency_links.txt +1 -0
  37. scry_mcp-0.2.0/src/scry_mcp.egg-info/entry_points.txt +2 -0
  38. scry_mcp-0.2.0/src/scry_mcp.egg-info/requires.txt +7 -0
  39. scry_mcp-0.2.0/src/scry_mcp.egg-info/top_level.txt +1 -0
  40. scry_mcp-0.2.0/tests/test_markers.py +138 -0
  41. scry_mcp-0.2.0/tests/test_mint.py +45 -0
  42. scry_mcp-0.2.0/tests/test_query.py +46 -0
  43. scry_mcp-0.2.0/tests/test_relationship.py +30 -0
  44. scry_mcp-0.2.0/tests/test_script.py +40 -0
  45. scry_mcp-0.2.0/tests/test_surface.py +126 -0
  46. scry_mcp-0.2.0/tests/test_watcher.py +70 -0
scry_mcp-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Patrick Michaelsen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,173 @@
1
+ Metadata-Version: 2.4
2
+ Name: scry-mcp
3
+ Version: 0.2.0
4
+ Summary: Marker-indexed SQL cache MCP server
5
+ Author-email: Patrick Michaelsen <michaelsenpatrick@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/prmichaelsen/scry
8
+ Project-URL: Repository, https://github.com/prmichaelsen/scry
9
+ Project-URL: Issues, https://github.com/prmichaelsen/scry/issues
10
+ Keywords: mcp,sqlite,markers,indexing,claude,fts5
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Topic :: Software Development
18
+ Classifier: Topic :: Database
19
+ Classifier: Topic :: Text Processing :: Indexing
20
+ Requires-Python: >=3.11
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: mcp>=1.0
24
+ Requires-Dist: watchdog>=4.0
25
+ Requires-Dist: pyyaml>=6.0
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest>=8.0; extra == "dev"
28
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
29
+ Dynamic: license-file
30
+
31
+ # scry
32
+
33
+ Marker-indexed SQL cache MCP server. Scry indexes in-file `@scry.*` markers
34
+ into a SQLite database that agents query via read-only SQL, providing
35
+ structured project knowledge without LLM reasoning.
36
+
37
+ [![PyPI](https://img.shields.io/pypi/v/scry-mcp.svg)](https://pypi.org/project/scry-mcp/)
38
+ [![Python](https://img.shields.io/pypi/pyversions/scry-mcp.svg)](https://pypi.org/project/scry-mcp/)
39
+
40
+ ## Install
41
+
42
+ ```bash
43
+ uv pip install scry-mcp
44
+ # or:
45
+ pip install scry-mcp
46
+ ```
47
+
48
+ The PyPI distribution is `scry-mcp` (the bare name `scry` was already taken
49
+ on PyPI). The import name and the installed console command are both
50
+ `scry`.
51
+
52
+ ## Quickstart
53
+
54
+ ```bash
55
+ # In your project root:
56
+ scry init # scaffolds agent/ + driver dirs, updates .gitignore
57
+ ```
58
+
59
+ Then add it to your MCP client config:
60
+
61
+ ```json
62
+ {
63
+ "mcpServers": {
64
+ "scry": {
65
+ "command": "scry"
66
+ }
67
+ }
68
+ }
69
+ ```
70
+
71
+ The MCP client inherits cwd from wherever it's launched, and scry walks
72
+ up from there looking for an `agent/` directory — so the same config
73
+ works for any project. Bare `scry` (no subcommand) starts the MCP
74
+ server over stdio — that's what Claude calls. Other subcommands:
75
+
76
+ | Command | Purpose |
77
+ |---|---|
78
+ | `scry` | Run the MCP server (default). |
79
+ | `scry init [path]` | Create `agent/` + `agent/drivers/@local/scry/{data,runtime,scripts}/` and a local `.gitignore` inside the driver dir. Idempotent — safe to run inside an ACP project. |
80
+ | `scry surface [--force]` | One-shot batch reindex without booting the server. |
81
+ | `scry version` | Print the package version. |
82
+
83
+ The server walks up from `cwd` until it finds an `agent/` directory; that
84
+ becomes the project root. The cache lives at
85
+ `agent/drivers/@<namespace>/scry/data/project.db` and is gitignored.
86
+
87
+ ## Markers
88
+
89
+ Scry recognizes five marker kinds. Block markers carry a YAML body between
90
+ open/close tokens; line markers are single-line `@scry.<kind> <id> <ref>`.
91
+
92
+ ```html
93
+ <!-- @scry.doc
94
+ id: design.auth-flow~a1b2c3d4
95
+ kind: design
96
+ summary: >
97
+ JWT auth middleware, token validation, refresh flow
98
+ status: active
99
+ weight: 0.85
100
+ tags: ["scope:auth", "topic:security"]
101
+ rationale: >
102
+ Missing this causes auth bypass bugs
103
+ applies: modifying auth, adding protected endpoints
104
+ seeded_questions:
105
+ - How does token refresh work?
106
+ @scry.doc.end -->
107
+
108
+ <!-- @scry.file
109
+ id: file.auth-middleware~e5f6a7b8
110
+ kind: middleware
111
+ summary: Express middleware that validates JWT on every request
112
+ status: active
113
+ weight: 0.7
114
+ @scry.file.end -->
115
+
116
+ <!-- @scry.anchor auth-check~f1e2d3c4
117
+ description: JWT validation point for protected routes
118
+ @scry.anchor.end -->
119
+ ```
120
+
121
+ ```python
122
+ # @scry.impl validate-jwt~a1b2c3d4 spec.auth~xyz89012#FR3
123
+ # @scry.test jwt-expiry~b2c3d4e5 spec.auth~xyz89012#UT1
124
+ ```
125
+
126
+ Block markers can be embedded in any host-language comment style (HTML,
127
+ Python, JS, JSDoc, Rust, bare YAML). Comment prefixes are inferred from
128
+ the YAML body — there is no per-language config.
129
+
130
+ ## MCP tools
131
+
132
+ | Tool | Purpose |
133
+ |---|---|
134
+ | `scry_sql(query)` | Read-only SQL gateway. Rejects mutator keywords. Returns `{results, row_count}` JSON. |
135
+ | `scry_mint(kind, prefix)` | Generate a collision-free ID and the marker schema. |
136
+ | `scry_surface(force=false)` | Batch reindex from disk. `force=true` hard-deletes records whose source file no longer exists. |
137
+ | `scry_scrub()` | Create a `<branch>--clean` git branch with all `@scry.*` markers stripped and `agent/` removed. |
138
+ | `scry_script(action, script?, params?)` | Discover and run validation scripts from `src/scry/scripts/` and `agent/drivers/@<ns>/scry/scripts/`. |
139
+
140
+ ## Database schema
141
+
142
+ Five marker-backed tables (`scry__doc`, `scry__file`, `scry__anchor`,
143
+ `scry__impl`, `scry__test`) plus `doc_relationship` (with cycle detection)
144
+ and `migration`. FTS5 is maintained via triggers on the source tables —
145
+ no manual rebuild step.
146
+
147
+ The cache is fully reconstructable from disk via `scry_surface`. The DB is
148
+ gitignored; after `git pull`, agents call `scry_surface` to rebuild.
149
+
150
+ ## Watcher
151
+
152
+ A daemon thread runs alongside the MCP server, watching the project tree
153
+ with a 150 ms debounce window. A lock file at
154
+ `agent/drivers/@<ns>/scry/runtime/lock` performs PID-based primary
155
+ election so multiple sessions don't race writes. The primary instance
156
+ runs a cold scan on startup; secondaries observe and wait.
157
+
158
+ On file deletion: docs and files are soft-deleted (`missing_since` set);
159
+ anchors, impls, and tests are hard-deleted.
160
+
161
+ ## Tests
162
+
163
+ ```bash
164
+ uv pip install -e ".[dev]"
165
+ pytest
166
+ ```
167
+
168
+ 43 unit tests cover the parser, SQL gateway, mint, surface, watcher
169
+ plumbing, script discovery, and relationship cycle detection.
170
+
171
+ ## License
172
+
173
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,143 @@
1
+ # scry
2
+
3
+ Marker-indexed SQL cache MCP server. Scry indexes in-file `@scry.*` markers
4
+ into a SQLite database that agents query via read-only SQL, providing
5
+ structured project knowledge without LLM reasoning.
6
+
7
+ [![PyPI](https://img.shields.io/pypi/v/scry-mcp.svg)](https://pypi.org/project/scry-mcp/)
8
+ [![Python](https://img.shields.io/pypi/pyversions/scry-mcp.svg)](https://pypi.org/project/scry-mcp/)
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ uv pip install scry-mcp
14
+ # or:
15
+ pip install scry-mcp
16
+ ```
17
+
18
+ The PyPI distribution is `scry-mcp` (the bare name `scry` was already taken
19
+ on PyPI). The import name and the installed console command are both
20
+ `scry`.
21
+
22
+ ## Quickstart
23
+
24
+ ```bash
25
+ # In your project root:
26
+ scry init # scaffolds agent/ + driver dirs, updates .gitignore
27
+ ```
28
+
29
+ Then add it to your MCP client config:
30
+
31
+ ```json
32
+ {
33
+ "mcpServers": {
34
+ "scry": {
35
+ "command": "scry"
36
+ }
37
+ }
38
+ }
39
+ ```
40
+
41
+ The MCP client inherits cwd from wherever it's launched, and scry walks
42
+ up from there looking for an `agent/` directory — so the same config
43
+ works for any project. Bare `scry` (no subcommand) starts the MCP
44
+ server over stdio — that's what Claude calls. Other subcommands:
45
+
46
+ | Command | Purpose |
47
+ |---|---|
48
+ | `scry` | Run the MCP server (default). |
49
+ | `scry init [path]` | Create `agent/` + `agent/drivers/@local/scry/{data,runtime,scripts}/` and a local `.gitignore` inside the driver dir. Idempotent — safe to run inside an ACP project. |
50
+ | `scry surface [--force]` | One-shot batch reindex without booting the server. |
51
+ | `scry version` | Print the package version. |
52
+
53
+ The server walks up from `cwd` until it finds an `agent/` directory; that
54
+ becomes the project root. The cache lives at
55
+ `agent/drivers/@<namespace>/scry/data/project.db` and is gitignored.
56
+
57
+ ## Markers
58
+
59
+ Scry recognizes five marker kinds. Block markers carry a YAML body between
60
+ open/close tokens; line markers are single-line `@scry.<kind> <id> <ref>`.
61
+
62
+ ```html
63
+ <!-- @scry.doc
64
+ id: design.auth-flow~a1b2c3d4
65
+ kind: design
66
+ summary: >
67
+ JWT auth middleware, token validation, refresh flow
68
+ status: active
69
+ weight: 0.85
70
+ tags: ["scope:auth", "topic:security"]
71
+ rationale: >
72
+ Missing this causes auth bypass bugs
73
+ applies: modifying auth, adding protected endpoints
74
+ seeded_questions:
75
+ - How does token refresh work?
76
+ @scry.doc.end -->
77
+
78
+ <!-- @scry.file
79
+ id: file.auth-middleware~e5f6a7b8
80
+ kind: middleware
81
+ summary: Express middleware that validates JWT on every request
82
+ status: active
83
+ weight: 0.7
84
+ @scry.file.end -->
85
+
86
+ <!-- @scry.anchor auth-check~f1e2d3c4
87
+ description: JWT validation point for protected routes
88
+ @scry.anchor.end -->
89
+ ```
90
+
91
+ ```python
92
+ # @scry.impl validate-jwt~a1b2c3d4 spec.auth~xyz89012#FR3
93
+ # @scry.test jwt-expiry~b2c3d4e5 spec.auth~xyz89012#UT1
94
+ ```
95
+
96
+ Block markers can be embedded in any host-language comment style (HTML,
97
+ Python, JS, JSDoc, Rust, bare YAML). Comment prefixes are inferred from
98
+ the YAML body — there is no per-language config.
99
+
100
+ ## MCP tools
101
+
102
+ | Tool | Purpose |
103
+ |---|---|
104
+ | `scry_sql(query)` | Read-only SQL gateway. Rejects mutator keywords. Returns `{results, row_count}` JSON. |
105
+ | `scry_mint(kind, prefix)` | Generate a collision-free ID and the marker schema. |
106
+ | `scry_surface(force=false)` | Batch reindex from disk. `force=true` hard-deletes records whose source file no longer exists. |
107
+ | `scry_scrub()` | Create a `<branch>--clean` git branch with all `@scry.*` markers stripped and `agent/` removed. |
108
+ | `scry_script(action, script?, params?)` | Discover and run validation scripts from `src/scry/scripts/` and `agent/drivers/@<ns>/scry/scripts/`. |
109
+
110
+ ## Database schema
111
+
112
+ Five marker-backed tables (`scry__doc`, `scry__file`, `scry__anchor`,
113
+ `scry__impl`, `scry__test`) plus `doc_relationship` (with cycle detection)
114
+ and `migration`. FTS5 is maintained via triggers on the source tables —
115
+ no manual rebuild step.
116
+
117
+ The cache is fully reconstructable from disk via `scry_surface`. The DB is
118
+ gitignored; after `git pull`, agents call `scry_surface` to rebuild.
119
+
120
+ ## Watcher
121
+
122
+ A daemon thread runs alongside the MCP server, watching the project tree
123
+ with a 150 ms debounce window. A lock file at
124
+ `agent/drivers/@<ns>/scry/runtime/lock` performs PID-based primary
125
+ election so multiple sessions don't race writes. The primary instance
126
+ runs a cold scan on startup; secondaries observe and wait.
127
+
128
+ On file deletion: docs and files are soft-deleted (`missing_since` set);
129
+ anchors, impls, and tests are hard-deleted.
130
+
131
+ ## Tests
132
+
133
+ ```bash
134
+ uv pip install -e ".[dev]"
135
+ pytest
136
+ ```
137
+
138
+ 43 unit tests cover the parser, SQL gateway, mint, surface, watcher
139
+ plumbing, script discovery, and relationship cycle detection.
140
+
141
+ ## License
142
+
143
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "scry-mcp"
7
+ version = "0.2.0"
8
+ description = "Marker-indexed SQL cache MCP server"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Patrick Michaelsen", email = "michaelsenpatrick@gmail.com" }]
13
+ keywords = ["mcp", "sqlite", "markers", "indexing", "claude", "fts5"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.11",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Operating System :: OS Independent",
21
+ "Topic :: Software Development",
22
+ "Topic :: Database",
23
+ "Topic :: Text Processing :: Indexing",
24
+ ]
25
+ dependencies = [
26
+ "mcp>=1.0",
27
+ "watchdog>=4.0",
28
+ "pyyaml>=6.0",
29
+ ]
30
+
31
+ [project.optional-dependencies]
32
+ dev = [
33
+ "pytest>=8.0",
34
+ "pytest-asyncio>=0.23",
35
+ ]
36
+
37
+ [project.urls]
38
+ Homepage = "https://github.com/prmichaelsen/scry"
39
+ Repository = "https://github.com/prmichaelsen/scry"
40
+ Issues = "https://github.com/prmichaelsen/scry/issues"
41
+
42
+ [project.scripts]
43
+ scry = "scry.cli:main"
44
+
45
+ [tool.setuptools.packages.find]
46
+ where = ["src"]
47
+
48
+ [tool.setuptools.package-data]
49
+ scry = ["migrations/*.sql"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """scry — marker-indexed SQL cache MCP server."""
2
+
3
+ __version__ = "0.2.0"
@@ -0,0 +1,10 @@
1
+ """`python -m scry` entry — delegates to the CLI dispatcher."""
2
+ from __future__ import annotations
3
+
4
+ import sys
5
+
6
+ from scry.cli import main
7
+
8
+
9
+ if __name__ == "__main__":
10
+ sys.exit(main())
@@ -0,0 +1,142 @@
1
+ """scry CLI dispatcher (installed by the `scry-mcp` distribution).
2
+
3
+ scry run the MCP server (default; what Claude calls)
4
+ scry init scaffold an `agent/` tree + driver dirs in CWD
5
+ scry surface one-shot batch reindex (no server, no watcher)
6
+ scry version print the package version
7
+ scry --help
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import json
13
+ import sys
14
+ from pathlib import Path
15
+
16
+ from scry import __version__
17
+
18
+
19
+ def _cmd_serve() -> int:
20
+ from scry.server import run_server
21
+ run_server()
22
+ return 0
23
+
24
+
25
+ _LOCAL_GITIGNORE = """\
26
+ # scry runtime + cache — fully reconstructable from disk, do not commit.
27
+ data/
28
+ runtime/
29
+ """
30
+
31
+
32
+ def _cmd_init(args: argparse.Namespace) -> int:
33
+ target = Path(args.path).resolve()
34
+ namespace = args.namespace
35
+ target.mkdir(parents=True, exist_ok=True)
36
+
37
+ created: list[str] = []
38
+ preserved: list[str] = []
39
+
40
+ def ensure_dir(p: Path, label: str | None = None) -> None:
41
+ rel = label or (str(p.relative_to(target)) + "/")
42
+ if p.exists():
43
+ preserved.append(rel)
44
+ else:
45
+ p.mkdir(parents=True)
46
+ created.append(rel)
47
+
48
+ agent = target / "agent"
49
+ ensure_dir(agent, "agent/")
50
+ driver = agent / "drivers" / f"@{namespace}" / "scry"
51
+ ensure_dir(driver / "data")
52
+ ensure_dir(driver / "runtime")
53
+ ensure_dir(driver / "scripts")
54
+
55
+ # Local .gitignore inside the driver dir — keeps scry's gitignore
56
+ # concerns self-contained instead of polluting the project root.
57
+ local_gi = driver / ".gitignore"
58
+ if local_gi.exists():
59
+ preserved.append(str(local_gi.relative_to(target)))
60
+ else:
61
+ local_gi.write_text(_LOCAL_GITIGNORE, encoding="utf-8")
62
+ created.append(str(local_gi.relative_to(target)))
63
+
64
+ print(f"Initialized scry in {target}")
65
+ print(f" namespace: @{namespace}")
66
+ if (agent / "commands").is_dir() or (agent / "progress.yaml").is_file():
67
+ print(" detected: ACP project (agent/ tree preserved)")
68
+ if created:
69
+ print(" created:")
70
+ for c in created:
71
+ print(f" + {c}")
72
+ if preserved:
73
+ print(" already present:")
74
+ for p in preserved:
75
+ print(f" · {p}")
76
+ print()
77
+ print("Optional: add `*.scratch.md` to your project .gitignore to honor")
78
+ print("the ephemeral-doc convention (filename → ephemeral=1 in cache).")
79
+ print()
80
+ print("Add this to your MCP client config:")
81
+ print(json.dumps({
82
+ "mcpServers": {
83
+ "scry": {"command": "scry"}
84
+ }
85
+ }, indent=2))
86
+ return 0
87
+
88
+
89
+ def _cmd_surface(args: argparse.Namespace) -> int:
90
+ from scry.config import get_db, get_project_root
91
+ from scry.service.migration import run_migrations
92
+ from scry.service.surface import surface
93
+
94
+ root = get_project_root()
95
+ conn = get_db()
96
+ try:
97
+ run_migrations(conn=conn)
98
+ result = surface(conn, project_root=root, force=args.force)
99
+ finally:
100
+ conn.close()
101
+ print(json.dumps(result, indent=2, default=str))
102
+ return 0
103
+
104
+
105
+ def _cmd_version() -> int:
106
+ print(__version__)
107
+ return 0
108
+
109
+
110
+ def main(argv: list[str] | None = None) -> int:
111
+ parser = argparse.ArgumentParser(
112
+ prog="scry",
113
+ description="Marker-indexed SQL cache MCP server (scry-mcp package).",
114
+ )
115
+ parser.add_argument("--version", action="version", version=f"scry {__version__}")
116
+ sub = parser.add_subparsers(dest="command")
117
+
118
+ p_init = sub.add_parser("init", help="Scaffold an agent/ tree in the current project.")
119
+ p_init.add_argument("path", nargs="?", default=".", help="Project directory (default: cwd).")
120
+ p_init.add_argument("--namespace", default="local", help="Driver namespace (default: local).")
121
+
122
+ p_surface = sub.add_parser("surface", help="One-shot batch reindex of the project tree.")
123
+ p_surface.add_argument("--force", action="store_true", help="Hard-delete records whose source file is gone.")
124
+
125
+ sub.add_parser("serve", help="Run the MCP server (default if no subcommand given).")
126
+ sub.add_parser("version", help="Print the package version.")
127
+
128
+ args = parser.parse_args(argv)
129
+ if args.command is None or args.command == "serve":
130
+ return _cmd_serve()
131
+ if args.command == "init":
132
+ return _cmd_init(args)
133
+ if args.command == "surface":
134
+ return _cmd_surface(args)
135
+ if args.command == "version":
136
+ return _cmd_version()
137
+ parser.print_help()
138
+ return 2
139
+
140
+
141
+ if __name__ == "__main__":
142
+ sys.exit(main())
@@ -0,0 +1,54 @@
1
+ """Configuration: project root detection, DB path, connection factory."""
2
+ from __future__ import annotations
3
+
4
+ import sqlite3
5
+ from pathlib import Path
6
+
7
+ EXCLUDED_DIRS = frozenset({".git", "node_modules", "__pycache__", ".venv", "dist"})
8
+ DEBOUNCE_MS = 150
9
+ BINARY_SNIFF_BYTES = 512
10
+
11
+
12
+ def get_project_root(start: Path | None = None) -> Path:
13
+ p = (start or Path.cwd()).resolve()
14
+ while p != p.parent:
15
+ if (p / "agent").is_dir():
16
+ return p
17
+ p = p.parent
18
+ return (start or Path.cwd()).resolve()
19
+
20
+
21
+ def get_namespace(project_root: Path | None = None) -> str:
22
+ root = project_root or get_project_root()
23
+ drivers = root / "agent" / "drivers"
24
+ if drivers.is_dir():
25
+ for entry in sorted(drivers.iterdir()):
26
+ if entry.name.startswith("@") and (entry / "scry").is_dir():
27
+ return entry.name[1:]
28
+ return "local"
29
+
30
+
31
+ def get_driver_dir(project_root: Path | None = None) -> Path:
32
+ root = project_root or get_project_root()
33
+ return root / "agent" / "drivers" / f"@{get_namespace(root)}" / "scry"
34
+
35
+
36
+ def get_db_path(project_root: Path | None = None) -> Path:
37
+ return get_driver_dir(project_root) / "data" / "project.db"
38
+
39
+
40
+ def get_lock_path(project_root: Path | None = None) -> Path:
41
+ return get_driver_dir(project_root) / "runtime" / "lock"
42
+
43
+
44
+ def get_driver_scripts_dir(project_root: Path | None = None) -> Path:
45
+ return get_driver_dir(project_root) / "scripts"
46
+
47
+
48
+ def get_db(db_path: Path | None = None) -> sqlite3.Connection:
49
+ path = db_path or get_db_path()
50
+ path.parent.mkdir(parents=True, exist_ok=True)
51
+ conn = sqlite3.connect(str(path), timeout=10)
52
+ conn.execute("PRAGMA journal_mode = WAL")
53
+ conn.row_factory = sqlite3.Row
54
+ return conn
File without changes