Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/clojure_mcp/config.clj
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,7 @@
(assoc :nrepl-env-type (:nrepl-env-type config)))))

(defn load-config
"Loads configuration from both user home (~/.clojure-mcp/config.edn) and project directory.
User home config provides defaults, project config provides overrides.
Validates both configs before merging."
"Loads the configuration from user home and project directories."
[cli-config-file user-dir]
;; Load user home config first (provides defaults)
(let [home-config (load-home-config)
Expand Down
10 changes: 1 addition & 9 deletions src/clojure_mcp/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,6 @@

This function handles the complete setup process including:
- Creating the nREPL client connection
- Starting the polling mechanism
- Loading required namespaces and helpers (if Clojure environment)
- Setting up the working directory
- Loading configuration
Expand All @@ -285,10 +284,7 @@
(try
(let [nrepl-client-map (nrepl/create (dissoc initial-config :project-dir :nrepl-env-type))
cli-env-type (:nrepl-env-type initial-config)
_ (do
(log/info "nREPL client map created")
(nrepl/start-polling nrepl-client-map)
(log/info "Started polling nREPL"))
_ (log/info "nREPL client map created")
;; Detect environment type early
;; TODO this needs to be sorted out
env-type (dialects/detect-nrepl-env-type nrepl-client-map)
Expand All @@ -314,7 +310,6 @@
(log/info "Creating additional nREPL connection" initial-config)
(try
(let [nrepl-client-map (nrepl/create initial-config)]
(nrepl/start-polling nrepl-client-map)
;; copy config
;; maybe we should create this just like the normal nrelp connection?
;; we should introspect the project and get a working directory
Expand All @@ -331,15 +326,12 @@
"Convenience higher-level API function to gracefully shut down MCP and nREPL servers.

This function handles the complete shutdown process including:
- Stopping nREPL polling if a client exists in nrepl-client-atom
- Gracefully closing the MCP server
- Proper error handling and logging"
[nrepl-client-atom]
(log/info "Shutting down servers")
(try
(when-let [client @nrepl-client-atom]
(log/info "Stopping nREPL polling")
(nrepl/stop-polling client)
;; Clean up auto-started nREPL process if present
(when-let [nrepl-process (:nrepl-process client)]
(log/info "Cleaning up auto-started nREPL process")
Expand Down
23 changes: 15 additions & 8 deletions src/clojure_mcp/dialects.clj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
(:require [clojure.edn :as edn]
[clojure.java.io :as io]
[taoensso.timbre :as log]
[nrepl.core :as nrepl-core]
[clojure-mcp.nrepl :as nrepl]
[clojure-mcp.utils.file :as file-utils]))

Expand Down Expand Up @@ -79,8 +80,10 @@
;; default to fetching from the nrepl
(when-let [exp (fetch-project-directory-exp nrepl-env-type)]
(try
(edn/read-string
(nrepl/tool-eval-code nrepl-client-map exp))
(let [result-value (->> (nrepl/eval-code nrepl-client-map exp :session-type :tools)
nrepl-core/combine-responses
:value)]
result-value)
(catch Exception e
(log/warn e "Failed to fetch project directory")
nil))))
Expand All @@ -93,10 +96,14 @@
"Fetches the project directory for the given nREPL client.
If project-dir is provided in opts, returns it directly.
Otherwise, evaluates environment-specific expression to get it."
[nrepl-client-map nrepl-env-type project-dir]
(if project-dir
(.getCanonicalPath (io/file project-dir))
(fetch-project-directory-helper nrepl-env-type nrepl-client-map)))
[nrepl-client-map nrepl-env-type project-dir-arg]
(if project-dir-arg
(.getCanonicalPath (io/file project-dir-arg))
(let [raw-result (fetch-project-directory-helper nrepl-env-type nrepl-client-map)]
;; nrepl sometimes returns strings with extra quotes and in a vector
(if (and (vector? raw-result) (= 1 (count raw-result)) (string? (first raw-result)))
(clojure.string/replace (first raw-result) #"^\"|\"$" "")
raw-result))))

;; High-level wrapper functions that execute the expressions

Expand All @@ -107,15 +114,15 @@
(log/debug "Initializing Clojure environment")
(when-let [init-exps (not-empty (initialize-environment-exp nrepl-env-type))]
(doseq [exp init-exps]
(nrepl/eval-code nrepl-client-map exp identity)))
(nrepl/eval-code nrepl-client-map exp)))
nrepl-client-map)

(defn load-repl-helpers
"Loads REPL helper functions appropriate for the environment."
[nrepl-client-map nrepl-env-type]
(when-let [helper-exps (not-empty (load-repl-helpers-exp nrepl-env-type))]
(doseq [exp helper-exps]
(nrepl/tool-eval-code nrepl-client-map exp)))
(nrepl/eval-code nrepl-client-map exp :session-type :tools)))
nrepl-client-map)

(defn detect-nrepl-env-type [nrepl-client-map]
Expand Down
31 changes: 13 additions & 18 deletions src/clojure_mcp/main_examples/shadow_main.clj
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ JavaScript interop is fully supported including `js/console.log`, `js/setTimeout

**IMPORTANT**: This repl is intended for CLOJURESCRIPT CODE only.")

(defn start-shadow-repl [nrepl-client-atom cljs-session {:keys [shadow-build shadow-watch]}]
(defn start-shadow-repl [nrepl-client-atom {:keys [shadow-build shadow-watch]}]
(let [start-code (format
;; TODO we need to check if its already running
;; here and only initialize if it isn't
Expand All @@ -39,36 +39,31 @@ JavaScript interop is fully supported including `js/console.log`, `js/setTimeout
"(do (shadow/repl %s) %s)")
(pr-str (keyword (name shadow-build)))
(pr-str (keyword (name shadow-build))))]
(nrepl/eval-code-msg
@nrepl-client-atom start-code {:session cljs-session}
(->> identity
(nrepl/out-err #(log/info %) #(log/info %))
(nrepl/value #(log/info %))
(nrepl/done (fn [_] (log/info "done")))
(nrepl/error (fn [args]
(log/info (pr-str args))
(log/info "ERROR in shadow start")))))
cljs-session))
(log/info "Starting Shadow CLJS...")
(try
(nrepl/eval-code @nrepl-client-atom start-code :session-type :shadow)
(log/info "Shadow CLJS started (or command sent)")
(catch Exception e
(log/error e "ERROR in shadow start")))
:shadow))

