Skip to content

Commit 6e91284

Browse files
MCP tested in Claude
1 parent 19fbbe7 commit 6e91284

File tree

6 files changed

+68
-50
lines changed

6 files changed

+68
-50
lines changed

deps-lock.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626
{
2727
"lib": "io.github.slimslenderslacks/lsp4clj",
2828
"url": "https://github.com/slimslenderslacks/lsp4clj.git",
29-
"rev": "995abcdbfebfa7ef6550625fe1ef3ca2c7683292",
29+
"rev": "04390f9b1dcf0946dff335b48951617bc6bf6a9d",
3030
"git-dir": "https/github.com/slimslenderslacks/lsp4clj",
31-
"hash": "sha256-6RQ95oLS+ZX36x88yQY8v7QOhVtkqYVtMFMkW/ENlnM="
31+
"hash": "sha256-LtBVHKhOsn6L8arX0AWuNnsz5YWT1dypmZFxosJ6rzQ="
3232
}
3333
],
3434
"mvn-deps": [

deps.edn

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
org.clojure/core.async {:mvn/version "1.6.681"}
1414
org.babashka/http-client {:mvn/version "0.4.12"}
1515
com.taoensso/timbre {:mvn/version "5.2.1"}
16-
io.github.slimslenderslacks/lsp4clj {:git/sha "995abcdbfebfa7ef6550625fe1ef3ca2c7683292"}
16+
io.github.slimslenderslacks/lsp4clj {:git/sha "04390f9b1dcf0946dff335b48951617bc6bf6a9d"}
1717
funcool/promesa {:mvn/version "9.0.470"}}
1818
:aliases {:main {:main-opts ["-m" "docker.main"]}
1919
:build {:ns-default build

src/docker/main.clj

+2-5
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@
6666
[nil "--nostream" "disable streaming responses"
6767
:id :stream
6868
:assoc-fn (fn [m k _] (assoc m k false))]
69+
[nil "--register ref" "register a prompt REF"]
70+
[nil "--mcp" "use the mcp jsonrpc protocol"]
6971
[nil "--debug" "add debug logging"]
7072
[nil "--help" "print option summary"]])
7173

@@ -171,11 +173,6 @@
171173
(constantly
172174
(fn [method params]
173175
(jsonrpc.producer/publish-docker-notify producer method params))))
174-
(when (:prompts opts)
175-
(try
176-
(db/add opts)
177-
(catch Throwable t
178-
(logger/error t))))
179176
(let [finished @server-promise]
180177
{:result-code (if (= :done finished) 0 1)})))
181178
(fn []

src/jsonrpc.clj

+1
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@
163163
(when-let [line (read-line)]
164164
(println :mcp line)
165165
(recur))))))
166+
(write-message (:in mcp) (request "initialize" {} (constantly 1)))
166167
(write-message (:in mcp) (request "ping" {} (constantly 1)))
167168
(-> @mcp :err)
168169
(-> @mcp :out slurp)

src/jsonrpc/db.clj

+9-5
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,19 @@
66

77
(def db* (atom {}))
88

9-
(defn get-prompt-data [{:keys [prompts] :as opts}]
10-
(let [f (if (string? prompts) (git/prompt-file prompts) prompts)
9+
(defn get-prompt-data [{:keys [register] :as opts}]
10+
(let [f (git/prompt-file register)
1111
{:keys [messages metadata functions] :as entry} (prompts/get-prompts (assoc opts :prompts f))]
1212
entry))
1313

1414
(defn add [opts]
15-
(logger/info "adding prompts" (:prompts opts))
16-
(swap! db* update-in [:mcp.prompts/registry] (fnil assoc {}) (:prompts opts) (get-prompt-data opts)))
15+
(logger/info "adding prompts" (:register opts))
16+
(swap! db* update-in [:mcp.prompts/registry]
17+
(fnil assoc {})
18+
#_(:register opts)
19+
"explain_dockerfile"
20+
(get-prompt-data opts)))
1721

1822
(comment
19-
(add {:prompts "github:docker/labs-ai-tools-for-devs?path=prompts/examples/explain_dockerfile.md&ref=slim/server"}))
23+
(add {:register "github:docker/labs-ai-tools-for-devs?path=prompts/examples/explain_dockerfile.md&ref=slim/server"}))
2024

src/jsonrpc/server.clj

+53-37
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
[jsonrpc.logger :as logger]
1414
[jsonrpc.producer :as producer]
1515
[lsp4clj.coercer :as coercer]
16+
[lsp4clj.io-chan :as io-chan]
1617
[lsp4clj.io-server :refer [stdio-server]]
1718
[lsp4clj.server :as lsp.server]
1819
[promesa.core :as p]
@@ -45,7 +46,8 @@
4546
[level & args]
4647
;; NOTE: this does not do compile-time elision because the level isn't a constant.
4748
;; We don't really care because we always log all levels.
48-
(timbre/log! level :p args))
49+
(logger/info (str level (apply str args)))
50+
#_(timbre/log! level :p args))
4951

