Skip to content

Commit

Permalink
Python servers require the initialized notification
Browse files Browse the repository at this point in the history
  • Loading branch information
slimslenderslacks committed Mar 6, 2025
1 parent 8d1e7dd commit 0e6276c
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 182 deletions.
15 changes: 3 additions & 12 deletions runbook.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
```sh
docker buildx build \
--builder hydrobuild \
--platform linux/amd64,linux/arm64 \
--tag mcp/docker:0.0.1 \
--file Dockerfile \
--push .
docker pull mcp/docker:0.0.1
```

```sh
cd src/extension && make build-extension
```

```sh
docker extension update docker/labs-ai-tools-for-devs:0.2.7
docker extension update docker/labs-ai-tools-for-devs:0.2.8
```

```sh
Expand All @@ -32,10 +23,10 @@ docker pull mcp/docker:prerelease
docker buildx build \
--builder hydrobuild \
--platform linux/amd64,linux/arm64 \
--tag mcp/docker:0.0.1 \
--tag mcp/docker:0.0.4 \
--file Dockerfile \
--push .
docker pull mcp/docker:0.0.1
docker pull mcp/docker:0.0.4
```

```sh
Expand Down
75 changes: 49 additions & 26 deletions src/docker.clj
Original file line number Diff line number Diff line change
Expand Up @@ -283,26 +283,6 @@
(def pull (comp (status? 200 "pull-image") pull-image))
(def images (comp ->json list-images))

(defn injected-entrypoint [secrets s]
(format "%s ; %s"
(->> secrets
(map (fn [[k v]]
(format "export %s=$(cat /secret/%s | sed -e \"s/^[[:space:]]*//\")" v (name k))))
(string/join " ; "))
s))

(defn inject-secret-transform [container-definition]
(let [{:keys [Entrypoint Cmd]}
(->
(image-inspect
(-> (images {"reference" [(:image container-definition)]})
first))
:Config)
real-entrypoint (string/join " " (concat Entrypoint (or (:command container-definition) Cmd)))]
(-> container-definition
(assoc :entrypoint ["/bin/sh" "-c" (injected-entrypoint (:secrets container-definition) real-entrypoint)])
(dissoc :command))))

(defn add-latest [image]
(let [[_ tag] (re-find #".*(:.*)$" image)]
(if tag
Expand Down Expand Up @@ -331,11 +311,56 @@
(and digest (= digest Id))))
(images {}))))

(defn check-then-pull [container-definition]
(when (not (has-image? (:image container-definition)))
(-pull container-definition)))

(defn injected-entrypoint [secrets environment s]
(->> (concat
(let [s (->> secrets
(map (fn [[k v]]
(format "export %s=$(cat /secret/%s | sed -e \"s/^[[:space:]]*//\")" v (name k))))
(string/join " ; "))]
(when (and s (not (= "" s)))
[s]))
(let [env (->> environment
(map (fn [s] (when-let [[_ k v] (and s (re-find #"(.*)=(.*)" s))]
[k v])))
(filter identity)
(map (fn [[k v]]
(format "export %s=%s" k v)))
(string/join " ; "))]
(when (and env (not (= "" env)))
[env]))
[s])
(string/join " ; ")))

(comment
(injected-entrypoint {:a "A"} ["BLAH=whatever"] "my command")
(injected-entrypoint nil nil "my command")
(injected-entrypoint {:a "A"} nil "my command")
(injected-entrypoint nil nil nil)
)

(defn inject-secret-transform [container-definition]
(check-then-pull container-definition)
(let [{:keys [Entrypoint Cmd Env]}
(->
(image-inspect
(-> (images {"reference" [(:image container-definition)]})
first))
:Config)
real-entrypoint (string/join " " (concat
(or (:entrypoint container-definition) Entrypoint)
(or (:command container-definition) Cmd)))]
(-> container-definition
(assoc :entrypoint ["/bin/sh" "-c" (injected-entrypoint (:secrets container-definition) Env real-entrypoint)])
(dissoc :command))))

(defn run-streaming-function-with-no-stdin
"run container function with no stdin, and no timeout, but streaming stdout"
[m cb]
(when (not (has-image? (:image m)))
(-pull m))
(check-then-pull m)
(let [x (-> m
(update :opts
(fnil merge {})
Expand Down Expand Up @@ -377,8 +402,7 @@
(defn run-background-function
"run container function with no stdin, and no streaming output"
[m]
(when (not (has-image? (:image m)))
(-pull m))
(check-then-pull m)
(let [x (create m)]
(start x)
(shutdown/schedule-container-shutdown
Expand All @@ -391,8 +415,7 @@
(defn run-function
"run container function with no stdin, and no streaming output"
[{:keys [timeout] :or {timeout 600000} :as m}]
(when (not (has-image? (:image m)))
(-pull m))
(check-then-pull m)
(let [x (create m)
finished-channel (async/promise-chan)]
(start x)
Expand Down
2 changes: 1 addition & 1 deletion src/docker/main.clj
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@
(fn [m]
(update-in m [:prompts] (fn [coll] (remove (fn [{:keys [type]}] (= type (second args))) coll))))))
"run" (fn []
(logger/setup (jsonrpc.server/->TimbreLogger))
(logger/setup (jsonrpc.logger/->TimbreLogger))

(let [[in send]
(let [[[w c] in] (user-loop/create-pipe)]
Expand Down
2 changes: 1 addition & 1 deletion src/extension/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
services:
mcp_docker:
image: mcp/docker:0.0.3
image: mcp/docker:0.0.4
ports:
- 8811:8811
volumes:
Expand Down
45 changes: 45 additions & 0 deletions src/jsonrpc/extras.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
(ns jsonrpc.extras
(:require
[clojure.core.async :as async]
git
graph
[jsonrpc.logger :as logger]
[lsp4clj.server :as lsp.server]
[promesa.core :as p]
state
volumes))

(defmethod lsp.server/receive-request "docker/prompts/register" [_ {:keys [db* id]} params]
(logger/info "docker/prompts/register"))

(defmethod lsp.server/receive-request "docker/prompts/run"
[_ {:keys [db* id] :as components} {:keys [thread-id] {:keys [file content uri]} :prompts :as params}]
(lsp.server/discarding-stdout
(let [conversation-id (str (java.util.UUID/randomUUID))
prompt-string (cond
file (slurp file)
content content
uri (slurp (git/prompt-file uri)))]
(swap! db* update-in [:mcp/conversations] (fnil assoc {}) conversation-id
{:state-promise
(p/create
(fn [resolve reject]
(resolve
(async/<!!
(volumes/with-volume
(fn [thread-id]
(let [m (-> {}
(assoc-in [:opts :conversation-id] conversation-id)
(assoc-in [:opts :thread-id] thread-id)
(assoc-in [:opts :prompt-content] prompt-string)
(state/construct-initial-state-from-prompts))]
(graph/stream
(if (-> m :metadata :agent)
((graph/require-graph (-> m :metadata :agent)) m)
(graph/chat-with-tools m))
m)))
(if thread-id
{:thread-id thread-id :save-thread-volume false}
{}))))))})
{:conversation-id conversation-id})))

49 changes: 48 additions & 1 deletion src/jsonrpc/logger.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
(ns jsonrpc.logger)
(ns jsonrpc.logger
(:require
[babashka.fs :as fs]
[taoensso.timbre :as timbre]
[taoensso.timbre.appenders.core :as appenders]))

(defprotocol ILogger
(setup [this])
Expand Down Expand Up @@ -53,3 +57,46 @@
(defn trace [x]
(info "trace " x)
x)

(defn log! [level args fmeta]
(timbre/log! level :p args {:?line (:line fmeta)
:?file (:file fmeta)
:?ns-str (:ns-str fmeta)}))

(defn decide-log-path []
(let [prompts-dir (fs/file "/prompts")]
(if (fs/exists? prompts-dir)
(do
(fs/create-dirs (fs/file prompts-dir "log"))
(fs/file prompts-dir "log/docker-mcp-server.out"))
(do
(fs/create-dirs (fs/file "./log"))
(fs/file "./log/docker-mcp-server.out")))))

(defrecord TimbreLogger []
ILogger
(setup [this]
(let [log-path (str (decide-log-path))]
(timbre/merge-config! {:middleware [#(assoc % :hostname_ "")]
:appenders {:println {:enabled? false}
:spit (appenders/spit-appender {:fname log-path})}})
(timbre/handle-uncaught-jvm-exceptions!)
(set-logger! this)
log-path))

(set-log-path [_this log-path]
(timbre/merge-config! {:appenders {:spit (appenders/spit-appender {:fname log-path})}}))

(-info [_this fmeta arg1] (log! :info [arg1] fmeta))
(-info [_this fmeta arg1 arg2] (log! :info [arg1 arg2] fmeta))
(-info [_this fmeta arg1 arg2 arg3] (log! :info [arg1 arg2 arg3] fmeta))
(-warn [_this fmeta arg1] (log! :warn [arg1] fmeta))
(-warn [_this fmeta arg1 arg2] (log! :warn [arg1 arg2] fmeta))
(-warn [_this fmeta arg1 arg2 arg3] (log! :warn [arg1 arg2 arg3] fmeta))
(-error [_this fmeta arg1] (log! :error [arg1] fmeta))
(-error [_this fmeta arg1 arg2] (log! :error [arg1 arg2] fmeta))
(-error [_this fmeta arg1 arg2 arg3] (log! :error [arg1 arg2 arg3] fmeta))
(-debug [_this fmeta arg1] (log! :debug [arg1] fmeta))
(-debug [_this fmeta arg1 arg2] (log! :debug [arg1 arg2] fmeta))
(-debug [_this fmeta arg1 arg2 arg3] (log! :debug [arg1 arg2 arg3] fmeta)))

39 changes: 38 additions & 1 deletion src/jsonrpc/producer.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
(ns jsonrpc.producer)
(ns jsonrpc.producer
(:require
[jsonrpc.logger :as logger]
[lsp4clj.server :as lsp.server]))

(defprotocol IProducer
(publish-exit [this params])
Expand All @@ -10,3 +13,37 @@
(publish-tool-list-changed [this params])
(publish-docker-notify [this method params]))

(defrecord McpProducer
[server db*]
IProducer

(publish-exit [_this p]
(logger/info "publish-exit " p)
(lsp.server/discarding-stdout
(->> p (lsp.server/send-notification server "$/exit"))))
; params is a map of progressToken, progress, and total
(publish-progress [_this params]
(lsp.server/discarding-stdout
(->> params (lsp.server/send-notification server "notifications/progress"))))
; params is a map of level, logger, data
; level is debug info notice warning error critical alert emergency
(publish-log [_this params]
(->> params (lsp.server/send-notification server "notifications/message")))

(publish-prompt-list-changed [_ params]
(logger/info "send prompt list changed")
(->> params (lsp.server/send-notification server "notifications/prompts/list_changed")))

(publish-resource-list-changed [_ params]
(logger/info "send resource list changed")
(->> params (lsp.server/send-notification server "notifications/resources/list_changed")))

(publish-resource-updated [_ params]
(->> params (lsp.server/send-notification server "notifications/resources/updated")))

(publish-tool-list-changed [_ params]
(logger/info "send tool list changed")
(->> params (lsp.server/send-notification server "notifications/tools/list_changed")))
(publish-docker-notify [_ method params]
(logger/info (format "%s - %s" method params))
(lsp.server/send-notification server method params)))
Loading

0 comments on commit 0e6276c

Please sign in to comment.