Skip to content

Commit 8a71f1e

Browse files
author
Bruce Hauman
committed
Require type parameter on deps_grep and filter binary entries
- Make type required: "clj" (source in jar) or "java" (download source jars) - Always filter binary entries (.class, images, native libs, archives, etc.) - Remove needs-java-sources? and type-to-glob in favor of direct type check - Add no-backwards-compatibility note to CLAUDE.md for MCP tool guidelines
1 parent 825d833 commit 8a71f1e

File tree

3 files changed

+42
-28
lines changed

3 files changed

+42
-28
lines changed

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@
1919
- Validate inputs and provide helpful error messages
2020
- Return structured data with both result and error status
2121
- Maintain atom-based state for consistent service access
22+
- **No backwards compatibility required**: This is an MCP server — tool schemas, parameters, and behavior can change freely between versions. Don't hesitate to make breaking changes to tool contracts when it improves the design.
2223

2324
Fast Apply: IMPORTANT: Use \`edit_file\` over \`str_replace\` or full file writes. It works with partial code snippets—no need for full file content.

src/clojure_mcp/tools/deps_grep/core.clj

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -69,30 +69,38 @@
6969
(when (.exists sources-file)
7070
sources-path))))
7171

72-
(defn needs-java-sources?
73-
"Check if the search options indicate we're looking for Java files."
74-
[{:keys [type glob]}]
75-
(or (= "java" type)
76-
(and glob (re-find #"\.java" glob))))
72+
(def ^:private binary-extensions
73+
"File extensions that are binary and should never be searched."
74+
#{".class" ".jar" ".war" ".ear"
75+
".png" ".jpg" ".jpeg" ".gif" ".ico" ".bmp" ".svg" ".webp"
76+
".so" ".dll" ".dylib" ".o" ".a"
77+
".zip" ".gz" ".tar" ".bz2" ".xz"
78+
".ttf" ".otf" ".woff" ".woff2"
79+
".pdf" ".doc" ".docx"})
80+
81+
(defn binary-entry?
82+
"Returns true if a jar entry path has a known binary extension."
83+
[entry-path]
84+
(some #(str/ends-with? (str/lower-case entry-path) %) binary-extensions))
7785

7886
(defn get-jars-with-sources
7987
"Given a list of jars and search opts, return jars plus any available sources jars.
80-
When searching for Java files, downloads missing sources from Maven Central."
88+
When type is \"java\", downloads missing sources from Maven Central."
8189
[jars opts]
8290
(let [;; First find sources jars already in Maven cache
8391
existing-sources (->> jars
8492
(keep find-sources-jar)
8593
(remove (set jars)))]
86-
(if (needs-java-sources? opts)
87-
;; For Java searches, also download missing sources
94+
(if (= "java" (:type opts))
95+
;; For Java ecosystem jars, download source jars to get searchable text
8896
(let [jars-with-sources (set (map #(str/replace % #"-sources\.jar$" ".jar")
8997
existing-sources))
9098
jars-missing-sources (remove jars-with-sources jars)
9199
_ (log/debug "Checking for Java sources for" (count jars-missing-sources) "jars")
92100
downloaded-sources (deps-sources/ensure-sources-jars! jars-missing-sources)]
93101
(log/debug "Downloaded" (count downloaded-sources) "sources jars")
94102
(into (vec jars) (concat existing-sources downloaded-sources)))
95-
;; For non-Java searches, just use existing sources
103+
;; For Clojure ecosystem jars, source is in the jar already
96104
(into (vec jars) existing-sources))))
97105

98106
(defn parse-library-filter
@@ -153,19 +161,14 @@
153161
(str "(" (str/replace alts "," "|") ")"))))]
154162
(boolean (re-find (re-pattern (str pattern-regex "$")) path)))))
155163

156-
(defn type-to-glob
157-
"Convert a file type (like 'clj') to a glob pattern."
158-
[type-str]
159-
(when type-str
160-
(str "*." type-str)))
161-
162164
(defn filter-entries
163-
"Filter jar entries by glob and/or type patterns."
164-
[entries {:keys [glob type]}]
165-
(let [effective-glob (or glob (type-to-glob type))]
166-
(if effective-glob
167-
(filter #(glob-matches? effective-glob %) entries)
168-
entries)))
165+
"Filter jar entries, removing binary files and applying optional glob pattern.
166+
Binary extensions (.class, images, native libs, etc.) are always excluded."
167+
[entries {:keys [glob]}]
168+
(let [text-entries (remove binary-entry? entries)]
169+
(if glob
170+
(filter #(glob-matches? glob %) text-entries)
171+
text-entries)))
169172

170173
(defn search-jar-entry-rg
171174
"Search using ripgrep. Reads jar entry via Java, pipes content to rg via stdin.
@@ -242,8 +245,10 @@
242245
- pattern: Regex pattern to search for
243246
- opts: Map of options
244247
:library - Required. Maven group or group/artifact to search
245-
:glob - Filter files by glob pattern (e.g., \"*.clj\")
246-
:type - Filter files by type (e.g., \"clj\", \"java\")
248+
:type - Required. Jar ecosystem type: \"clj\" or \"java\"
249+
\"clj\" searches the jar directly (source is in the jar)
250+
\"java\" downloads source jars from Maven Central
251+
:glob - Optional. Filter files by glob pattern (e.g., \"*.java\")
247252
:output-mode - :content, :files-with-matches, or :count
248253
:case-insensitive - Case insensitive search
249254
:line-numbers - Include line numbers (default true for content mode)
@@ -263,7 +268,7 @@
263268
(if-not base-jars
264269
{:error "Failed to resolve classpath. Is this a deps.edn project?"}
265270
(let [library (:library opts)
266-
cache-key [project-dir library (needs-java-sources? opts)]
271+
cache-key [project-dir library (:type opts)]
267272
filtered-jars (filter-jars-by-library base-jars library)]
268273
(if (empty? filtered-jars)
269274
{:error (str "No libraries found matching: " (:library opts)

src/clojure_mcp/tools/deps_grep/tool.clj

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@
2121
"Search for patterns in dependency jar files on the classpath.
2222
- Uses `clojure -Spath` to resolve the exact dependency jars
2323
- Searches inside jar files for matching content
24-
- Supports glob patterns to filter file types (e.g., \"*.clj\", \"*.java\")
24+
- The `type` parameter is required and indicates the jar ecosystem:
25+
- \"clj\": Clojure libraries (source is in the jar, searched directly)
26+
- \"java\": Java libraries (source jars downloaded from Maven Central)
27+
- Binary files (.class, images, native libs) are always excluded
28+
- Optional `glob` parameter narrows which files are searched (e.g., \"*.java\", \"*test*\")
2529
- Three output modes: content (with line numbers), files_with_matches, count
26-
- Use this to find code patterns in your project's dependencies
2730
- Results include both jar path and entry path for use with deps_read tool
2831
- Requires: clojure CLI. Optional: ripgrep (rg) for context/multiline support
2932
- For Java sources: also requires curl (downloads sources from Maven Central)")
@@ -37,15 +40,16 @@
3740
:glob {:type :string
3841
:description "Glob pattern to filter files (e.g., \"*.clj\", \"*.{clj,java}\")"}
3942
:type {:type :string
40-
:description "File type to search (e.g., \"clj\", \"java\"). Alternative to glob."}
43+
:enum ["clj" "java"]
44+
:description "Jar ecosystem type. \"clj\" for Clojure libraries (source in jar), \"java\" for Java libraries (downloads source jars from Maven Central)."}
4145
:output_mode {:type :string
4246
:enum ["content" "files_with_matches" "count"]
4347
:description "Output mode: 'content' shows matching lines, 'files_with_matches' shows file paths, 'count' shows match count"}
4448
:case_insensitive {:type :boolean
4549
:description "Case insensitive search"}
4650
:head_limit {:type :integer
4751
:description "Limit output to first N results"}}
48-
:required [:pattern :library]})
52+
:required [:pattern :library :type]})
4953

5054
(defmethod tool-system/validate-inputs :deps-grep [{:keys [nrepl-client-atom]} inputs]
5155
(let [{:keys [pattern library glob type output_mode case_insensitive head_limit]} inputs
@@ -57,6 +61,10 @@
5761
(throw (ex-info "Missing required parameter: pattern" {:inputs inputs})))
5862
(when-not library
5963
(throw (ex-info "Missing required parameter: library" {:inputs inputs})))
64+
(when-not type
65+
(throw (ex-info "Missing required parameter: type (must be \"clj\" or \"java\")" {:inputs inputs})))
66+
(when-not (#{"clj" "java"} type)
67+
(throw (ex-info (str "Invalid type: \"" type "\". Must be \"clj\" or \"java\"") {:inputs inputs})))
6068

6169
{:project-dir project-dir
6270
:pattern pattern

0 commit comments

Comments
 (0)