;; when having a completely different connection for cljs
(defn shadow-eval-tool-secondary-connection-tool [nrepl-client-atom {:keys [shadow-port _shadow-build _shadow-watch] :as config}]
(let [cljs-nrepl-client-map (core/create-additional-connection nrepl-client-atom {:port shadow-port})
cljs-nrepl-client-atom (atom cljs-nrepl-client-map)]
(start-shadow-repl
cljs-nrepl-client-atom
(nrepl/eval-session cljs-nrepl-client-map)
config)
(-> (eval-tool/eval-code cljs-nrepl-client-atom)
(-> (eval-tool/eval-code cljs-nrepl-client-atom {:session-type :shadow})
(assoc :name tool-name)
(assoc :description description))))

;; when sharing the clojure and cljs repl
(defn shadow-eval-tool [nrepl-client-atom {:keys [_shadow-build _shadow-watch] :as config}]
(let [cljs-session (nrepl/new-session @nrepl-client-atom)
_ (start-shadow-repl nrepl-client-atom cljs-session config)]
(-> (eval-tool/eval-code nrepl-client-atom {:nrepl-session cljs-session})
(assoc :name tool-name)
(assoc :description description))))
(start-shadow-repl nrepl-client-atom config)
(-> (eval-tool/eval-code nrepl-client-atom {:session-type :shadow})
(assoc :name tool-name)
(assoc :description description)))

;; So we can set up shadow two ways
;; 1. as a single repl connection using the shadow clojure connection for cloj eval
Expand Down
Loading