@paretools/search 0.7.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 Dave London
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,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { registerAllTools } from "./tools/index.js";
5
+ const server = new McpServer({ name: "@paretools/search", version: "0.7.0" }, {
6
+ instructions: "Structured code search operations (ripgrep + fd). Returns typed JSON with match locations, file lists, and match counts. Use instead of running rg/fd/grep/find in the terminal.",
7
+ });
8
+ registerAllTools(server);
9
+ const transport = new StdioServerTransport();
10
+ await server.connect(transport);
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,OAAO,EAAE,EAC/C;IACE,YAAY,EACV,kLAAkL;CACrL,CACF,CAAC;AAEF,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAEzB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { SearchResult, FindResult, CountResult } from "../schemas/index.js";
2
+ /** Formats structured search results into a human-readable match listing. */
3
+ export declare function formatSearch(data: SearchResult): string;
4
+ /** Formats structured find results into a human-readable file listing. */
5
+ export declare function formatFind(data: FindResult): string;
6
+ /** Formats structured count results into a human-readable per-file count listing. */
7
+ export declare function formatCount(data: CountResult): string;
8
+ /** Compact search: totals only, drop individual matches. */
9
+ export interface SearchCompact {
10
+ [key: string]: unknown;
11
+ totalMatches: number;
12
+ filesSearched: number;
13
+ }
14
+ export declare function compactSearchMap(data: SearchResult): SearchCompact;
15
+ export declare function formatSearchCompact(data: SearchCompact): string;
16
+ /** Compact find: total count only, drop individual file entries. */
17
+ export interface FindCompact {
18
+ [key: string]: unknown;
19
+ total: number;
20
+ }
21
+ export declare function compactFindMap(data: FindResult): FindCompact;
22
+ export declare function formatFindCompact(data: FindCompact): string;
23
+ /** Compact count: totals only, drop per-file breakdown. */
24
+ export interface CountCompact {
25
+ [key: string]: unknown;
26
+ totalMatches: number;
27
+ totalFiles: number;
28
+ }
29
+ export declare function compactCountMap(data: CountResult): CountCompact;
30
+ export declare function formatCountCompact(data: CountCompact): string;
31
+ //# sourceMappingURL=formatters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatters.d.ts","sourceRoot":"","sources":["../../src/lib/formatters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAIjF,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAQvD;AAED,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAQnD;AAED,qFAAqF;AACrF,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAQrD;AAID,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,aAAa,CAKlE;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAG/D;AAED,oEAAoE;AACpE,MAAM,WAAW,WAAW;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,CAI5D;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAG3D;AAED,2DAA2D;AAC3D,MAAM,WAAW,YAAY;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,GAAG,YAAY,CAK/D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAG7D"}
@@ -0,0 +1,64 @@
1
+ // ── Full formatters ─────────────────────────────────────────────────
2
+ /** Formats structured search results into a human-readable match listing. */
3
+ export function formatSearch(data) {
4
+ if (data.totalMatches === 0)
5
+ return "search: no matches found.";
6
+ const lines = [`search: ${data.totalMatches} matches in ${data.filesSearched} files`];
7
+ for (const m of data.matches) {
8
+ lines.push(` ${m.file}:${m.line}:${m.column}: ${m.lineContent}`);
9
+ }
10
+ return lines.join("\n");
11
+ }
12
+ /** Formats structured find results into a human-readable file listing. */
13
+ export function formatFind(data) {
14
+ if (data.total === 0)
15
+ return "find: no files found.";
16
+ const lines = [`find: ${data.total} files`];
17
+ for (const f of data.files) {
18
+ lines.push(` ${f.path}`);
19
+ }
20
+ return lines.join("\n");
21
+ }
22
+ /** Formats structured count results into a human-readable per-file count listing. */
23
+ export function formatCount(data) {
24
+ if (data.totalFiles === 0)
25
+ return "count: no matches found.";
26
+ const lines = [`count: ${data.totalMatches} matches in ${data.totalFiles} files`];
27
+ for (const f of data.files) {
28
+ lines.push(` ${f.file}: ${f.count}`);
29
+ }
30
+ return lines.join("\n");
31
+ }
32
+ export function compactSearchMap(data) {
33
+ return {
34
+ totalMatches: data.totalMatches,
35
+ filesSearched: data.filesSearched,
36
+ };
37
+ }
38
+ export function formatSearchCompact(data) {
39
+ if (data.totalMatches === 0)
40
+ return "search: no matches found.";
41
+ return `search: ${data.totalMatches} matches in ${data.filesSearched} files`;
42
+ }
43
+ export function compactFindMap(data) {
44
+ return {
45
+ total: data.total,
46
+ };
47
+ }
48
+ export function formatFindCompact(data) {
49
+ if (data.total === 0)
50
+ return "find: no files found.";
51
+ return `find: ${data.total} files`;
52
+ }
53
+ export function compactCountMap(data) {
54
+ return {
55
+ totalMatches: data.totalMatches,
56
+ totalFiles: data.totalFiles,
57
+ };
58
+ }
59
+ export function formatCountCompact(data) {
60
+ if (data.totalFiles === 0)
61
+ return "count: no matches found.";
62
+ return `count: ${data.totalMatches} matches in ${data.totalFiles} files`;
63
+ }
64
+ //# sourceMappingURL=formatters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatters.js","sourceRoot":"","sources":["../../src/lib/formatters.ts"],"names":[],"mappings":"AAEA,uEAAuE;AAEvE,6EAA6E;AAC7E,MAAM,UAAU,YAAY,CAAC,IAAkB;IAC7C,IAAI,IAAI,CAAC,YAAY,KAAK,CAAC;QAAE,OAAO,2BAA2B,CAAC;IAEhE,MAAM,KAAK,GAAG,CAAC,WAAW,IAAI,CAAC,YAAY,eAAe,IAAI,CAAC,aAAa,QAAQ,CAAC,CAAC;IACtF,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,UAAU,CAAC,IAAgB;IACzC,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC;QAAE,OAAO,uBAAuB,CAAC;IAErD,MAAM,KAAK,GAAG,CAAC,SAAS,IAAI,CAAC,KAAK,QAAQ,CAAC,CAAC;IAC5C,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,WAAW,CAAC,IAAiB;IAC3C,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC;QAAE,OAAO,0BAA0B,CAAC;IAE7D,MAAM,KAAK,GAAG,CAAC,UAAU,IAAI,CAAC,YAAY,eAAe,IAAI,CAAC,UAAU,QAAQ,CAAC,CAAC;IAClF,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAWD,MAAM,UAAU,gBAAgB,CAAC,IAAkB;IACjD,OAAO;QACL,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,aAAa,EAAE,IAAI,CAAC,aAAa;KAClC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAAmB;IACrD,IAAI,IAAI,CAAC,YAAY,KAAK,CAAC;QAAE,OAAO,2BAA2B,CAAC;IAChE,OAAO,WAAW,IAAI,CAAC,YAAY,eAAe,IAAI,CAAC,aAAa,QAAQ,CAAC;AAC/E,CAAC;AAQD,MAAM,UAAU,cAAc,CAAC,IAAgB;IAC7C,OAAO;QACL,KAAK,EAAE,IAAI,CAAC,KAAK;KAClB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAiB;IACjD,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC;QAAE,OAAO,uBAAuB,CAAC;IACrD,OAAO,SAAS,IAAI,CAAC,KAAK,QAAQ,CAAC;AACrC,CAAC;AASD,MAAM,UAAU,eAAe,CAAC,IAAiB;IAC/C,OAAO;QACL,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,UAAU,EAAE,IAAI,CAAC,UAAU;KAC5B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAkB;IACnD,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC;QAAE,OAAO,0BAA0B,CAAC;IAC7D,OAAO,UAAU,IAAI,CAAC,YAAY,eAAe,IAAI,CAAC,UAAU,QAAQ,CAAC;AAC3E,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { SearchResult, FindResult, CountResult } from "../schemas/index.js";
2
+ /**
3
+ * Parses `rg --json` JSONL output into a structured SearchResult.
4
+ *
5
+ * Each line of stdout is a JSON object. We filter for type="match" events
6
+ * and extract the file, line number, column (from submatches[0].start),
7
+ * match text, and full line content (trimmed).
8
+ */
9
+ export declare function parseRgJsonOutput(stdout: string, maxResults: number): SearchResult;
10
+ /**
11
+ * Parses `fd` output (one file path per line) into a structured FindResult.
12
+ * Extracts basename and extension for each entry.
13
+ */
14
+ export declare function parseFdOutput(stdout: string, maxResults: number): FindResult;
15
+ /**
16
+ * Parses `rg --count` output into a structured CountResult.
17
+ * Each line has the format `file:count`. We split on the LAST colon
18
+ * to handle file paths that may contain colons (e.g., Windows drive letters).
19
+ */
20
+ export declare function parseRgCountOutput(stdout: string): CountResult;
21
+ //# sourceMappingURL=parsers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parsers.d.ts","sourceRoot":"","sources":["../../src/lib/parsers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAoCjF;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,YAAY,CA6ClF;AAID;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,UAAU,CAwB5E;AAID;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAwB9D"}
@@ -0,0 +1,107 @@
1
+ import * as path from "node:path";
2
+ /**
3
+ * Parses `rg --json` JSONL output into a structured SearchResult.
4
+ *
5
+ * Each line of stdout is a JSON object. We filter for type="match" events
6
+ * and extract the file, line number, column (from submatches[0].start),
7
+ * match text, and full line content (trimmed).
8
+ */
9
+ export function parseRgJsonOutput(stdout, maxResults) {
10
+ const lines = stdout.split("\n").filter(Boolean);
11
+ const matches = [];
12
+ let filesSearched = 0;
13
+ const seenFiles = new Set();
14
+ for (const line of lines) {
15
+ let parsed;
16
+ try {
17
+ parsed = JSON.parse(line);
18
+ }
19
+ catch {
20
+ continue;
21
+ }
22
+ if (parsed.type === "match") {
23
+ const data = parsed.data;
24
+ const file = data.path.text;
25
+ seenFiles.add(file);
26
+ if (matches.length < maxResults) {
27
+ const submatch = data.submatches[0];
28
+ matches.push({
29
+ file,
30
+ line: data.line_number,
31
+ column: submatch ? submatch.start + 1 : 1, // 1-based column
32
+ matchText: submatch ? submatch.match.text : "",
33
+ lineContent: data.lines.text.trimEnd(),
34
+ });
35
+ }
36
+ }
37
+ else if (parsed.type === "summary") {
38
+ const stats = parsed.data.stats;
39
+ filesSearched = stats.searches_with_match;
40
+ }
41
+ }
42
+ // If no summary line was emitted, use the count of unique files with matches
43
+ if (filesSearched === 0) {
44
+ filesSearched = seenFiles.size;
45
+ }
46
+ return {
47
+ matches,
48
+ totalMatches: matches.length,
49
+ filesSearched,
50
+ };
51
+ }
52
+ // ── fd parser ───────────────────────────────────────────────────────
53
+ /**
54
+ * Parses `fd` output (one file path per line) into a structured FindResult.
55
+ * Extracts basename and extension for each entry.
56
+ */
57
+ export function parseFdOutput(stdout, maxResults) {
58
+ const lines = stdout.split("\n").filter(Boolean);
59
+ const files = [];
60
+ for (const line of lines) {
61
+ if (files.length >= maxResults)
62
+ break;
63
+ const filePath = line.trim();
64
+ if (!filePath)
65
+ continue;
66
+ const name = path.basename(filePath);
67
+ const ext = path.extname(filePath);
68
+ files.push({
69
+ path: filePath,
70
+ name,
71
+ ext,
72
+ });
73
+ }
74
+ return {
75
+ files,
76
+ total: files.length,
77
+ };
78
+ }
79
+ // ── rg --count parser ───────────────────────────────────────────────
80
+ /**
81
+ * Parses `rg --count` output into a structured CountResult.
82
+ * Each line has the format `file:count`. We split on the LAST colon
83
+ * to handle file paths that may contain colons (e.g., Windows drive letters).
84
+ */
85
+ export function parseRgCountOutput(stdout) {
86
+ const lines = stdout.split("\n").filter(Boolean);
87
+ const files = [];
88
+ let totalMatches = 0;
89
+ for (const line of lines) {
90
+ const lastColon = line.lastIndexOf(":");
91
+ if (lastColon === -1)
92
+ continue;
93
+ const file = line.slice(0, lastColon);
94
+ const countStr = line.slice(lastColon + 1).trim();
95
+ const count = parseInt(countStr, 10);
96
+ if (isNaN(count))
97
+ continue;
98
+ files.push({ file, count });
99
+ totalMatches += count;
100
+ }
101
+ return {
102
+ files,
103
+ totalMatches,
104
+ totalFiles: files.length,
105
+ };
106
+ }
107
+ //# sourceMappingURL=parsers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parsers.js","sourceRoot":"","sources":["../../src/lib/parsers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { type RunResult } from "@paretools/shared";
2
+ export declare function rgCmd(args: string[], cwd?: string): Promise<RunResult>;
3
+ export declare function fdCmd(args: string[], cwd?: string): Promise<RunResult>;
4
+ //# sourceMappingURL=search-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-runner.d.ts","sourceRoot":"","sources":["../../src/lib/search-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAExD,wBAAsB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAE5E;AAED,wBAAsB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAE5E"}
@@ -0,0 +1,8 @@
1
+ import { run } from "@paretools/shared";
2
+ export async function rgCmd(args, cwd) {
3
+ return run("rg", args, { cwd, timeout: 120_000 });
4
+ }
5
+ export async function fdCmd(args, cwd) {
6
+ return run("fd", args, { cwd, timeout: 120_000 });
7
+ }
8
+ //# sourceMappingURL=search-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-runner.js","sourceRoot":"","sources":["../../src/lib/search-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAkB,MAAM,mBAAmB,CAAC;AAExD,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,IAAc,EAAE,GAAY;IACtD,OAAO,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,IAAc,EAAE,GAAY;IACtD,OAAO,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;AACpD,CAAC"}
@@ -0,0 +1,54 @@
1
+ import { z } from "zod";
2
+ /** Zod schema for a single search match with file location and matched text. */
3
+ export declare const SearchMatchSchema: z.ZodObject<{
4
+ file: z.ZodString;
5
+ line: z.ZodNumber;
6
+ column: z.ZodNumber;
7
+ matchText: z.ZodString;
8
+ lineContent: z.ZodString;
9
+ }, z.core.$strip>;
10
+ /** Zod schema for structured ripgrep search output with match list and totals. */
11
+ export declare const SearchResultSchema: z.ZodObject<{
12
+ matches: z.ZodArray<z.ZodObject<{
13
+ file: z.ZodString;
14
+ line: z.ZodNumber;
15
+ column: z.ZodNumber;
16
+ matchText: z.ZodString;
17
+ lineContent: z.ZodString;
18
+ }, z.core.$strip>>;
19
+ totalMatches: z.ZodNumber;
20
+ filesSearched: z.ZodNumber;
21
+ }, z.core.$strip>;
22
+ export type SearchResult = z.infer<typeof SearchResultSchema>;
23
+ /** Zod schema for a single file entry with path, name, and extension. */
24
+ export declare const FindFileSchema: z.ZodObject<{
25
+ path: z.ZodString;
26
+ name: z.ZodString;
27
+ ext: z.ZodString;
28
+ }, z.core.$strip>;
29
+ /** Zod schema for structured fd output with file list and total count. */
30
+ export declare const FindResultSchema: z.ZodObject<{
31
+ files: z.ZodArray<z.ZodObject<{
32
+ path: z.ZodString;
33
+ name: z.ZodString;
34
+ ext: z.ZodString;
35
+ }, z.core.$strip>>;
36
+ total: z.ZodNumber;
37
+ }, z.core.$strip>;
38
+ export type FindResult = z.infer<typeof FindResultSchema>;
39
+ /** Zod schema for a single file match count entry. */
40
+ export declare const CountFileSchema: z.ZodObject<{
41
+ file: z.ZodString;
42
+ count: z.ZodNumber;
43
+ }, z.core.$strip>;
44
+ /** Zod schema for structured rg --count output with per-file counts and totals. */
45
+ export declare const CountResultSchema: z.ZodObject<{
46
+ files: z.ZodArray<z.ZodObject<{
47
+ file: z.ZodString;
48
+ count: z.ZodNumber;
49
+ }, z.core.$strip>>;
50
+ totalMatches: z.ZodNumber;
51
+ totalFiles: z.ZodNumber;
52
+ }, z.core.$strip>;
53
+ export type CountResult = z.infer<typeof CountResultSchema>;
54
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,gFAAgF;AAChF,eAAO,MAAM,iBAAiB;;;;;;iBAM5B,CAAC;AAEH,kFAAkF;AAClF,eAAO,MAAM,kBAAkB;;;;;;;;;;iBAI7B,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D,yEAAyE;AACzE,eAAO,MAAM,cAAc;;;;iBAIzB,CAAC;AAEH,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB;;;;;;;iBAG3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAI1D,sDAAsD;AACtD,eAAO,MAAM,eAAe;;;iBAG1B,CAAC;AAEH,mFAAmF;AACnF,eAAO,MAAM,iBAAiB;;;;;;;iBAI5B,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { z } from "zod";
2
+ // ── Search (rg --json) ───────────────────────────────────────────────
3
+ /** Zod schema for a single search match with file location and matched text. */
4
+ export const SearchMatchSchema = z.object({
5
+ file: z.string(),
6
+ line: z.number(),
7
+ column: z.number(),
8
+ matchText: z.string(),
9
+ lineContent: z.string(),
10
+ });
11
+ /** Zod schema for structured ripgrep search output with match list and totals. */
12
+ export const SearchResultSchema = z.object({
13
+ matches: z.array(SearchMatchSchema),
14
+ totalMatches: z.number(),
15
+ filesSearched: z.number(),
16
+ });
17
+ // ── Find (fd) ────────────────────────────────────────────────────────
18
+ /** Zod schema for a single file entry with path, name, and extension. */
19
+ export const FindFileSchema = z.object({
20
+ path: z.string(),
21
+ name: z.string(),
22
+ ext: z.string(),
23
+ });
24
+ /** Zod schema for structured fd output with file list and total count. */
25
+ export const FindResultSchema = z.object({
26
+ files: z.array(FindFileSchema),
27
+ total: z.number(),
28
+ });
29
+ // ── Count (rg --count) ──────────────────────────────────────────────
30
+ /** Zod schema for a single file match count entry. */
31
+ export const CountFileSchema = z.object({
32
+ file: z.string(),
33
+ count: z.number(),
34
+ });
35
+ /** Zod schema for structured rg --count output with per-file counts and totals. */
36
+ export const CountResultSchema = z.object({
37
+ files: z.array(CountFileSchema),
38
+ totalMatches: z.number(),
39
+ totalFiles: z.number(),
40
+ });
41
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,wEAAwE;AAExE,gFAAgF;AAChF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;CACxB,CAAC,CAAC;AAEH,kFAAkF;AAClF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC;IACnC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;IACxB,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE;CAC1B,CAAC,CAAC;AAIH,wEAAwE;AAExE,yEAAyE;AACzE,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IACrC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;CAChB,CAAC,CAAC;AAEH,0EAA0E;AAC1E,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC;IAC9B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;CAClB,CAAC,CAAC;AAIH,uEAAuE;AAEvE,sDAAsD;AACtD,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;CAClB,CAAC,CAAC;AAEH,mFAAmF;AACnF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC;IAC/B,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;IACxB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;CACvB,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerCountTool(server: McpServer): void;
3
+ //# sourceMappingURL=count.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"count.d.ts","sourceRoot":"","sources":["../../src/tools/count.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOzE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,QAuElD"}
@@ -0,0 +1,60 @@
1
+ import { z } from "zod";
2
+ import { compactDualOutput, assertNoFlagInjection, INPUT_LIMITS } from "@paretools/shared";
3
+ import { rgCmd } from "../lib/search-runner.js";
4
+ import { parseRgCountOutput } from "../lib/parsers.js";
5
+ import { formatCount, compactCountMap, formatCountCompact } from "../lib/formatters.js";
6
+ import { CountResultSchema } from "../schemas/index.js";
7
+ export function registerCountTool(server) {
8
+ server.registerTool("count", {
9
+ title: "Match Count",
10
+ description: "Counts pattern matches per file using ripgrep. Returns per-file match counts and totals. Use instead of running `rg --count` or `grep -c` in the terminal.",
11
+ inputSchema: {
12
+ pattern: z
13
+ .string()
14
+ .max(INPUT_LIMITS.STRING_MAX)
15
+ .describe("Regular expression pattern to count matches for"),
16
+ path: z
17
+ .string()
18
+ .max(INPUT_LIMITS.PATH_MAX)
19
+ .optional()
20
+ .describe("Directory or file to search in (default: cwd)"),
21
+ glob: z
22
+ .string()
23
+ .max(INPUT_LIMITS.SHORT_STRING_MAX)
24
+ .optional()
25
+ .describe("Glob pattern to filter files (e.g., '*.ts', '*.{js,jsx}')"),
26
+ caseSensitive: z
27
+ .boolean()
28
+ .optional()
29
+ .default(true)
30
+ .describe("Case-sensitive search (default: true)"),
31
+ compact: z
32
+ .boolean()
33
+ .optional()
34
+ .default(true)
35
+ .describe("Auto-compact when structured output exceeds raw CLI tokens. Set false to always get full schema."),
36
+ },
37
+ outputSchema: CountResultSchema,
38
+ }, async ({ pattern, path, glob, caseSensitive, compact }) => {
39
+ assertNoFlagInjection(pattern, "pattern");
40
+ if (path)
41
+ assertNoFlagInjection(path, "path");
42
+ if (glob)
43
+ assertNoFlagInjection(glob, "glob");
44
+ const cwd = path || process.cwd();
45
+ const args = ["--count"];
46
+ if (!caseSensitive) {
47
+ args.push("--ignore-case");
48
+ }
49
+ if (glob) {
50
+ args.push("--glob", glob);
51
+ }
52
+ args.push(pattern);
53
+ const result = await rgCmd(args, cwd);
54
+ // rg exits with code 1 when no matches are found — that's not an error
55
+ const data = parseRgCountOutput(result.stdout);
56
+ const rawOutput = (result.stdout + "\n" + result.stderr).trim();
57
+ return compactDualOutput(data, rawOutput, formatCount, compactCountMap, formatCountCompact, compact === false);
58
+ });
59
+ }
60
+ //# sourceMappingURL=count.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"count.js","sourceRoot":"","sources":["../../src/tools/count.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC3F,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACxF,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,UAAU,iBAAiB,CAAC,MAAiB;IACjD,MAAM,CAAC,YAAY,CACjB,OAAO,EACP;QACE,KAAK,EAAE,aAAa;QACpB,WAAW,EACT,4JAA4J;QAC9J,WAAW,EAAE;YACX,OAAO,EAAE,CAAC;iBACP,MAAM,EAAE;iBACR,GAAG,CAAC,YAAY,CAAC,UAAU,CAAC;iBAC5B,QAAQ,CAAC,iDAAiD,CAAC;YAC9D,IAAI,EAAE,CAAC;iBACJ,MAAM,EAAE;iBACR,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAC;iBAC1B,QAAQ,EAAE;iBACV,QAAQ,CAAC,+CAA+C,CAAC;YAC5D,IAAI,EAAE,CAAC;iBACJ,MAAM,EAAE;iBACR,GAAG,CAAC,YAAY,CAAC,gBAAgB,CAAC;iBAClC,QAAQ,EAAE;iBACV,QAAQ,CAAC,2DAA2D,CAAC;YACxE,aAAa,EAAE,CAAC;iBACb,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,OAAO,CAAC,IAAI,CAAC;iBACb,QAAQ,CAAC,uCAAuC,CAAC;YACpD,OAAO,EAAE,CAAC;iBACP,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,OAAO,CAAC,IAAI,CAAC;iBACb,QAAQ,CACP,kGAAkG,CACnG;SACJ;QACD,YAAY,EAAE,iBAAiB;KAChC,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE,EAAE;QACxD,qBAAqB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAC1C,IAAI,IAAI;YAAE,qBAAqB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC9C,IAAI,IAAI;YAAE,qBAAqB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAE9C,MAAM,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;QAEzB,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC7B,CAAC;QAED,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAEnB,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAEtC,uEAAuE;QACvE,MAAM,IAAI,GAAG,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC/C,MAAM,SAAS,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAEhE,OAAO,iBAAiB,CACtB,IAAI,EACJ,SAAS,EACT,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,OAAO,KAAK,KAAK,CAClB,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerFindTool(server: McpServer): void;
3
+ //# sourceMappingURL=find.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find.d.ts","sourceRoot":"","sources":["../../src/tools/find.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOzE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,QAiFjD"}
@@ -0,0 +1,71 @@
1
+ import { z } from "zod";
2
+ import { compactDualOutput, assertNoFlagInjection, INPUT_LIMITS } from "@paretools/shared";
3
+ import { fdCmd } from "../lib/search-runner.js";
4
+ import { parseFdOutput } from "../lib/parsers.js";
5
+ import { formatFind, compactFindMap, formatFindCompact } from "../lib/formatters.js";
6
+ import { FindResultSchema } from "../schemas/index.js";
7
+ export function registerFindTool(server) {
8
+ server.registerTool("find", {
9
+ title: "Find Files",
10
+ description: "Finds files and directories using fd with structured output. Returns file paths, names, and extensions. Use instead of running `fd` or `find` in the terminal.",
11
+ inputSchema: {
12
+ pattern: z
13
+ .string()
14
+ .max(INPUT_LIMITS.STRING_MAX)
15
+ .optional()
16
+ .describe("Regex pattern to match file/directory names"),
17
+ path: z
18
+ .string()
19
+ .max(INPUT_LIMITS.PATH_MAX)
20
+ .optional()
21
+ .describe("Directory to search in (default: cwd)"),
22
+ type: z
23
+ .enum(["file", "directory", "symlink"])
24
+ .optional()
25
+ .describe("Filter by entry type: file, directory, or symlink"),
26
+ extension: z
27
+ .string()
28
+ .max(INPUT_LIMITS.SHORT_STRING_MAX)
29
+ .optional()
30
+ .describe("Filter by file extension (e.g., 'ts', 'js')"),
31
+ maxResults: z
32
+ .number()
33
+ .optional()
34
+ .default(1000)
35
+ .describe("Maximum number of results to return (default: 1000)"),
36
+ compact: z
37
+ .boolean()
38
+ .optional()
39
+ .default(true)
40
+ .describe("Auto-compact when structured output exceeds raw CLI tokens. Set false to always get full schema."),
41
+ },
42
+ outputSchema: FindResultSchema,
43
+ }, async ({ pattern, path, type, extension, maxResults, compact }) => {
44
+ if (pattern)
45
+ assertNoFlagInjection(pattern, "pattern");
46
+ if (path)
47
+ assertNoFlagInjection(path, "path");
48
+ if (extension)
49
+ assertNoFlagInjection(extension, "extension");
50
+ const cwd = path || process.cwd();
51
+ const args = ["--color", "never"];
52
+ if (type) {
53
+ const typeMap = { file: "f", directory: "d", symlink: "l" };
54
+ args.push("--type", typeMap[type]);
55
+ }
56
+ if (extension) {
57
+ args.push("--extension", extension);
58
+ }
59
+ if (maxResults) {
60
+ args.push("--max-results", String(maxResults));
61
+ }
62
+ if (pattern) {
63
+ args.push(pattern);
64
+ }
65
+ const result = await fdCmd(args, cwd);
66
+ const data = parseFdOutput(result.stdout, maxResults ?? 1000);
67
+ const rawOutput = (result.stdout + "\n" + result.stderr).trim();
68
+ return compactDualOutput(data, rawOutput, formatFind, compactFindMap, formatFindCompact, compact === false);
69
+ });
70
+ }
71
+ //# sourceMappingURL=find.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find.js","sourceRoot":"","sources":["../../src/tools/find.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerAllTools(server: McpServer): void;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAMzE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,QAKjD"}
@@ -0,0 +1,14 @@
1
+ import { shouldRegisterTool } from "@paretools/shared";
2
+ import { registerSearchTool } from "./search.js";
3
+ import { registerFindTool } from "./find.js";
4
+ import { registerCountTool } from "./count.js";
5
+ export function registerAllTools(server) {
6
+ const s = (name) => shouldRegisterTool("search", name);
7
+ if (s("search"))
8
+ registerSearchTool(server);
9
+ if (s("find"))
10
+ registerFindTool(server);
11
+ if (s("count"))
12
+ registerCountTool(server);
13
+ }
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,UAAU,gBAAgB,CAAC,MAAiB;IAChD,MAAM,CAAC,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,kBAAkB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC/D,IAAI,CAAC,CAAC,QAAQ,CAAC;QAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,CAAC,CAAC,MAAM,CAAC;QAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,CAAC,OAAO,CAAC;QAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerSearchTool(server: McpServer): void;
3
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOzE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,QA8EnD"}
@@ -0,0 +1,67 @@
1
+ import { z } from "zod";
2
+ import { compactDualOutput, assertNoFlagInjection, INPUT_LIMITS } from "@paretools/shared";
3
+ import { rgCmd } from "../lib/search-runner.js";
4
+ import { parseRgJsonOutput } from "../lib/parsers.js";
5
+ import { formatSearch, compactSearchMap, formatSearchCompact } from "../lib/formatters.js";
6
+ import { SearchResultSchema } from "../schemas/index.js";
7
+ export function registerSearchTool(server) {
8
+ server.registerTool("search", {
9
+ title: "Code Search",
10
+ description: "Searches file contents using ripgrep with structured JSON output. Returns match locations with file, line, column, matched text, and line content. Use instead of running `rg` or `grep` in the terminal.",
11
+ inputSchema: {
12
+ pattern: z
13
+ .string()
14
+ .max(INPUT_LIMITS.STRING_MAX)
15
+ .describe("Regular expression pattern to search for"),
16
+ path: z
17
+ .string()
18
+ .max(INPUT_LIMITS.PATH_MAX)
19
+ .optional()
20
+ .describe("Directory or file to search in (default: cwd)"),
21
+ glob: z
22
+ .string()
23
+ .max(INPUT_LIMITS.SHORT_STRING_MAX)
24
+ .optional()
25
+ .describe("Glob pattern to filter files (e.g., '*.ts', '*.{js,jsx}')"),
26
+ caseSensitive: z
27
+ .boolean()
28
+ .optional()
29
+ .default(true)
30
+ .describe("Case-sensitive search (default: true)"),
31
+ maxResults: z
32
+ .number()
33
+ .optional()
34
+ .default(1000)
35
+ .describe("Maximum number of matches to return (default: 1000)"),
36
+ compact: z
37
+ .boolean()
38
+ .optional()
39
+ .default(true)
40
+ .describe("Auto-compact when structured output exceeds raw CLI tokens. Set false to always get full schema."),
41
+ },
42
+ outputSchema: SearchResultSchema,
43
+ }, async ({ pattern, path, glob, caseSensitive, maxResults, compact }) => {
44
+ assertNoFlagInjection(pattern, "pattern");
45
+ if (path)
46
+ assertNoFlagInjection(path, "path");
47
+ if (glob)
48
+ assertNoFlagInjection(glob, "glob");
49
+ const cwd = path || process.cwd();
50
+ const args = ["--json"];
51
+ if (!caseSensitive) {
52
+ args.push("--ignore-case");
53
+ }
54
+ if (glob) {
55
+ args.push("--glob", glob);
56
+ }
57
+ args.push(pattern);
58
+ // When path is provided, pass it as the search path argument to rg
59
+ // (not as cwd, since cwd is already set to it for relative paths)
60
+ const result = await rgCmd(args, cwd);
61
+ // rg exits with code 1 when no matches are found — that's not an error
62
+ const data = parseRgJsonOutput(result.stdout, maxResults ?? 1000);
63
+ const rawOutput = (result.stdout + "\n" + result.stderr).trim();
64
+ return compactDualOutput(data, rawOutput, formatSearch, compactSearchMap, formatSearchCompact, compact === false);
65
+ });
66
+ }
67
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@paretools/search",
3
+ "version": "0.7.0",
4
+ "mcpName": "io.github.Dave-London/search",
5
+ "description": "MCP server for code search (ripgrep + fd) with structured, token-efficient output",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "mcp",
9
+ "mcp-server",
10
+ "model-context-protocol",
11
+ "structured-output",
12
+ "ai",
13
+ "ai-agent",
14
+ "claude",
15
+ "cursor",
16
+ "copilot",
17
+ "token-efficient",
18
+ "devtools",
19
+ "cli",
20
+ "ripgrep",
21
+ "rg",
22
+ "fd",
23
+ "search",
24
+ "find",
25
+ "grep"
26
+ ],
27
+ "homepage": "https://github.com/Dave-London/Pare/tree/main/packages/server-search",
28
+ "engines": {
29
+ "node": ">=20"
30
+ },
31
+ "type": "module",
32
+ "bin": {
33
+ "pare-search": "./dist/index.js"
34
+ },
35
+ "exports": {
36
+ ".": {
37
+ "types": "./dist/index.d.ts",
38
+ "default": "./dist/index.js"
39
+ }
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "https://github.com/Dave-London/Pare.git",
47
+ "directory": "packages/server-search"
48
+ },
49
+ "files": [
50
+ "dist"
51
+ ],
52
+ "dependencies": {
53
+ "@modelcontextprotocol/sdk": "^1.26.0",
54
+ "zod": "^4.3.6",
55
+ "@paretools/shared": "0.6.0"
56
+ },
57
+ "devDependencies": {
58
+ "@types/node": "^25.2.3",
59
+ "typescript": "^5.7.0",
60
+ "vitest": "^4.0.18",
61
+ "@paretools/tsconfig": "0.0.0"
62
+ },
63
+ "scripts": {
64
+ "build": "tsc",
65
+ "test": "vitest run",
66
+ "test:watch": "vitest",
67
+ "lint": "eslint src/"
68
+ }
69
+ }