listMcpTools — inventory without connecting
Clojure (JVM) + cljgo · package net.clojars.muthuishere/toolnexus · SPEC §2 · clojure/src/toolnexus/mcp.cljc
List what each configured server would expose, plus per-server status, without wiring it into a toolkit.
What to use instead
Section titled “What to use instead”Split the question in two, because the two halves have genuinely different costs.
Is the config sane? That is offline, free and instant.
toolnexus.mcp/parse-config hands back one map per server with
the kind inferred, the enablement resolved and the timeout defaulted — no process, no socket. A
misspelled server block, a server with neither command nor url, an entry you thought was enabled
and is not: all visible here.
(require '[toolnexus.mcp :as mcp])
(def servers (mcp/parse-config (slurp "mcp.json")))
(mapv (juxt :name :kind :enabled :timeout) servers);; => [["docs" "remote" true 5000] ["fs" "local" true 30000] ["old" "unknown" false 30000]]
;; a "kind" of "unknown" means neither command nor url — that server can only fail(filterv #(= "unknown" (:kind %)) servers)What tools does it actually expose? That question cannot be answered without talking to the
server — MCP has no static manifest, tools/list is a live RPC. So the honest answer is
from-config, which connects and hands you the inventory and the
per-server outcome in one value:
(require '[toolnexus.mcp :as mcp])
(def res (mcp/from-config (slurp "mcp.json")))
(try ;; the inventory (mapv (juxt :server :remote-name :name) (:tools res)) ;; => [["docs" "search" "docs_search"] ["fs" "read file" "fs_read_file"]]
;; the per-server outcome, in the same value (:statuses res) ;; => {"docs" "connected" "fs" "connected" "old" "disabled"} (:errors res) ;; => {} when everything worked
;; a doctor command is this and nothing more (when-let [bad (seq (:errors res))] (doseq [[server message] bad] (println "FAILED" server "-" message)))
(finally (mcp/disconnect-all res)))If what you actually want is the name a server’s tool will get — to write an allowlist, or to check a collision against a builtin — you can compute that without connecting, because SPEC §0.2 naming is pure:
(require '[toolnexus.mcp :as mcp])
(mcp/mcp-tool-name "remote api" "zebra note") ;; => "remote_api_zebra_note"(mcp/mcp-tool-name "remote api" "alpha/stats") ;; => "remote_api_alpha_stats"The one thing to avoid is treating a connect-and-disconnect as a cheap probe. Connecting spawns a
child process or opens an HTTP session per server, serially; always pair it with
mcp/disconnect-all in a finally, and keep the :timeout short if the probe is on a startup
path.
See also
Section titled “See also”toolnexus.mcp/parse-config— the offline half of the questiontoolnexus.mcp/from-config— the connected half::toolsplus:statusesand:errors- Bounding a load —
:timeoutper server, and teardown toolnexus.core/build— the same status map, across every source