Skip to content

Commit ac91411

Browse files
script resources collection
1 parent fa9668d commit ac91411

File tree

13 files changed

+263
-95
lines changed

13 files changed

+263
-95
lines changed

docs/content/tools/docs/mcp-server.md

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
title: running the mcp/docker image
3+
---
4+
5+
## Logging
6+
7+
When the Docker MCP server is [enabled for claude desktop](claude-desktop), the `mcp/docker` image will be running
8+
locally. You can tail the server logs by running the following command:
9+
10+
```sh
11+
docker run --rm -t --init \
12+
-v docker-prompts:/prompts alpine:latest \
13+
tail -f /prompts/log/docker-mcp-server.out
14+
```
15+

prompts/bootstrap.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
---
2+
name: register a new skill
23
tools:
34
- name: register-skill
45
description: Register a new skill

prompts/examples/qrencode.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ parameter-values:
1313

1414
# prompt
1515

16-
Generate a QR code for the content '{{content}}'.
17-
Save the generated image to `/thread/resources/qrcode.png`.
16+
Generate a QR code for the content '{{content}}' and write the output to the file `/thread/resources/qrcode.png`.
1817
If the command fails, read the man page and try again.
1918
If successful, output the path to the generated image in markdown syntax.
2019

src/docker.clj

+44-26
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
[babashka.fs :as fs]
55
[cheshire.core :as json]
66
[clojure.core.async :as async]
7-
[clojure.java.io :as io]
87
[clojure.pprint :refer [pprint]]
98
[clojure.string :as string]
109
[creds]
@@ -109,7 +108,7 @@
109108
;; Tty wraps the process in a pseudo terminal
110109
{:StdinOnce true
111110
:OpenStdin true}
112-
(defn create-container [{:keys [image entrypoint workdir command host-dir env thread-id opts mounts volumes] :or {opts {:Tty true}} :as m}]
111+
(defn create-container [{:keys [image entrypoint workdir command host-dir env thread-id opts mounts volumes] :or {opts {:Tty true}}}]
113112
#_(jsonrpc/notify :message {:content (str m)})
114113
(let [payload (json/generate-string
115114
(merge
@@ -167,6 +166,8 @@
167166
{:raw-args ["--unix-socket" "/var/run/docker.sock"]
168167
:throw false}))
169168

169+
;; container was created with a Terminal (output is not-multiplexed)
170+
;; use after the container has stopped (not streaming)
170171
(defn attach-container [{:keys [Id]}]
171172
;; logs is true (as opposed to stream=true) so we run this after the container has stopped
172173
;; TTY is true above so this is the just the raw data sent to the PTY (not multiplexed)
@@ -175,6 +176,7 @@
175176
{:raw-args ["--unix-socket" "/var/run/docker.sock"]
176177
:throw false}))
177178

179+
;; container was created without a Terminal (output is multiplexed so read as bytes)
178180
(defn attach-container-stdout-logs [{:keys [Id]}]
179181
;; this assumes no Tty so the output will be multiplexed back
180182
(curl/post
@@ -183,8 +185,8 @@
183185
:as :bytes
184186
:throw false}))
185187

188+
;; container must be created with a Terminal (so we can stream with a Reader)
186189
(defn attach-container-stream-stdout [{:keys [Id]}]
187-
;; this assumes no Tty so the output will be multiplexed back
188190
(curl/post
189191
(format "http://localhost/containers/%s/attach?stderr=false&stdout=true&stream=true" Id)
190192
{:raw-args ["--unix-socket" "/var/run/docker.sock"]
@@ -256,46 +258,55 @@
256258
[m cb]
257259
(when (not (has-image? (:image m)))
258260
(-pull m))
259-
(let [x (create m)
261+
(let [x (-> m
262+
(update :opts
263+
(fnil merge {})
264+
{:Tty true
265+
:StdinOnce false
266+
:OpenStdin false
267+
:AttachStdin false})
268+
(create))
260269
finished-channel (async/promise-chan)]
261270
(start x)
262271

263272
(async/go
264273
(try
265274
(let [s (:body (attach-container-stream-stdout x))]
266-
(println s)
267275
(doseq [line (line-seq (java.io.BufferedReader. (java.io.InputStreamReader. s)))]
268276
(cb line)))
269277
(catch Throwable e
270278
(println e))))
271279

272-
;; watch the container
280+
;; watch the container
273281
(async/go
274282
(wait x)
275283
(async/>! finished-channel {:done :exited}))
276284

277-
;; body is raw PTY output
278-
(let [finish-reason (async/<!! finished-channel)
279-
s (:body (attach x))
280-
info (inspect x)]
281-
(delete x)
282-
(merge
283-
finish-reason
284-
{:pty-output s
285-
:exit-code (-> info :State :ExitCode)
286-
:info info}))))
285+
{:container x
286+
;; stopped channel
287+
:stopped (async/go
288+
;; body is raw PTY output
289+
(let [finish-reason (async/<!! finished-channel)
290+
s (:body (attach x))
291+
info (inspect x)]
292+
(delete x)
293+
(merge
294+
finish-reason
295+
{:pty-output s
296+
:exit-code (-> info :State :ExitCode)
297+
:info info})))}))
287298

288299
(comment
289300
(async/thread
290301
(run-streaming-function-with-no-stdin
291-
{:image "vonwig/inotifywait:latest"
292-
:volumes ["docker-prompts:/prompts"]
293-
:command ["-e" "create" "-e" "modify" "-e" "delete" "-q" "-m" "/prompts"]
294-
:opts {:Tty true
295-
:StdinOnce false
296-
:OpenStdin false
297-
:AttachStdin false}}
298-
println)))
302+
{:image "vonwig/inotifywait:latest"
303+
:volumes ["docker-prompts:/prompts"]
304+
:command ["-e" "create" "-e" "modify" "-e" "delete" "-q" "-m" "/prompts"]
305+
:opts {:Tty true
306+
:StdinOnce false
307+
:OpenStdin false
308+
:AttachStdin false}}
309+
println)))
299310

300311
(defn run-function
301312
"run container function with no stdin, and no streaming output"
@@ -327,6 +338,9 @@
327338
:exit-code (-> info :State :ExitCode)
328339
:info info}))))
329340

