From 5ee41c983344ecc94838fca75036ed2120287145 Mon Sep 17 00:00:00 2001 From: Bruce Hauman Date: Wed, 19 Nov 2025 12:36:54 -0600 Subject: [PATCH 1/2] Migrate from clojure.tools.logging to Timbre Replace clojure.tools.logging with taoensso.timbre for pure Clojure logging. Changes: - Create src/clojure_mcp/logging.clj with centralized Timbre configuration - Add logging initialization to all main entry points (main.clj, sse_main.clj, figwheel_main.clj, shadow_main.clj) - Update all 26 source files to use [taoensso.timbre :as log] - Add test/clojure_mcp/test_helper.clj to suppress logging during tests - Update :test alias in deps.edn to use -M flag with test helper - Add helpful error message when using -X:test instead of -M:test - Remove clojure.tools.logging and logback dependencies - Add slf4j-nop to main deps to suppress Java library logging - Delete dev/logback.xml (no longer needed) Benefits: - Pure Clojure logging solution (no Java dependencies) - Simplified configuration - Better REPL integration - Clean test output with zero logging noise - Consistent logging across all entry points --- deps.edn | 51 +++++--------- dev/logback.xml | 57 ---------------- src/clojure_mcp/agent/general_agent.clj | 2 +- src/clojure_mcp/agent/langchain.clj | 2 +- .../agent/langchain/chat_listener.clj | 2 +- src/clojure_mcp/agent/langchain/model.clj | 2 +- src/clojure_mcp/config.clj | 2 +- src/clojure_mcp/core.clj | 2 +- src/clojure_mcp/dialects.clj | 2 +- src/clojure_mcp/logging.clj | 67 +++++++++++++++++++ src/clojure_mcp/main.clj | 6 ++ .../main_examples/figwheel_main.clj | 6 ++ src/clojure_mcp/main_examples/shadow_main.clj | 8 ++- src/clojure_mcp/nrepl.clj | 2 +- src/clojure_mcp/nrepl_launcher.clj | 2 +- src/clojure_mcp/sse_core.clj | 2 +- src/clojure_mcp/sse_main.clj | 8 ++- src/clojure_mcp/tool_system.clj | 2 +- .../tools/agent_tool_builder/core.clj | 2 +- .../tools/agent_tool_builder/file_changes.clj | 2 +- .../tools/agent_tool_builder/tool.clj | 2 +- src/clojure_mcp/tools/bash/core.clj | 2 +- src/clojure_mcp/tools/bash/tool.clj | 2 +- src/clojure_mcp/tools/eval/core.clj | 2 +- src/clojure_mcp/tools/figwheel/tool.clj | 2 +- src/clojure_mcp/tools/glob_files/core.clj | 2 +- src/clojure_mcp/tools/grep/core.clj | 2 +- src/clojure_mcp/tools/project/core.clj | 2 +- src/clojure_mcp/tools/scratch_pad/tool.clj | 2 +- .../tools/unified_clojure_edit/tool.clj | 2 +- .../unified_read_file/file_timestamps.clj | 2 +- .../tools/unified_read_file/tool.clj | 2 +- test/clojure_mcp/test_helper.clj | 17 +++++ 33 files changed, 153 insertions(+), 117 deletions(-) delete mode 100644 dev/logback.xml create mode 100644 src/clojure_mcp/logging.clj create mode 100644 test/clojure_mcp/test_helper.clj diff --git a/deps.edn b/deps.edn index 049fd962..f37c5a3a 100644 --- a/deps.edn +++ b/deps.edn @@ -2,7 +2,6 @@ :deps {org.clojure/clojure {:mvn/version "1.12.1"} org.clojure/data.json {:mvn/version "2.5.1"} nrepl/nrepl {:mvn/version "1.3.1"} - org.clojure/tools.logging {:mvn/version "1.3.0"} org.clojure/tools.cli {:mvn/version "1.1.230"} org.clj-commons/pretty {:mvn/version "3.6.7"} @@ -27,22 +26,22 @@ dev.langchain4j/langchain4j-anthropic {:mvn/version "1.8.0"} dev.langchain4j/langchain4j-google-ai-gemini {:mvn/version "1.8.0"} - ;; in order to use the stdio server you have to handle logging somehow - ;; org.slf4j/slf4j-nop {:mvn/version "2.0.16"} + com.taoensso/timbre {:mvn/version "6.8.0"} + + ;; Suppress logging from Java libraries (MCP SDK, LangChain4j, etc.) + org.slf4j/slf4j-nop {:mvn/version "2.0.16"} ;; native Java diff library io.github.java-diff-utils/java-diff-utils {:mvn/version "4.15"}} :aliases {:mcp - {:extra-deps {org.slf4j/slf4j-nop {:mvn/version "2.0.16"}} - :exec-fn clojure-mcp.main/start-mcp-server + {:exec-fn clojure-mcp.main/start-mcp-server ;; it needs an nrepl port to talk to :exec-args {:port 7888}} :mcp-sse - {:extra-deps {org.slf4j/slf4j-nop {:mvn/version "2.0.16"} ;; optional - jakarta.servlet/jakarta.servlet-api {:mvn/version "6.1.0"} + {:extra-deps {jakarta.servlet/jakarta.servlet-api {:mvn/version "6.1.0"} org.eclipse.jetty/jetty-server {:mvn/version "11.0.20"} org.eclipse.jetty/jetty-servlet {:mvn/version "11.0.20"}} :exec-fn clojure-mcp.sse-main/start-sse-mcp-server @@ -52,31 +51,26 @@ :mcp-sse-port 8078}} :mcp-figwheel - {:extra-deps {org.slf4j/slf4j-nop {:mvn/version "2.0.16"}} - :exec-fn clojure-mcp.main-examples.figwheel-main/start-mcp-server + {:exec-fn clojure-mcp.main-examples.figwheel-main/start-mcp-server :exec-args {:port 7888 :figwheel-build "dev"}} :mcp-shadow - {:extra-deps {org.slf4j/slf4j-nop {:mvn/version "2.0.16"}} - :exec-fn clojure-mcp.main-examples.shadow-main/start-mcp-server + {:exec-fn clojure-mcp.main-examples.shadow-main/start-mcp-server :exec-args {:port 7888 :shadow-build "app"}} ;; dual shadow and project nrepl setup :mcp-shadow-dual - {:extra-deps {org.slf4j/slf4j-nop {:mvn/version "2.0.16"}} - :extra-paths ["dev" "test"] + {:extra-paths ["dev" "test"] :exec-fn clojure-mcp.main-examples.shadow-main/start-mcp-server ;; it needs an nrepl port to talk to :exec-args {:port 7888 :shadow-build "app" :shadow-port 7889}} - ;; below are dev set ups that need a logback.xml file + ;; below are dev set ups :prompt-cli - {:extra-deps {org.slf4j/slf4j-nop {:mvn/version "2.0.16"}} - :main-opts ["-m" "clojure-mcp.prompt-cli"]} + {:main-opts ["-m" "clojure-mcp.prompt-cli"]} :dev-mcp - {:extra-deps {ch.qos.logback/logback-classic {:mvn/version "1.4.14"}} - :extra-paths ["dev" "test"] + {:extra-paths ["dev" "test"] :exec-fn clojure-mcp.main/start-mcp-server ;; it needs an nrepl port to talk to :exec-args {:port 7888 @@ -84,35 +78,28 @@ ;; :start-nrepl-cmd ["clojure" "-M:nrepl"] }} - ;; DEV setup needs logback.xml :dev-mcp-figwheel - {:extra-deps {ch.qos.logback/logback-classic {:mvn/version "1.4.14"}} - :extra-paths ["dev" "test"] + {:extra-paths ["dev" "test"] :exec-fn clojure-mcp.main-examples.figwheel-main/start-mcp-server ;; it needs an nrepl port to talk to :exec-args {:port 7888 :figwheel-build "dev"}} - ;; DEV setup needs logback.xml :dev-mcp-shadow - {:extra-deps {ch.qos.logback/logback-classic {:mvn/version "1.4.14"}} - :extra-paths ["dev" "test"] + {:extra-paths ["dev" "test"] :exec-fn clojure-mcp.main-examples.shadow-main/start-mcp-server ;; it needs an nrepl port to talk to :exec-args {:port 7888 :shadow-build "app"}} :dev-mcp-shadow-dual - {:extra-deps {ch.qos.logback/logback-classic {:mvn/version "1.4.14"}} - :extra-paths ["dev" "test"] + {:extra-paths ["dev" "test"] :exec-fn clojure-mcp.main-examples.shadow-main/start-mcp-server ;; it needs an nrepl port to talk to :exec-args {:port 7888 :shadow-build "app" :shadow-port 7889}} :nrepl {:extra-paths ["test" "dev"] - :extra-deps {ch.qos.logback/logback-classic {:mvn/version "1.4.14"}} :main-opts ["-m" "nrepl.cmdline" "--port" "7888"]} :dkr-nrepl {:extra-paths ["test" "dev"] - :extra-deps {ch.qos.logback/logback-classic {:mvn/version "1.4.14"}} :main-opts ["-m" "nrepl.cmdline" "--port" "7888" ;; for Docker "--bind" "0.0.0.0"]} @@ -120,18 +107,16 @@ ;; dev cycle :test {:extra-paths ["test" "dev"] - :exec-fn cognitect.test-runner/test - :extra-deps {ch.qos.logback/logback-classic {:mvn/version "1.4.14"} - org.clojure/test.check {:mvn/version "1.1.1"} + :exec-fn clojure-mcp.test-helper/run-tests-with-exec + :extra-deps {org.clojure/test.check {:mvn/version "1.1.1"} nrepl/nrepl {:mvn/version "1.3.1"} ;; Add nrepl server for testing io.github.cognitect-labs/test-runner {:git/tag "v0.5.1" :git/sha "dfb30dd"}} - :main-opts ["-m" "cognitect.test-runner"]} + :main-opts ["-e" "(require 'clojure-mcp.test-helper)" "-m" "cognitect.test-runner"]} :index {:exec-fn clojure-mcp.code-indexer/map-project :exec-args {} - :extra-deps {org.slf4j/slf4j-nop {:mvn/version "2.0.16"}} ;; Override with: clojure -X:index :dirs '["src" "lib"]' :include-tests true :out-file '"my-index.txt"' } diff --git a/dev/logback.xml b/dev/logback.xml deleted file mode 100644 index 25507c0a..00000000 --- a/dev/logback.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - logs/clojure-mcp.log - - - %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - logs/clojure-mcp-%d{yyyy-MM-dd}.log - - 1000 - - - - - - logs/file-timestamps.log - - - %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - logs/file-timestamps-%d{yyyy-MM-dd}.log - 30 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/clojure_mcp/agent/general_agent.clj b/src/clojure_mcp/agent/general_agent.clj index aa124d1a..5f8e091a 100644 --- a/src/clojure_mcp/agent/general_agent.clj +++ b/src/clojure_mcp/agent/general_agent.clj @@ -2,7 +2,7 @@ "A generalized agent library that can be parameterized with system prompts, context, tools, memory, and models." (:require [clojure.string :as string] - [clojure.tools.logging :as log] + [taoensso.timbre :as log] [clojure.java.io :as io] [clojure-mcp.agent.langchain :as chain] [clojure-mcp.tools.project.core :as project-core] diff --git a/src/clojure_mcp/agent/langchain.clj b/src/clojure_mcp/agent/langchain.clj index 123705bd..0eac76d3 100644 --- a/src/clojure_mcp/agent/langchain.clj +++ b/src/clojure_mcp/agent/langchain.clj @@ -2,7 +2,7 @@ (:require [clojure.data.json :as json] [clojure-mcp.agent.langchain.schema :as schema] - [clojure.tools.logging :as log] + [taoensso.timbre :as log] [clojure.string :as string] [clojure.pprint]) (:import diff --git a/src/clojure_mcp/agent/langchain/chat_listener.clj b/src/clojure_mcp/agent/langchain/chat_listener.clj index c7796679..5c901df9 100644 --- a/src/clojure_mcp/agent/langchain/chat_listener.clj +++ b/src/clojure_mcp/agent/langchain/chat_listener.clj @@ -2,7 +2,7 @@ "Creates ChatModelListener instances from Clojure functions that work with EDN data" (:require [clojure-mcp.agent.langchain.message-conv :as msg-conv] - [clojure.tools.logging :as log]) + [taoensso.timbre :as log]) (:import [dev.langchain4j.model.chat.listener ChatModelListener diff --git a/src/clojure_mcp/agent/langchain/model.clj b/src/clojure_mcp/agent/langchain/model.clj index 86a7c417..35c9dbdb 100644 --- a/src/clojure_mcp/agent/langchain/model.clj +++ b/src/clojure_mcp/agent/langchain/model.clj @@ -2,7 +2,7 @@ (:require [clojure-mcp.config.schema :as schema] [clojure-mcp.config :as config] - [clojure.tools.logging :as log]) + [taoensso.timbre :as log]) (:import [dev.langchain4j.model.anthropic AnthropicChatModel diff --git a/src/clojure_mcp/config.clj b/src/clojure_mcp/config.clj index 401184f3..979d9530 100644 --- a/src/clojure_mcp/config.clj +++ b/src/clojure_mcp/config.clj @@ -4,7 +4,7 @@ [clojure-mcp.config.schema :as schema] [clojure-mcp.utils.file :as file-utils] [clojure.edn :as edn] - [clojure.tools.logging :as log])) + [taoensso.timbre :as log])) (defn- relative-to [dir path] (try diff --git a/src/clojure_mcp/core.clj b/src/clojure_mcp/core.clj index b5bbf9ed..3ed41fab 100644 --- a/src/clojure_mcp/core.clj +++ b/src/clojure_mcp/core.clj @@ -2,7 +2,7 @@ (:require [clojure.data.json :as json] [clojure.java.io :as io] [clojure.spec.alpha :as s] - [clojure.tools.logging :as log] + [taoensso.timbre :as log] [clojure-mcp.nrepl :as nrepl] [clojure-mcp.config :as config] [clojure-mcp.dialects :as dialects] diff --git a/src/clojure_mcp/dialects.clj b/src/clojure_mcp/dialects.clj index d6dfb9a4..7f5505e1 100644 --- a/src/clojure_mcp/dialects.clj +++ b/src/clojure_mcp/dialects.clj @@ -5,7 +5,7 @@ and initialization sequences specific to each dialect." (:require [clojure.edn :as edn] [clojure.java.io :as io] - [clojure.tools.logging :as log] + [taoensso.timbre :as log] [clojure-mcp.nrepl :as nrepl] [clojure-mcp.utils.file :as file-utils])) diff --git a/src/clojure_mcp/logging.clj b/src/clojure_mcp/logging.clj new file mode 100644 index 00000000..70e2e4c5 --- /dev/null +++ b/src/clojure_mcp/logging.clj @@ -0,0 +1,67 @@ +(ns clojure-mcp.logging + "Centralized logging configuration using Timbre. + + This namespace provides functions to configure Timbre-based logging + for the Clojure MCP server with support for both development and + production modes." + (:require [taoensso.timbre :as timbre])) + +(defn configure-logging! + "Configure Timbre logging with the given options. + + Options: + - :log-file Path to log file (default: 'logs/clojure-mcp.log') + - :enable-logging? Whether to enable logging (default: true) + - :log-level Minimum log level (default: :debug) + Valid values: :trace :debug :info :warn :error :fatal :report + + Examples: + + Development mode with full logging: + (configure-logging! {:log-file \"logs/clojure-mcp.log\" + :enable-logging? true + :log-level :debug}) + + Production mode with logging suppressed: + (configure-logging! {:enable-logging? false})" + [{:keys [log-file enable-logging? log-level] + :or {log-file "logs/clojure-mcp.log" + enable-logging? true + log-level :debug}}] + (timbre/set-config! + {:appenders {:spit (assoc + (timbre/spit-appender {:fname log-file}) + :enabled? enable-logging? + :min-level (or log-level :report) + :ns-filter (if enable-logging? + {:allow #{"clojure-mcp.*"}} + {:deny #{"*"}}))}})) + +(defn configure-dev-logging! + "Configure logging for development mode with debug level. + Convenience function that enables full debug logging to logs/clojure-mcp.log" + [] + (configure-logging! {:log-file "logs/clojure-mcp.log" + :enable-logging? true + :log-level :debug})) + +(defn configure-prod-logging! + "Configure logging for production mode with all logging suppressed. + Convenience function that disables all logging." + [] + (configure-logging! {:enable-logging? false})) + +(defn configure-test-logging! + "Configure logging for test mode with all logging suppressed. + Convenience function that disables all logging during tests." + [] + (configure-logging! {:enable-logging? false})) + +(defn suppress-logging-for-tests! + "Automatically suppress logging if running in test mode. + Checks for CLOJURE_MCP_TEST environment variable or + clojure.mcp.test system property." + [] + (when (or (System/getenv "CLOJURE_MCP_TEST") + (System/getProperty "clojure.mcp.test")) + (configure-test-logging!))) diff --git a/src/clojure_mcp/main.clj b/src/clojure_mcp/main.clj index d6a8bbc8..feb333fa 100644 --- a/src/clojure_mcp/main.clj +++ b/src/clojure_mcp/main.clj @@ -1,5 +1,6 @@ (ns clojure-mcp.main (:require [clojure-mcp.core :as core] + [clojure-mcp.logging :as logging] [clojure-mcp.prompts :as prompts] [clojure-mcp.resources :as resources] [clojure-mcp.tools :as tools])) @@ -33,6 +34,11 @@ (tools/build-all-tools nrepl-client-atom)) (defn start-mcp-server [opts] + ;; Configure logging before starting the server + (logging/configure-logging! + {:log-file (get opts :log-file "logs/clojure-mcp.log") + :enable-logging? (get opts :enable-logging? true) + :log-level (get opts :log-level :debug)}) (core/build-and-start-mcp-server opts {:make-tools-fn make-tools diff --git a/src/clojure_mcp/main_examples/figwheel_main.clj b/src/clojure_mcp/main_examples/figwheel_main.clj index 0ee284b6..1992d1a8 100644 --- a/src/clojure_mcp/main_examples/figwheel_main.clj +++ b/src/clojure_mcp/main_examples/figwheel_main.clj @@ -10,6 +10,7 @@ See the comments below for the required deps.edn configuration." (:require [clojure-mcp.core :as core] + [clojure-mcp.logging :as logging] [clojure-mcp.main :as main] [clojure-mcp.tools.figwheel.tool :as figwheel-tool])) @@ -36,6 +37,11 @@ (figwheel-tool/figwheel-eval nrepl-client-atom {:figwheel-build (or figwheel-build "dev")}))) (defn start-mcp-server [opts] + ;; Configure logging before starting the server + (logging/configure-logging! + {:log-file (get opts :log-file "logs/clojure-mcp.log") + :enable-logging? (get opts :enable-logging? true) + :log-level (get opts :log-level :debug)}) (core/build-and-start-mcp-server opts {:make-tools-fn (fn [nrepl-client-atom working-directory] diff --git a/src/clojure_mcp/main_examples/shadow_main.clj b/src/clojure_mcp/main_examples/shadow_main.clj index 16900dd8..ab2c49b1 100644 --- a/src/clojure_mcp/main_examples/shadow_main.clj +++ b/src/clojure_mcp/main_examples/shadow_main.clj @@ -11,8 +11,9 @@ - Dual connection: Connect to a separate Shadow CLJS nREPL server" (:require [clojure-mcp.core :as core] + [clojure-mcp.logging :as logging] [clojure-mcp.nrepl :as nrepl] - [clojure.tools.logging :as log] + [taoensso.timbre :as log] [clojure-mcp.main :as main] [clojure-mcp.tools.eval.tool :as eval-tool])) @@ -82,6 +83,11 @@ JavaScript interop is fully supported including `js/console.log`, `js/setTimeout (shadow-eval-tool nrepl-client-atom config)))) (defn start-mcp-server [opts] + ;; Configure logging before starting the server + (logging/configure-logging! + {:log-file (get opts :log-file "logs/clojure-mcp.log") + :enable-logging? (get opts :enable-logging? true) + :log-level (get opts :log-level :debug)}) (core/build-and-start-mcp-server opts {:make-tools-fn (fn [nrepl-client-atom working-directory] diff --git a/src/clojure_mcp/nrepl.clj b/src/clojure_mcp/nrepl.clj index b68dc271..e151275d 100644 --- a/src/clojure_mcp/nrepl.clj +++ b/src/clojure_mcp/nrepl.clj @@ -4,7 +4,7 @@ [nrepl.core :as nrepl] [nrepl.misc :as nrepl.misc] [nrepl.transport] - [clojure.tools.logging :as log] + [taoensso.timbre :as log] [clojure.edn])) ;; callback system diff --git a/src/clojure_mcp/nrepl_launcher.clj b/src/clojure_mcp/nrepl_launcher.clj index 66ca461b..bba555c8 100644 --- a/src/clojure_mcp/nrepl_launcher.clj +++ b/src/clojure_mcp/nrepl_launcher.clj @@ -5,7 +5,7 @@ [clojure.java.io :as io] [clojure.java.process :as process] [clojure.string :as str] - [clojure.tools.logging :as log]) + [taoensso.timbre :as log]) (:import [java.io BufferedReader] [java.util.concurrent TimeUnit])) diff --git a/src/clojure_mcp/sse_core.clj b/src/clojure_mcp/sse_core.clj index 8548d2c0..06f0838f 100644 --- a/src/clojure_mcp/sse_core.clj +++ b/src/clojure_mcp/sse_core.clj @@ -3,7 +3,7 @@ [clojure-mcp.core :as core] [clojure-mcp.config :as config] [clojure-mcp.nrepl-launcher :as nrepl-launcher] - [clojure.tools.logging :as log]) + [taoensso.timbre :as log]) (:import [io.modelcontextprotocol.server.transport HttpServletSseServerTransportProvider] diff --git a/src/clojure_mcp/sse_main.clj b/src/clojure_mcp/sse_main.clj index 9a030476..3996bffe 100644 --- a/src/clojure_mcp/sse_main.clj +++ b/src/clojure_mcp/sse_main.clj @@ -1,14 +1,20 @@ (ns clojure-mcp.sse-main "Example of a custom MCP server using Server-Sent Events (SSE) transport. - + This demonstrates reusing the standard tools, prompts, and resources from main.clj while using a different transport mechanism (SSE instead of stdio). The SSE transport allows web-based clients to connect." (:require + [clojure-mcp.logging :as logging] [clojure-mcp.main :as main] [clojure-mcp.sse-core :as sse-core])) (defn start-sse-mcp-server [opts] + ;; Configure logging before starting the server + (logging/configure-logging! + {:log-file (get opts :log-file "logs/clojure-mcp.log") + :enable-logging? (get opts :enable-logging? true) + :log-level (get opts :log-level :debug)}) (sse-core/build-and-start-mcp-server opts {:make-tools-fn main/make-tools diff --git a/src/clojure_mcp/tool_system.clj b/src/clojure_mcp/tool_system.clj index 8f1468de..d383861f 100644 --- a/src/clojure_mcp/tool_system.clj +++ b/src/clojure_mcp/tool_system.clj @@ -5,7 +5,7 @@ (:require [clojure.string :as string] [clojure.walk :as walk] - [clojure.tools.logging :as log])) + [taoensso.timbre :as log])) ;; Core multimethods for tool behavior diff --git a/src/clojure_mcp/tools/agent_tool_builder/core.clj b/src/clojure_mcp/tools/agent_tool_builder/core.clj index f27c7fde..7e86aa2d 100644 --- a/src/clojure_mcp/tools/agent_tool_builder/core.clj +++ b/src/clojure_mcp/tools/agent_tool_builder/core.clj @@ -8,7 +8,7 @@ [clojure-mcp.tools :as tools] [clojure-mcp.tools.agent-tool-builder.file-changes :as file-changes] [clojure.string :as str] - [clojure.tools.logging :as log])) + [taoensso.timbre :as log])) (defn build-agent-from-config "Builds an agent from a configuration map. diff --git a/src/clojure_mcp/tools/agent_tool_builder/file_changes.clj b/src/clojure_mcp/tools/agent_tool_builder/file_changes.clj index dd95118a..846db971 100644 --- a/src/clojure_mcp/tools/agent_tool_builder/file_changes.clj +++ b/src/clojure_mcp/tools/agent_tool_builder/file_changes.clj @@ -5,7 +5,7 @@ [clojure-mcp.utils.diff :as diff-utils] [clojure-mcp.utils.file :as file-utils] [clojure.string :as str] - [clojure.tools.logging :as log])) + [taoensso.timbre :as log])) (defn reset-changed-files! "Reset the changed-files map to empty. diff --git a/src/clojure_mcp/tools/agent_tool_builder/tool.clj b/src/clojure_mcp/tools/agent_tool_builder/tool.clj index 8faaf4b6..dfe2189a 100644 --- a/src/clojure_mcp/tools/agent_tool_builder/tool.clj +++ b/src/clojure_mcp/tools/agent_tool_builder/tool.clj @@ -5,7 +5,7 @@ [clojure-mcp.tool-system :as tool-system] [clojure-mcp.tools.agent-tool-builder.core :as core] [clojure-mcp.tools.agent-tool-builder.default-agents :as default-agents] - [clojure.tools.logging :as log])) + [taoensso.timbre :as log])) (defn create-single-agent-tool "Creates a tool registration for a single agent configuration. diff --git a/src/clojure_mcp/tools/bash/core.clj b/src/clojure_mcp/tools/bash/core.clj index 2bb1a863..3cf5d751 100644 --- a/src/clojure_mcp/tools/bash/core.clj +++ b/src/clojure_mcp/tools/bash/core.clj @@ -4,7 +4,7 @@ [clojure.java.io :as io] [clojure.string :as str] [clojure.edn :as edn] - [clojure.tools.logging :as log] + [taoensso.timbre :as log] [clojure-mcp.nrepl :as nrepl] [clojure-mcp.tools.eval.core :as eval-core]) (:import diff --git a/src/clojure_mcp/tools/bash/tool.clj b/src/clojure_mcp/tools/bash/tool.clj index c2eb5c1c..39e3fea1 100644 --- a/src/clojure_mcp/tools/bash/tool.clj +++ b/src/clojure_mcp/tools/bash/tool.clj @@ -6,7 +6,7 @@ [clojure-mcp.utils.valid-paths :as valid-paths] [clojure-mcp.tools.bash.core :as core] [clojure-mcp.nrepl :as nrepl] - [clojure.tools.logging :as log] + [taoensso.timbre :as log] [clojure.java.io :as io] [clojure.string :as str])) diff --git a/src/clojure_mcp/tools/eval/core.clj b/src/clojure_mcp/tools/eval/core.clj index de351a14..c171ac23 100644 --- a/src/clojure_mcp/tools/eval/core.clj +++ b/src/clojure_mcp/tools/eval/core.clj @@ -6,7 +6,7 @@ [clojure-mcp.delimiter :as delimiter] [clojure-mcp.sexp.paren-utils :as paren-utils] [clojure.string :as string] - [clojure.tools.logging :as log])) + [taoensso.timbre :as log])) ;; Eval results formatting ;; The goal is to make it clear for the LLM to understand diff --git a/src/clojure_mcp/tools/figwheel/tool.clj b/src/clojure_mcp/tools/figwheel/tool.clj index 2029c4c1..10f9bbed 100644 --- a/src/clojure_mcp/tools/figwheel/tool.clj +++ b/src/clojure_mcp/tools/figwheel/tool.clj @@ -1,6 +1,6 @@ (ns clojure-mcp.tools.figwheel.tool (:require - [clojure.tools.logging :as log] + [taoensso.timbre :as log] [clojure.string :as string] [clojure-mcp.nrepl :as nrepl] [clojure-mcp.tool-system :as tool-system] diff --git a/src/clojure_mcp/tools/glob_files/core.clj b/src/clojure_mcp/tools/glob_files/core.clj index 1653c9a6..aaa3d200 100644 --- a/src/clojure_mcp/tools/glob_files/core.clj +++ b/src/clojure_mcp/tools/glob_files/core.clj @@ -4,7 +4,7 @@ (:require [clojure.java.io :as io] [clojure.string :as str] [clojure.java.shell :as shell] - [clojure.tools.logging :as log]) + [taoensso.timbre :as log]) (:import (java.nio.file FileVisitResult FileSystems Files Paths SimpleFileVisitor))) diff --git a/src/clojure_mcp/tools/grep/core.clj b/src/clojure_mcp/tools/grep/core.clj index a7529b26..aaf8c8f2 100644 --- a/src/clojure_mcp/tools/grep/core.clj +++ b/src/clojure_mcp/tools/grep/core.clj @@ -5,7 +5,7 @@ [clojure.string :as str] [clojure.java.shell :as shell] [clojure.java.io :as io] - [clojure.tools.logging :as log])) + [taoensso.timbre :as log])) ;; Cache tool availability to avoid repeated shell calls (def ^:private tool-availability (atom {})) diff --git a/src/clojure_mcp/tools/project/core.clj b/src/clojure_mcp/tools/project/core.clj index b4f89553..8b044f37 100644 --- a/src/clojure_mcp/tools/project/core.clj +++ b/src/clojure_mcp/tools/project/core.clj @@ -9,7 +9,7 @@ [clojure-mcp.tools.glob-files.core :as glob] [clojure-mcp.utils.valid-paths :as vpaths] [clojure-mcp.utils.file :as file-utils] - [clojure.tools.logging :as log]) + [taoensso.timbre :as log]) (:import [java.io File] [java.nio.file Paths])) diff --git a/src/clojure_mcp/tools/scratch_pad/tool.clj b/src/clojure_mcp/tools/scratch_pad/tool.clj index 73d6e894..2991dd7a 100644 --- a/src/clojure_mcp/tools/scratch_pad/tool.clj +++ b/src/clojure_mcp/tools/scratch_pad/tool.clj @@ -4,7 +4,7 @@ [clojure-mcp.tool-system :as tool-system] [clojure-mcp.tools.scratch-pad.core :as core] [clojure-mcp.utils.file :as file-utils] - [clojure.tools.logging :as log] + [taoensso.timbre :as log] [clojure.walk :as walk] [clojure.pprint :as pprint] [clojure.java.io :as io] diff --git a/src/clojure_mcp/tools/unified_clojure_edit/tool.clj b/src/clojure_mcp/tools/unified_clojure_edit/tool.clj index 861a6e79..48226313 100644 --- a/src/clojure_mcp/tools/unified_clojure_edit/tool.clj +++ b/src/clojure_mcp/tools/unified_clojure_edit/tool.clj @@ -6,7 +6,7 @@ [clojure-mcp.tools.form-edit.pipeline :as pipeline] [clojure-mcp.delimiter :as delimiter] [clojure-mcp.utils.valid-paths :as valid-paths] - [clojure.tools.logging :as log] + [taoensso.timbre :as log] [clojure.string :as str])) ;; Tool name and description diff --git a/src/clojure_mcp/tools/unified_read_file/file_timestamps.clj b/src/clojure_mcp/tools/unified_read_file/file_timestamps.clj index 91344747..eb61d5f1 100644 --- a/src/clojure_mcp/tools/unified_read_file/file_timestamps.clj +++ b/src/clojure_mcp/tools/unified_read_file/file_timestamps.clj @@ -3,7 +3,7 @@ (:require [clojure-mcp.tools.unified-read-file.core :as core] [clojure.java.io :as io] - [clojure.tools.logging :as log])) + [taoensso.timbre :as log])) (defn get-file-timestamps "Gets the current file timestamps map from the nrepl-client. diff --git a/src/clojure_mcp/tools/unified_read_file/tool.clj b/src/clojure_mcp/tools/unified_read_file/tool.clj index e5d861f2..00c778bb 100644 --- a/src/clojure_mcp/tools/unified_read_file/tool.clj +++ b/src/clojure_mcp/tools/unified_read_file/tool.clj @@ -10,7 +10,7 @@ [clojure-mcp.tools.unified-read-file.core :as core] [clojure-mcp.file-content :as file-content] [clojure-mcp.config :as config] - [clojure.tools.logging :as log] + [taoensso.timbre :as log] [clojure.string :as str])) ;; Factory function to create the tool configuration diff --git a/test/clojure_mcp/test_helper.clj b/test/clojure_mcp/test_helper.clj new file mode 100644 index 00000000..bb67172f --- /dev/null +++ b/test/clojure_mcp/test_helper.clj @@ -0,0 +1,17 @@ +(ns clojure-mcp.test-helper + "Test helper namespace that configures the test environment. + This namespace is loaded automatically before tests run." + (:require [clojure-mcp.logging :as logging])) + +;; Suppress logging during tests +(logging/configure-test-logging!) + +(defn run-tests-with-exec + "Called when using -X:test (incorrect usage). + Prints helpful message directing user to use -M:test instead." + [& _args] + (println "\n⚠️ Error: The :test alias requires the -M flag, not -X") + (println "\nPlease run tests using:") + (println " clojure -M:test") + (println "\nThe -M flag is required to suppress logging during test runs.") + (System/exit 1)) From 969d27334da5573ba31bc25aea1f29912f1a948f Mon Sep 17 00:00:00 2001 From: Bruce Hauman Date: Wed, 19 Nov 2025 20:39:47 -0600 Subject: [PATCH 2/2] chagne logging defaults --- src/clojure_mcp/logging.clj | 22 +++++++++++-------- src/clojure_mcp/main.clj | 6 ++--- .../main_examples/figwheel_main.clj | 6 ++--- src/clojure_mcp/main_examples/shadow_main.clj | 6 ++--- src/clojure_mcp/sse_main.clj | 6 ++--- 5 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/clojure_mcp/logging.clj b/src/clojure_mcp/logging.clj index 70e2e4c5..f4abf421 100644 --- a/src/clojure_mcp/logging.clj +++ b/src/clojure_mcp/logging.clj @@ -6,6 +6,8 @@ production modes." (:require [taoensso.timbre :as timbre])) +(def default-log-file ".clojure-mcp/clojure-mcp.log") + (defn configure-logging! "Configure Timbre logging with the given options. @@ -25,17 +27,19 @@ Production mode with logging suppressed: (configure-logging! {:enable-logging? false})" [{:keys [log-file enable-logging? log-level] - :or {log-file "logs/clojure-mcp.log" - enable-logging? true + :or {log-file default-log-file + enable-logging? false log-level :debug}}] (timbre/set-config! - {:appenders {:spit (assoc - (timbre/spit-appender {:fname log-file}) - :enabled? enable-logging? - :min-level (or log-level :report) - :ns-filter (if enable-logging? - {:allow #{"clojure-mcp.*"}} - {:deny #{"*"}}))}})) + {:appenders (if enable-logging? + {:spit (assoc + (timbre/spit-appender {:fname log-file}) + :enabled? enable-logging? + :min-level (or log-level :report) + :ns-filter (if enable-logging? + {:allow #{"clojure-mcp.*"}} + {:deny #{"*"}}))} + {})})) (defn configure-dev-logging! "Configure logging for development mode with debug level. diff --git a/src/clojure_mcp/main.clj b/src/clojure_mcp/main.clj index feb333fa..1c4b7ad5 100644 --- a/src/clojure_mcp/main.clj +++ b/src/clojure_mcp/main.clj @@ -36,11 +36,11 @@ (defn start-mcp-server [opts] ;; Configure logging before starting the server (logging/configure-logging! - {:log-file (get opts :log-file "logs/clojure-mcp.log") - :enable-logging? (get opts :enable-logging? true) + {:log-file (get opts :log-file logging/default-log-file) + :enable-logging? (get opts :enable-logging? false) :log-level (get opts :log-level :debug)}) (core/build-and-start-mcp-server - opts + (dissoc opts :log-file :log-level :enable-logging?) {:make-tools-fn make-tools :make-prompts-fn make-prompts :make-resources-fn make-resources})) diff --git a/src/clojure_mcp/main_examples/figwheel_main.clj b/src/clojure_mcp/main_examples/figwheel_main.clj index 1992d1a8..26af2211 100644 --- a/src/clojure_mcp/main_examples/figwheel_main.clj +++ b/src/clojure_mcp/main_examples/figwheel_main.clj @@ -39,11 +39,11 @@ (defn start-mcp-server [opts] ;; Configure logging before starting the server (logging/configure-logging! - {:log-file (get opts :log-file "logs/clojure-mcp.log") - :enable-logging? (get opts :enable-logging? true) + {:log-file (get opts :log-file logging/default-log-file) + :enable-logging? (get opts :enable-logging? false) :log-level (get opts :log-level :debug)}) (core/build-and-start-mcp-server - opts + (dissoc opts :log-file :log-level :enable-logging?) {:make-tools-fn (fn [nrepl-client-atom working-directory] (make-tools nrepl-client-atom working-directory opts)) :make-prompts-fn main/make-prompts diff --git a/src/clojure_mcp/main_examples/shadow_main.clj b/src/clojure_mcp/main_examples/shadow_main.clj index ab2c49b1..f0f30b9e 100644 --- a/src/clojure_mcp/main_examples/shadow_main.clj +++ b/src/clojure_mcp/main_examples/shadow_main.clj @@ -85,11 +85,11 @@ JavaScript interop is fully supported including `js/console.log`, `js/setTimeout (defn start-mcp-server [opts] ;; Configure logging before starting the server (logging/configure-logging! - {:log-file (get opts :log-file "logs/clojure-mcp.log") - :enable-logging? (get opts :enable-logging? true) + {:log-file (get opts :log-file logging/default-log-file) + :enable-logging? (get opts :enable-logging? false) :log-level (get opts :log-level :debug)}) (core/build-and-start-mcp-server - opts + (dissoc opts :log-file :log-level :enable-logging?) {:make-tools-fn (fn [nrepl-client-atom working-directory] (make-tools nrepl-client-atom working-directory opts)) :make-prompts-fn main/make-prompts diff --git a/src/clojure_mcp/sse_main.clj b/src/clojure_mcp/sse_main.clj index 3996bffe..e3cff537 100644 --- a/src/clojure_mcp/sse_main.clj +++ b/src/clojure_mcp/sse_main.clj @@ -12,11 +12,11 @@ (defn start-sse-mcp-server [opts] ;; Configure logging before starting the server (logging/configure-logging! - {:log-file (get opts :log-file "logs/clojure-mcp.log") - :enable-logging? (get opts :enable-logging? true) + {:log-file (get opts :log-file logging/default-log-file) + :enable-logging? (get opts :enable-logging? false) :log-level (get opts :log-level :debug)}) (sse-core/build-and-start-mcp-server - opts + (dissoc opts :log-file :log-level :enable-logging?) {:make-tools-fn main/make-tools :make-prompts-fn main/make-prompts :make-resources-fn main/make-resources}))