mirror of
https://github.com/lukaszraczylo/filepuff-mcp.git
synced 2026-06-05 22:23:50 +00:00
5ad975ee7a
* v2.0: token-optimization overhaul Additive (backward-compatible flags): - file_read: skeleton mode, strip (imports/license/block_comments), compact_line_numbers, 8-char etag with prefix-match compat - ast_query: format=verbose|compact|location, pagination cursor - file_search: cluster mode, pagination cursor - lsp_query (references): compact output Breaking (v2): - Preambles removed; opt-in verbose=true restores - edit_apply: response=count|diff|none, default count - ping tool removed - symbol_at/find_definition/find_references merged into lsp_query - Tool descriptions trimmed -83%, help moved to filepuff://help/<tool> - Batch file_read dedups by etag Protocol: - ResourceLink returned for file_read >64 KiB (force_inline override) - OnAfterInitialize hook reads capabilities.experimental.filepuff for session defaults (default_format, default_max_results, default_cluster, compact_refs, line_numbers, resource_link_threshold) * fix: drop --max-total-count from ripgrep args The flag does not exist in stable ripgrep (confirmed up to 15.1.0 -- "unrecognized flag --max-total-count, similar flags that are available: --max-count"). Every file_search call failed on hosts with stock rg. --max-count is per-file, not a drop-in replacement, so rely on the in-process truncation in parseOutput that was already the documented safety net.
184 lines
9.8 KiB
Go
184 lines
9.8 KiB
Go
// Package server implements the MCP server for file operations.
|
|
package server
|
|
|
|
// helpFileRead is the full flag documentation and examples for the file_read tool,
|
|
// served at filepuff://help/file_read.
|
|
const helpFileRead = "# file_read — flags and examples\n\n" +
|
|
"## Token-saving features\n\n" +
|
|
"| Flag | Effect |\n" +
|
|
"|------|--------|\n" +
|
|
"| `previous_etag` | Skip re-reading unchanged files. Returns `[unchanged, etag: ...]` if file is unchanged. |\n" +
|
|
"| `symbol_name` | Read only a named function/struct/class — eliminates an ast_query round-trip. |\n" +
|
|
"| `symbols_only=true` | Return only symbol list (~95% fewer tokens). Requires `include_ast=true`. Alias: `mode='symbols_only'`. |\n" +
|
|
"| `mode` | `full` (default) \\| `skeleton` (signatures + `{ ... }` stubs, bodies elided) \\| `symbols_only` |\n" +
|
|
"| `strip` | Remove content classes before line-numbering: `imports`, `license`, `block_comments`. Emits `[stripped: ...]` footer. |\n" +
|
|
"| `no_line_numbers=true` | Omit the ` 12│ ` line-number prefix (~10% savings). `line_number_interval=0` has the same effect. |\n" +
|
|
"| `line_number_interval=N` | Print line numbers only every N lines. |\n" +
|
|
"| `compact_line_numbers=true` | Use compact `12│` prefix instead of ` 12│ ` (no padding, no trailing space). |\n" +
|
|
"| `collapse_blank_lines=true` | Collapse consecutive blank lines to one. |\n" +
|
|
"| `max_lines=N` | Truncate output with omitted count notice. Applied after `line_start`/`line_end`. |\n" +
|
|
"| `paths=[...]` | Read multiple files in one call. Each file gets a `--- path ---` header. |\n\n" +
|
|
"All responses include `[etag: hex]` footer (8 hex chars) for use as `previous_etag` in subsequent reads.\n\n" +
|
|
"## Examples\n\n" +
|
|
"```json\n" +
|
|
"// Full file\n" +
|
|
`{"path": "main.go"}` + "\n\n" +
|
|
"// Etag check — returns unchanged notice if file hasn't changed\n" +
|
|
`{"path": "main.go", "previous_etag": "a3f9c2b1"}` + "\n\n" +
|
|
"// Read only one named symbol\n" +
|
|
`{"path": "server.go", "symbol_name": "handleFileRead"}` + "\n\n" +
|
|
"// Skeleton mode — signatures only, bodies elided\n" +
|
|
`{"path": "server.go", "mode": "skeleton"}` + "\n\n" +
|
|
"// Strip imports and license header\n" +
|
|
`{"path": "main.go", "strip": ["imports", "license"]}` + "\n\n" +
|
|
"// Batch read multiple files\n" +
|
|
`{"paths": ["a.go", "b.go"]}` + "\n\n" +
|
|
"// Specific line range\n" +
|
|
`{"path": "main.go", "line_start": 10, "line_end": 50}` + "\n" +
|
|
"```\n"
|
|
|
|
// helpFileSearch is the full flag documentation and examples for the file_search tool,
|
|
// served at filepuff://help/file_search.
|
|
const helpFileSearch = "# file_search — flags and examples\n\n" +
|
|
"## Output format\n\n" +
|
|
"Matches grouped by file. Each file section has matching lines prefixed by `L{line}│` and context lines prefixed by ` │`. Zero matches: `No matches found.`\n\n" +
|
|
"## Flags\n\n" +
|
|
"| Flag | Effect |\n" +
|
|
"|------|--------|\n" +
|
|
"| `verbose=true` | Emit `Found N matches in M files:` preamble (v1 behaviour). Default: false. |\n" +
|
|
"| `cluster=true` | Coalesce consecutive match lines into ranges (`L12-14│ text`). Drops context lines for density. |\n" +
|
|
"| `cursor` | Opaque pagination token from a previous truncated response — fetches next page. |\n" +
|
|
"| `max_results` | Page size for pagination. Re-run with `cursor` to get next page. |\n" +
|
|
"| `context_lines` | Number of context lines around matches (default: 2). |\n" +
|
|
"| `ignore_case` | Case-insensitive search. |\n" +
|
|
"| `regex` | Treat pattern as regex (default: true). |\n" +
|
|
"| `file_types` | Restrict to file extensions, e.g. `[\"go\", \"ts\"]`. |\n" +
|
|
"| `paths` | Paths to search in (defaults to workspace root). |\n\n" +
|
|
"## Examples\n\n" +
|
|
"```json\n" +
|
|
"// Search for error-returning functions in Go files\n" +
|
|
`{"pattern": "func.*Error", "file_types": ["go"], "max_results": 20}` + "\n\n" +
|
|
"// Case-insensitive literal search\n" +
|
|
`{"pattern": "TODO", "ignore_case": true}` + "\n\n" +
|
|
"// Paginated search — fetch next page\n" +
|
|
`{"pattern": "import", "max_results": 50, "cursor": "<token from previous response>"}` + "\n\n" +
|
|
"// Clustered — dense view of many matches\n" +
|
|
`{"pattern": "return err", "file_types": ["go"], "cluster": true}` + "\n" +
|
|
"```\n"
|
|
|
|
// helpASTQuery is the full flag documentation and examples for the ast_query tool,
|
|
// served at filepuff://help/ast_query.
|
|
const helpASTQuery = "# ast_query — flags and examples\n\n" +
|
|
"## Output format\n\n" +
|
|
"Entries in format `**file:line** (node_type)` with code blocks and captured variables (`$NAME=value`). Zero matches: `No matches found.`\n\n" +
|
|
"## Flags\n\n" +
|
|
"| Flag | Effect |\n" +
|
|
"|------|--------|\n" +
|
|
"| `verbose=true` | Emit `Found N match(es):` preamble (v1 behaviour). Default: false. |\n" +
|
|
"| `format` | `verbose` (default, full code+captures) \\| `compact` (one line per match) \\| `location` (file:line only) |\n" +
|
|
"| `cursor` | Opaque pagination token from a previous truncated response — fetches next page. |\n" +
|
|
"| `max_results` | Page size (default: 100). |\n" +
|
|
"| `name_exact` | Exact symbol name to match. |\n" +
|
|
"| `name_matches` | Regex pattern to filter by name. |\n" +
|
|
"| `kind_in` | Node types to match (e.g. `function_declaration`, `class_declaration`). |\n" +
|
|
"| `paths` | Paths to search in (defaults to workspace root). |\n\n" +
|
|
"## Pattern placeholders\n\n" +
|
|
"| Placeholder | Meaning |\n" +
|
|
"|-------------|----------|\n" +
|
|
"| `$NAME` | Matches a single node, captures as `$NAME` |\n" +
|
|
"| `$$$ARGS` | Matches zero or more nodes (variadic capture) |\n" +
|
|
"| `$_` | Wildcard — matches any single node, no capture |\n\n" +
|
|
"## Examples\n\n" +
|
|
"```json\n" +
|
|
"// All Go functions returning error\n" +
|
|
`{"pattern": "func $NAME($$$ARGS) error", "language": "go"}` + "\n\n" +
|
|
"// Python classes\n" +
|
|
`{"pattern": "class $NAME: $$$BODY", "language": "python"}` + "\n\n" +
|
|
"// Specific named function\n" +
|
|
`{"pattern": "func $NAME($$$ARGS)", "language": "go", "name_exact": "NewServer"}` + "\n\n" +
|
|
"// Compact output — one line per match\n" +
|
|
`{"pattern": "func $NAME($$$ARGS) error", "language": "go", "format": "compact"}` + "\n" +
|
|
"```\n"
|
|
|
|
// helpLSPQuery is the full flag documentation and examples for the lsp_query tool,
|
|
// served at filepuff://help/lsp_query.
|
|
const helpLSPQuery = "# lsp_query — flags and examples\n\n" +
|
|
"## Actions\n\n" +
|
|
"### hover\n" +
|
|
"Returns type/doc from LSP, falls back to AST node info. `verbose=true` adds `**Symbol Information**` header.\n\n" +
|
|
"### definition\n" +
|
|
"Returns `file:line:col` + 3-line code preview for each definition. `verbose=true` adds `Found N definition(s):` header.\n\n" +
|
|
"### references\n" +
|
|
"Returns references grouped by file. Flags:\n" +
|
|
"- `include_declaration` (default true) — include the declaration itself\n" +
|
|
"- `compact=true` — collapse to one line per file\n" +
|
|
"- `verbose=true` — add `Found N reference(s):` header\n\n" +
|
|
"Note: `include_declaration` and `compact` are errors when used with actions other than `references`.\n\n" +
|
|
"## Examples\n\n" +
|
|
"```json\n" +
|
|
"// Hover — type/doc at position\n" +
|
|
`{"action": "hover", "file": "server.go", "line": 45, "column": 6}` + "\n\n" +
|
|
"// Definition — where is this symbol defined?\n" +
|
|
`{"action": "definition", "file": "handler.go", "line": 23, "column": 10}` + "\n\n" +
|
|
"// References — all usages\n" +
|
|
`{"action": "references", "file": "types.go", "line": 5, "column": 6}` + "\n\n" +
|
|
"// References — compact (one line per file)\n" +
|
|
`{"action": "references", "file": "types.go", "line": 5, "column": 6, "compact": true}` + "\n" +
|
|
"```\n"
|
|
|
|
// helpEditApply is the full flag documentation and examples for the edit_apply tool,
|
|
// served at filepuff://help/edit_apply.
|
|
const helpEditApply = "# edit_apply — flags and examples\n\n" +
|
|
"## Response format (`response` flag)\n\n" +
|
|
"| Value | Output |\n" +
|
|
"|-------|--------|\n" +
|
|
"| `count` (default) | `+3 -1` added/removed line counts only |\n" +
|
|
"| `diff` | Full unified diff of changes made |\n" +
|
|
"| `none` | Empty response (silent success) |\n\n" +
|
|
"`compact_response=true` is a deprecated alias for `response=\"count\"` kept for pre-v2 compatibility.\n\n" +
|
|
"For code files (Go, TypeScript, JavaScript, Python, C, C++, Rust) syntax is validated before writing — the edit is rejected if it would produce invalid syntax.\n\n" +
|
|
"## Selector types\n\n" +
|
|
"### AST-mode selectors (code files)\n" +
|
|
"- `selector_kind` — AST node type (e.g. `function_declaration`, `class_declaration`)\n" +
|
|
"- `selector_name` — symbol name to match\n\n" +
|
|
"### Text-mode selectors (all files)\n" +
|
|
"- `selector_text` — exact text to match (must be unique, or use `selector_index`)\n" +
|
|
"- `selector_pattern` — regex pattern to match\n" +
|
|
"- `selector_line` / `selector_line_end` — line range\n\n" +
|
|
"### Shared\n" +
|
|
"- `selector_index` — index of match when multiple exist (default: 0)\n\n" +
|
|
"## Examples\n\n" +
|
|
"```json\n" +
|
|
"// AST mode — replace a named function\n" +
|
|
"{\n" +
|
|
` "file": "main.go",` + "\n" +
|
|
` "operation": "replace",` + "\n" +
|
|
` "selector_kind": "function_declaration",` + "\n" +
|
|
` "selector_name": "Hello",` + "\n" +
|
|
` "new_content": "func Hello() {\n\treturn\n}"` + "\n" +
|
|
"}\n\n" +
|
|
"// Text mode — replace a markdown header\n" +
|
|
"{\n" +
|
|
` "file": "README.md",` + "\n" +
|
|
` "operation": "replace",` + "\n" +
|
|
` "selector_text": "## Old Header",` + "\n" +
|
|
` "new_content": "## New Header"` + "\n" +
|
|
"}\n\n" +
|
|
"// Line range replacement\n" +
|
|
"{\n" +
|
|
` "file": "config.yaml",` + "\n" +
|
|
` "operation": "replace",` + "\n" +
|
|
` "selector_line": 5,` + "\n" +
|
|
` "selector_line_end": 10,` + "\n" +
|
|
` "new_content": "key: value"` + "\n" +
|
|
"}\n\n" +
|
|
"// Request full diff in response\n" +
|
|
"{\n" +
|
|
` "file": "main.go",` + "\n" +
|
|
` "operation": "replace",` + "\n" +
|
|
` "selector_name": "Hello",` + "\n" +
|
|
` "new_content": "func Hello() {}",` + "\n" +
|
|
` "response": "diff"` + "\n" +
|
|
"}\n" +
|
|
"```\n"
|