341+
;; not curl - opens a real socket to write to the container stdin
342+
;; returns the open socket - closing the socket will signal the program to finish
343+
;; if it's waiting on input
330344
(defn write-stdin [container-id content]
331345
(let [buf (ByteBuffer/allocate (* 1024 20))
332346
address (UnixDomainSocketAddress/of "/var/run/docker.sock")
@@ -362,7 +376,10 @@
362376
(println t)
363377
"")))
364378

365-
(defn function-call-with-stdin [m]
379+
(defn function-call-with-stdin
380+
"creates and starts container, then writes to stdin process
381+
returns container map with Id, and socket - socket is open socket to stdin"
382+
[m]
366383
(when (not (has-image? (:image m)))
367384
(-pull m))
368385
(let [x (merge
@@ -390,7 +407,8 @@
390407
(async/go
391408
(wait x)
392409
(async/>! finished-channel {:done :exited}))
393-
;; body is raw PTY output
410+
;; body is raw PTY output - could we
411+
;; have just been reading from the socket since it's two way?
394412
(try
395413
(let [finish-reason (async/<!! finished-channel)
396414
s (docker-stream-format->stdout

src/git.clj

+15-1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@
5656
(fs/create-dirs default-dir)
5757
default-dir))))
5858

59+
(comment
60+
"alpine/git uses a VOLUME /git declaration
61+
so we need to mount something there or docker will create
62+
a volume.
63+
https://docs.docker.com/reference/dockerfile/#volume
64+
see https://hub.docker.com/r/alpine/git for usage guidelines")
65+
5966
(defn pull [{:keys [dir ref]}]
6067
(docker/run-container
6168
(merge
@@ -64,24 +71,31 @@
6471
(when ref [ref]))}
6572
(if (string/starts-with? (str dir) "/prompts")
6673
{:workdir (str dir)
74+
:volumes ["docker-prompts-git:/git"]
6775
:mounts ["docker-prompts:/prompts:rw"]}
68-
{:host-dir (str dir)}))))
76+
{:host-dir (str dir)
77+
:volumes ["docker-prompts-git:/git"]}))))
6978

7079
(defn clone [{:keys [dir owner repo ref ref-hash]}]
7180
(docker/run-container
7281
(merge
7382
{:image "alpine/git:latest"}
7483
(if (string/starts-with? (str dir) "/prompts")
7584
{:workdir (str dir)
85+
:volumes ["docker-prompts-git:/git"]
7686
:command (concat ["clone" "--depth" "1" (format "https://github.com/%s/%s" owner repo)]
7787
(when ref ["-b" ref])
7888
[(format "/prompts/%s" ref-hash)])
7989
:mounts ["docker-prompts:/prompts:rw"]}
8090
{:host-dir (str dir)
91+
:volumes ["docker-prompts-git:/git"]
8192
:command (concat ["clone" "--depth" "1" (format "https://github.com/%s/%s" owner repo)]
8293
(when ref ["-b" ref])
8394
[(format "/project/%s" ref-hash)])}))))
8495

96+
(comment
97+
(clone {:dir "/Users/slim/crap" :owner "docker" :repo "labs-make-runbook" :ref "main" :ref-hash "crap"}))
98+
8599
(defn prompt-file
86100
"returns the path or nil if the github ref does not resolve
87101
throws if the path in the repo does not exist or if the clone fails"

src/jsonrpc/db.clj

-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
(try
4545
(let [{:keys [registry]} (yaml/parse-string registry-content)
4646
prompt-registry (get-prompt-data (assoc opts :register (map :ref (vals registry))))]
47-
(logger/info "merging" prompt-registry)
4847
(swap! db* add-dynamic-prompts prompt-registry))
4948
(catch Throwable e
5049
(logger/error e "could not merge dynamic prompts"))))

src/jsonrpc/logger.clj

+3
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,6 @@
5050
`(when *logger*
5151
(-debug *logger* ~fmeta ~@args))))
5252

53+
(defn trace [x]
54+
(info "trace " x)
55+
x)

0 commit comments

Comments
 (0)