tgrep-mcp 0.3.0

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shizhy
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.
package/LICENSE-tgrep ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Microsoft Corporation.
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
package/README.md ADDED
@@ -0,0 +1,283 @@
1
+ # tgrep-mcp
2
+
3
+ An MCP server that exposes [tgrep](https://github.com/microsoft/tgrep) over stdio.
4
+
5
+ ## Overview
6
+
7
+ tgrep runs regex queries against a prebuilt trigram index instead of reading every file. This
8
+ server exposes tgrep as MCP tools and keeps a search backend resident, so repeated queries do not
9
+ pay process start cost.
10
+
11
+ The server speaks newline-delimited JSON-RPC 2.0 on stdin and stdout. Any MCP client that can start
12
+ a local process can run it.
13
+
14
+ It provides three tools:
15
+
16
+ - `tgrep_search` searches a file or directory.
17
+ - `tgrep_index` builds or rebuilds the index for a root.
18
+ - `tgrep_status` reports configuration, health, and counters.
19
+
20
+ ## Requirements
21
+
22
+ - Node.js 20 or later.
23
+ - The `tgrep` executable, version 1.0.8. The npm package does not include it. Install it from the
24
+ [releases page](https://github.com/microsoft/tgrep/releases) and either add it to `PATH` or set
25
+ `TGREP_BIN`. The git repository contains a Windows build at `bin/tgrep.exe`; the published package
26
+ does not.
27
+
28
+ ## Install and run
29
+
30
+ Add the server to your MCP client configuration. This is the standard setup:
31
+
32
+ ```json
33
+ {
34
+ "mcpServers": {
35
+ "tgrep": {
36
+ "command": "npx",
37
+ "args": ["-y", "tgrep-mcp"],
38
+ "cwd": "/path/to/your/repository"
39
+ }
40
+ }
41
+ }
42
+ ```
43
+
44
+ Two details matter:
45
+
46
+ - Keep `-y` in `args`. Without it, npx prompts for confirmation, the prompt consumes stdin, and the
47
+ MCP handshake stalls.
48
+ - Set `cwd` to the repository you want to search. See [Search root](#search-root).
49
+
50
+ To pin a version, use `"args": ["-y", "tgrep-mcp@0.3.0"]`.
51
+
52
+ To run from a checkout instead:
53
+
54
+ ```bash
55
+ node /path/to/tgrep-mcp/server.mjs
56
+ ```
57
+
58
+ ## Configure
59
+
60
+ The server reads all configuration from the environment and from the client's declared workspace
61
+ roots. It has no built-in paths.
62
+
63
+ ### Search root
64
+
65
+ A call to `tgrep_search` that omits `path` searches the default root. The server resolves the
66
+ default root in this order:
67
+
68
+ 1. The first workspace root declared by the client, if the client supports the MCP `roots` feature.
69
+ 2. `TGREP_DEFAULT_ROOT`, if set.
70
+ 3. The working directory of the server process.
71
+
72
+ Most clients do not implement `roots`. Setting `cwd` in the client configuration is therefore the
73
+ simplest way to control the default root.
74
+
75
+ ### Allowed roots
76
+
77
+ The server refuses to search outside the allowed roots. It resolves them in this order:
78
+
79
+ 1. `TGREP_ALLOWED_ROOTS`, if set. The value is a list of paths separated by `;` or `,` (POSIX
80
+ systems also accept `:`). The value `*` disables the check.
81
+ 2. The default root, plus any workspace roots declared by the client.
82
+
83
+ Listing a directory allows everything below it. A path that is inside an allowed root is searched
84
+ using that root's index, so a repository is indexed once even when you search a subdirectory.
85
+
86
+ ### tgrep executable
87
+
88
+ The server resolves the executable in this order:
89
+
90
+ 1. `TGREP_BIN`, if set.
91
+ 2. `bin/tgrep` or `bin/tgrep.exe` next to `server.mjs`, if present. This applies to a git checkout
92
+ of this repository, not to an npm install.
93
+ 3. `tgrep` (or `tgrep.exe` on Windows), resolved from `PATH`.
94
+
95
+ Run `tgrep_status` to see which executable and which roots are in effect.
96
+
97
+ ## Tools
98
+
99
+ ### tgrep_search
100
+
101
+ Regex search over a directory or a single file.
102
+
103
+ | Parameter | Type | Required | Description |
104
+ |---|---|---|---|
105
+ | `pattern` | string | yes | Regular expression, in ripgrep syntax. Maximum 2048 characters. |
106
+ | `path` | string | no | File or directory to search. Absolute, or relative to the server's working directory. Defaults to the default root. Must be inside the allowed roots. |
107
+ | `glob` | string | no | Glob filter, for example `*.ts`. Maximum 512 characters. |
108
+ | `ignoreCase` | boolean | no | Case-insensitive matching. |
109
+ | `maxPerFile` | integer | no | Maximum matching lines per file. |
110
+ | `maxResults` | integer | no | Maximum matches returned. Default 200, maximum 5000. |
111
+ | `includeGitIgnored` | boolean | no | Also search files excluded by `.gitignore`. Forces the CLI backend. |
112
+
113
+ The result reports the indexed root that was searched, the resolved target, how many matches tgrep
114
+ found, and how many were returned. Match paths are relative to that root and use forward slashes.
115
+
116
+ `maxResults` bounds the match list in `structuredContent`. A second bound, `TGREP_MAX_OUTPUT_BYTES`,
117
+ caps the total match text per call. When either bound applies, `truncated` is `true` and `warnings`
118
+ explains which one applied.
119
+
120
+ ### tgrep_index
121
+
122
+ Builds or rebuilds the index for a root and stops that root's search daemon. The next search starts a
123
+ new one. The first search on an unindexed root builds the index automatically, so call this tool only
124
+ after a branch switch, a large pull, or when results look stale.
125
+
126
+ | Parameter | Type | Required | Description |
127
+ |---|---|---|---|
128
+ | `path` | string | no | Repository root. Defaults to the default root. |
129
+
130
+ The tool indexes the outermost allowed root that contains the path.
131
+
132
+ ### tgrep_status
133
+
134
+ Reports health, effective configuration, index freshness, and call counters. This tool reads state
135
+ only. It does not build an index and does not start a daemon.
136
+
137
+ | Parameter | Type | Required | Description |
138
+ |---|---|---|---|
139
+ | `path` | string | no | Root to report index and daemon detail for. Defaults to the default root. |
140
+
141
+ The response contains a `status` field with the value `healthy`, `degraded`, or `unhealthy`, and a
142
+ list of checks. Each check reports its own status, a message, and its duration. `unhealthy` takes
143
+ precedence over `degraded`.
144
+
145
+ ## Environment variables
146
+
147
+ | Variable | Default | Description |
148
+ |---|---|---|
149
+ | `TGREP_BIN` | see [tgrep executable](#tgrep-executable) | Path to the tgrep executable. |
150
+ | `TGREP_INDEX_ROOT` | Platform cache directory | Directory holding one index per root. On Windows, `%LOCALAPPDATA%\tgrep-mcp\index`. On macOS, `~/Library/Caches/tgrep-mcp/index`. On Linux, `$XDG_CACHE_HOME/tgrep-mcp/index` or `~/.cache/tgrep-mcp/index`. |
151
+ | `TGREP_DEFAULT_ROOT` | Client workspace root, then working directory | Root searched when a call omits `path`. |
152
+ | `TGREP_ALLOWED_ROOTS` | Default root plus client workspace roots | Roots that may be searched. Separated by `;` or `,`. `*` allows any path. |
153
+ | `TGREP_TIMEOUT_MS` | `60000` | Timeout for one operation. Index builds use four times this value. |
154
+ | `TGREP_TOOL_TIMEOUT_MS` | 4x `TGREP_TIMEOUT_MS` + 30s | Deadline for one tool call. |
155
+ | `TGREP_DIRECT` | `auto` | Set to `off` to use the CLI backend only. |
156
+ | `TGREP_DAEMON_WAIT_MS` | `15000` | Time to wait for a new `tgrep serve` process to become ready. |
157
+ | `TGREP_MAX_CONCURRENCY` | `4` | Tool calls that may run at the same time, across roots. |
158
+ | `TGREP_MAX_CALLS_PER_MINUTE` | `600` | Call budget. `0` disables it. |
159
+ | `TGREP_MAX_RESULTS` | `5000` | Upper bound for `maxResults`. |
160
+ | `TGREP_MAX_PER_FILE` | `10000` | Upper bound for `maxPerFile`. |
161
+ | `TGREP_MAX_LINE_CHARS` | `512` | Maximum length of the text of one match. |
162
+ | `TGREP_MAX_OUTPUT_BYTES` | `262144` | Maximum total match text returned by one call. |
163
+ | `TGREP_INDEX_STALE_MS` | `86400000` | Index age that produces a staleness warning. |
164
+ | `TGREP_LOG_LEVEL` | `normal` | `quiet`, `normal`, or `debug`. Diagnostics go to stderr. |
165
+
166
+ A value that cannot be parsed does not stop the server. The server logs the problem, uses the
167
+ default, and reports it in the `configuration` check of `tgrep_status`.
168
+
169
+ ## Index location and freshness
170
+
171
+ Each root has one index directory under `TGREP_INDEX_ROOT`. The directory name contains a readable
172
+ part derived from the root and a hash of the full path, so two roots can never share an index.
173
+
174
+ Indexes are written outside the searched repository. Searching a repository does not modify it.
175
+
176
+ With the direct backend, a resident `tgrep serve` process watches for changes and keeps the index
177
+ current. Otherwise the index reflects the last build. `tgrep_status` reports the index age, and
178
+ `tgrep_search` adds a warning when the index is older than `TGREP_INDEX_STALE_MS`.
179
+
180
+ To delete all indexes, remove the directory named by `TGREP_INDEX_ROOT`. They are rebuilt on demand.
181
+
182
+ ## Search backends
183
+
184
+ The server tries two backends in order.
185
+
186
+ 1. **direct.** A TCP JSON-RPC connection to a resident `tgrep serve` process. The server starts that
187
+ process if needed, and reuses a healthy one that is already running. Ports are advertised in
188
+ `serve.json` inside the index directory. This backend avoids creating a process per search and
189
+ avoids re-reading the index.
190
+
191
+ 2. **cli.** A `tgrep` child process per search. This backend is slower and is the fallback.
192
+
193
+ The direct protocol is not a documented tgrep interface. The server records the tgrep release it was
194
+ verified against and reports a `degraded` status when the installed release differs. Any connection
195
+ or protocol error falls back to the CLI backend. After three consecutive direct failures the server
196
+ pauses the direct backend for 30 seconds, doubling the pause on each further failure up to five
197
+ minutes.
198
+
199
+ The server stops daemons it started when it exits, and removes their `serve.json`. It never stops a
200
+ daemon it did not start.
201
+
202
+ ### Performance
203
+
204
+ The figures below were measured on the development machine with a repository of moderate size. They
205
+ are indicative only; absolute values depend on the machine and the size of the index.
206
+
207
+ | Query | direct | cli |
208
+ |---|---|---|
209
+ | No matches | ~6 ms | ~200 ms |
210
+ | 4 matches | ~9 ms | ~200 ms |
211
+ | 1613 matches, cold cache | ~198 ms | ~500 ms |
212
+
213
+ The first search on a root adds the cost of starting a daemon and building an index.
214
+
215
+ ## Ignore rules
216
+
217
+ tgrep does not apply ripgrep's `.ignore` re-include rules. If a repository's `.ignore` re-includes a
218
+ tree that `.gitignore` excludes, tgrep skips those files and ripgrep does not. An empty result can
219
+ therefore be wrong for such a repository.
220
+
221
+ When an empty result is the conclusion, confirm it with a tool that follows ripgrep semantics. To
222
+ include gitignored files in a tgrep search, set `includeGitIgnored`. That forces the CLI backend.
223
+
224
+ ## Security and limits
225
+
226
+ - Every argument is validated for type, range, and length. Unknown arguments are rejected.
227
+ - Searches are confined to the allowed roots. Path containment is evaluated per path segment, and
228
+ paths on another volume are rejected.
229
+ - Calls are limited to `TGREP_MAX_CALLS_PER_MINUTE` by default. A rejected call returns
230
+ `RATE_LIMITED` with a retry delay.
231
+ - Match text is stripped of control characters and truncated to `TGREP_MAX_LINE_CHARS`. Result sets
232
+ are bounded by `maxResults` and `TGREP_MAX_OUTPUT_BYTES`.
233
+ - Each call writes one audit record to stderr.
234
+
235
+ Failures return a stable `error.code`. The codes are `VALIDATION_ERROR`, `PATH_NOT_ALLOWED`,
236
+ `NOT_FOUND`, `BINARY_MISSING`, `INDEX_FAILED`, `BACKEND_UNAVAILABLE`, `TIMEOUT`, `CANCELLED`,
237
+ `RATE_LIMITED`, and `INTERNAL_ERROR`. `RATE_LIMITED` and `TIMEOUT` include `retryAfterSeconds`.
238
+ `PATH_NOT_ALLOWED` requires a different path or a configuration change, not a retry.
239
+
240
+ Invalid arguments are reported as JSON-RPC errors with code `-32602`. Failures during execution are
241
+ reported as tool results with `isError: true` and a structured error object.
242
+
243
+ ## Troubleshooting
244
+
245
+ **The tools do not appear in the client.** Run `npx -y tgrep-mcp` in a terminal. The process should
246
+ start and wait for input. If it exits, read the error on stderr.
247
+
248
+ **`BINARY_MISSING`.** The server could not start tgrep. Install tgrep, or set `TGREP_BIN` to its full
249
+ path. Run `tgrep_status` to see the path the server resolved.
250
+
251
+ **`PATH_NOT_ALLOWED`.** The requested path is outside the allowed roots. Add the path to
252
+ `TGREP_ALLOWED_ROOTS`, set that variable to `*`, or set `cwd` to a directory that contains it.
253
+
254
+ **`TIMEOUT`.** The query exceeded `TGREP_TIMEOUT_MS`. Narrow the pattern, add a `glob`, or search a
255
+ subdirectory.
256
+
257
+ **A search returns fewer files than ripgrep.** See [Ignore rules](#ignore-rules).
258
+
259
+ **`tgrep_status` reports `degraded`.** Read the `checks` array. Common causes are a missing index
260
+ (the first search builds one), an unreachable daemon (searches will use the CLI backend), and a
261
+ tgrep release other than the verified one.
262
+
263
+ ## Development
264
+
265
+ ```bash
266
+ npm test # unit tests, no process spawning required
267
+ npm run probe # end-to-end protocol and conformance check
268
+ ```
269
+
270
+ The probe starts a server and exercises every tool, including the failure paths. It requires the
271
+ ability to spawn a process with piped stdio. It validates the responses against the schemas the
272
+ server advertises. Set `TGREP_PROBE_SERVER` to test an installed copy of the package instead of the
273
+ working tree.
274
+
275
+ The probe reports round-trip time per call. Measurements from a single run are not meaningful on a
276
+ loaded machine; compare repeated runs.
277
+
278
+ ## License
279
+
280
+ MIT. See `LICENSE`.
281
+
282
+ The git repository distributes a tgrep binary, which is also MIT licensed. See `LICENSE-tgrep`. The
283
+ npm package does not include that binary.
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "tgrep-mcp",
3
+ "version": "0.3.0",
4
+ "description": "MCP server that exposes tgrep, a trigram-indexed regex search tool, over stdio.",
5
+ "license": "MIT",
6
+ "author": "Shizhy <usstpeter@163.com>",
7
+ "type": "module",
8
+ "bin": {
9
+ "tgrep-mcp": "server.mjs"
10
+ },
11
+ "files": [
12
+ "server.mjs",
13
+ "README.md",
14
+ "LICENSE",
15
+ "LICENSE-tgrep"
16
+ ],
17
+ "engines": {
18
+ "node": ">=20"
19
+ },
20
+ "keywords": [
21
+ "mcp",
22
+ "model-context-protocol",
23
+ "modelcontextprotocol",
24
+ "tgrep",
25
+ "grep",
26
+ "regex",
27
+ "code-search",
28
+ "stdio"
29
+ ],
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "scripts": {
34
+ "test": "node test/server.test.mjs",
35
+ "probe": "node probe.mjs",
36
+ "prepack": "node test/server.test.mjs"
37
+ }
38
+ }