5052
(defn log! [level args fmeta]
5153
(timbre/log! level :p args {:?line (:line fmeta)
@@ -70,15 +72,12 @@
7072
;; merges client-info capabilities and client protocol-version
7173
(swap! db* merge params)
7274
{:protocol-version "2024-11-05"
73-
:capabilities {:logging {}
74-
:prompts {}
75-
:resources {}
76-
:tools {}
77-
:experimental {}}
75+
:capabilities {:prompts {}
76+
:tools {}}
7877
:server-info {:name "docker-mcp-server"
7978
:version "0.0.1"}})
8079

81-
(defmethod lsp.server/receive-notification "initialized" [_ _ _]
80+
(defmethod lsp.server/receive-notification "notifications/initialized" [_ _ _]
8281
(logger/info "Initialized!"))
8382

8483
; level is debug info notice warning error critical alert emergency
@@ -93,18 +92,21 @@
9392
:hasMore false}})
9493

9594
(defn entry->prompt-listing [k v m]
96-
{:description (-> v :metadata :description)
97-
:name (str k)
98-
:arguments []})
95+
{:name (str k)})
9996

100-
(defmethod lsp.server/receive-request "prompts/list" [_ {:keys [db*]} _]
97+
(defmethod lsp.server/receive-request "prompts/list" [_ {:keys [db*]} params]
10198
;; TODO might contain a cursor
102-
{:prompts (->> (:mcp.prompts/registry @db*)
103-
(mapcat (fn [[k v]] (map (partial entry->prompt-listing k v) (:messages v))))
104-
(into []))})
99+
(logger/info "prompts/list" params)
100+
(let [prompts
101+
{:prompts (->> (:mcp.prompts/registry @db*)
102+
(mapcat (fn [[k v]] (map (partial entry->prompt-listing k v) (:messages v))))
103+
(into []))}]
104+
(logger/info prompts)
105+
prompts))
105106

106107
(defmethod lsp.server/receive-request "prompts/get" [_ {:keys [db*]} {:keys [name]}]
107108
;; TODO resolve arguments
109+
(logger/info "prompts/get")
108110
(let [{:keys [messages metadata]} (-> @db* :mcp.prompts/registry (get name))]
109111
{:description (:description metadata)
110112
:messages (->> messages
@@ -128,6 +130,7 @@
128130

129131
(defmethod lsp.server/receive-request "tools/list" [_ {:keys [db*]} _]
130132
;; TODO cursors
133+
(logger/info "tools/list")
131134
{:tools (->> (:mcp.prompts/registry @db*)
132135
(vals)
133136
(mapcat :functions)
@@ -137,22 +140,25 @@
137140
(into []))})
138141

139142
(defmethod lsp.server/receive-request "tools/call" [_ {:keys [db*]} params]
140-
(eventually
141-
(lsp.server/discarding-stdout
142-
(let [tools (->> @db* :mcp.prompts/registry vals (mapcat :functions))
143-
tool-defaults {:functions tools
144-
:host-dir (-> @db* :host-dir)}]
145-
{:content
146-
(->>
147-
(tools/make-tool-calls
148-
0
149-
(partial tools/function-handler tool-defaults)
150-
[{:function (update params :arguments (fn [arguments] (json/generate-string arguments))) :id "1"}])
151-
(async/reduce conj [])
152-
(async/<!!)
153-
(map :content)
154-
(apply str))
155-
:is-error false}))))
143+
(logger/info "tools/call")
144+
(lsp.server/discarding-stdout
145+
(let [tools (->> @db* :mcp.prompts/registry vals (mapcat :functions))
146+
tool-defaults {:functions tools
147+
:host-dir (-> @db* :host-dir)}]
148+
(logger/info "calling tools " tool-defaults)
149+
(logger/info "with params" params)
150+
(let [content (->>
151+
(tools/make-tool-calls
152+
0
153+
(partial tools/function-handler tool-defaults)
154+
[{:function (update params :arguments (fn [arguments] (json/generate-string arguments))) :id "1"}])
155+
(async/reduce conj [])
156+
(async/<!!)
157+
(map :content)
158+
(apply str))]
159+
(logger/info "content " content)
160+
{:content [{:type "text" :text content}]
161+
:is-error false}))))
156162

157163
(defmethod lsp.server/receive-request "docker/prompts/register" [_ {:keys [db* id]} params]
158164
;; supports only git refs
@@ -264,20 +270,30 @@
264270
log-path (logger/setup timbre-logger)
265271
db* db/db*
266272
log-ch (async/chan (async/sliding-buffer 20))
267-
server (stdio-server {;:keyword-function identity
268-
:in (or (:in opts) System/in)
269-
:out System/out
270-
:log-ch log-ch
271-
:trace-ch log-ch
272-
:trace-level trace-level})
273+
server (stdio-server
274+
(merge
275+
{;:keyword-function identity
276+
:in (or (:in opts) System/in)
277+
:out System/out
278+
:log-ch log-ch
279+
:trace-ch log-ch
280+
:trace-level trace-level}
281+
(when (:mcp opts)
282+
{:in-chan-factory io-chan/mcp-input-stream->input-chan
283+
:out-chan-factory io-chan/mcp-output-stream->output-chan})))
273284
producer (McpProducer. server db*)
274285
components {:db* db*
275286
:logger timbre-logger
276287
:producer producer
277288
:server server}]
278289
(swap! db* merge {:log-path log-path} (dissoc opts :in))
279-
(logger/info "Starting server...")
290+
(when (:register opts)
291+
(try
292+
(db/add opts)
293+
(catch Throwable t
294+
(logger/error t))))
280295
(monitor-server-logs log-ch)
296+
(logger/info "Starting server...")
281297
[producer (lsp.server/start server components)])))
282298

283299
(comment

0 commit comments

Comments
 (0)