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.
- scry_mcp-0.2.0/LICENSE +21 -0
- scry_mcp-0.2.0/PKG-INFO +173 -0
- scry_mcp-0.2.0/README.md +143 -0
- scry_mcp-0.2.0/pyproject.toml +49 -0
- scry_mcp-0.2.0/setup.cfg +4 -0
- scry_mcp-0.2.0/src/scry/__init__.py +3 -0
- scry_mcp-0.2.0/src/scry/__main__.py +10 -0
- scry_mcp-0.2.0/src/scry/cli.py +142 -0
- scry_mcp-0.2.0/src/scry/config.py +54 -0
- scry_mcp-0.2.0/src/scry/domain/__init__.py +0 -0
- scry_mcp-0.2.0/src/scry/domain/markers.py +289 -0
- scry_mcp-0.2.0/src/scry/migrations/001_initial.sql +146 -0
- scry_mcp-0.2.0/src/scry/scripts/__init__.py +0 -0
- scry_mcp-0.2.0/src/scry/scripts/validate_coverage.py +54 -0
- scry_mcp-0.2.0/src/scry/server.py +24 -0
- scry_mcp-0.2.0/src/scry/service/__init__.py +0 -0
- scry_mcp-0.2.0/src/scry/service/migration.py +58 -0
- scry_mcp-0.2.0/src/scry/service/mint.py +125 -0
- scry_mcp-0.2.0/src/scry/service/query.py +44 -0
- scry_mcp-0.2.0/src/scry/service/relationship.py +46 -0
- scry_mcp-0.2.0/src/scry/service/script.py +98 -0
- scry_mcp-0.2.0/src/scry/service/scrub.py +72 -0
- scry_mcp-0.2.0/src/scry/service/surface.py +343 -0
- scry_mcp-0.2.0/src/scry/service/watcher.py +226 -0
- scry_mcp-0.2.0/src/scry/tools/__init__.py +16 -0
- scry_mcp-0.2.0/src/scry/tools/_common.py +25 -0
- scry_mcp-0.2.0/src/scry/tools/scry_mint.py +25 -0
- scry_mcp-0.2.0/src/scry/tools/scry_script.py +36 -0
- scry_mcp-0.2.0/src/scry/tools/scry_scrub.py +16 -0
- scry_mcp-0.2.0/src/scry/tools/scry_sql.py +23 -0
- scry_mcp-0.2.0/src/scry/tools/scry_surface.py +23 -0
- scry_mcp-0.2.0/src/scry/util/__init__.py +0 -0
- scry_mcp-0.2.0/src/scry/util/comments.py +47 -0
- scry_mcp-0.2.0/src/scry_mcp.egg-info/PKG-INFO +173 -0
- scry_mcp-0.2.0/src/scry_mcp.egg-info/SOURCES.txt +44 -0
- scry_mcp-0.2.0/src/scry_mcp.egg-info/dependency_links.txt +1 -0
- scry_mcp-0.2.0/src/scry_mcp.egg-info/entry_points.txt +2 -0
- scry_mcp-0.2.0/src/scry_mcp.egg-info/requires.txt +7 -0
- scry_mcp-0.2.0/src/scry_mcp.egg-info/top_level.txt +1 -0
- scry_mcp-0.2.0/tests/test_markers.py +138 -0
- scry_mcp-0.2.0/tests/test_mint.py +45 -0
- scry_mcp-0.2.0/tests/test_query.py +46 -0
- scry_mcp-0.2.0/tests/test_relationship.py +30 -0
- scry_mcp-0.2.0/tests/test_script.py +40 -0
- scry_mcp-0.2.0/tests/test_surface.py +126 -0
- 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.
|
scry_mcp-0.2.0/PKG-INFO
ADDED
|
@@ -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
|
+
[](https://pypi.org/project/scry-mcp/)
|
|
38
|
+
[](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).
|
scry_mcp-0.2.0/README.md
ADDED
|
@@ -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
|
+
[](https://pypi.org/project/scry-mcp/)
|
|
8
|
+
[](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"]
|
scry_mcp-0.2.0/setup.cfg
ADDED
|
@@ -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
|