|
3 | 3 | (:require
|
4 | 4 | [babashka.fs :as fs]
|
5 | 5 | [cheshire.core :as json]
|
6 |
| - [clj-yaml.core :as yaml] |
7 | 6 | [clojure.core :as c]
|
8 | 7 | [clojure.core.async :as async]
|
9 | 8 | [clojure.pprint :as pprint]
|
|
19 | 18 | [lsp4clj.io-chan :as io-chan]
|
20 | 19 | [lsp4clj.io-server :refer [stdio-server]]
|
21 | 20 | [lsp4clj.server :as lsp.server]
|
| 21 | + [medley.core :as medley] |
22 | 22 | [promesa.core :as p]
|
23 | 23 | shutdown
|
24 | 24 | state
|
|
118 | 118 | {:description (or (:description metadata) name)
|
119 | 119 | :messages (prompt-function (or arguments {}))}))
|
120 | 120 |
|
121 |
| -(defmethod lsp.server/receive-request "resources/list" [_ _ _] |
| 121 | +(defmethod lsp.server/receive-request "resources/list" [_ {:keys [db*]} _] |
122 | 122 | (logger/info "resources/list")
|
123 |
| - {:resources []}) |
124 |
| - |
125 |
| -(defmethod lsp.server/receive-request "resources/read" [_ _ params] |
| 123 | + (let [resources |
| 124 | + {:resources (or (->> (:mcp.prompts/resources @db*) |
| 125 | + (vals) |
| 126 | + (map #(select-keys % [:uri :name :description :mimeType]))) |
| 127 | + [])}] |
| 128 | + (logger/info resources) |
| 129 | + resources)) |
| 130 | + |
| 131 | +(defmethod lsp.server/receive-request "resources/read" [_ {:keys [db*]} params] |
126 | 132 | (logger/info "resouces/read" params)
|
127 |
| - {:contents []}) |
| 133 | + {:contents (concat |
| 134 | + [] |
| 135 | + (when-let [m (get-in @db* [:mcp.prompts/resources (:uri params)])] |
| 136 | + [(select-keys m [:uri :mimeType :text :blob])]))}) |
128 | 137 |
|
129 | 138 | (defmethod lsp.server/receive-request "resources/templates/list" [_ _ _]
|
130 | 139 | (logger/info "resources/templates/list")
|
|
149 | 158 | (assoc :inputSchema (or (-> m :function :parameters) {:type "object" :properties {}})))))
|
150 | 159 | (into []))})
|
151 | 160 |
|
| 161 | +(defn resource-uri [db-resources uri] |
| 162 | + ((->> db-resources |
| 163 | + vals |
| 164 | + (map (fn [{:keys [uri matches]}] [matches uri])) |
| 165 | + (into {})) uri)) |
| 166 | + |
| 167 | +(defn update-matched-resources |
| 168 | + "check if any collected-resources match the db ones and update them" |
| 169 | + [db-resources collected-resources] |
| 170 | + (let [matched (->> collected-resources |
| 171 | + (filter (comp (into #{} (->> db-resources (vals) (map :matches))) :uri :resource)))] |
| 172 | + (medley/deep-merge |
| 173 | + db-resources |
| 174 | + (->> matched |
| 175 | + (map (fn [{{:keys [uri text]} :resource}] [(resource-uri db-resources uri) {:text text}])) |
| 176 | + (into {}))))) |
| 177 | + |
| 178 | +(comment |
| 179 | + (update-matched-resources |
| 180 | + {"memo://insights" {:uri "memo://insights" :text "No Business Insights" :matches "resource:///thread/insights.txt"}} |
| 181 | + [{:resource {:uri "resource:///thread/insights.txt" :text "updated"}}])) |
| 182 | + |
| 183 | +(defn update-resources |
| 184 | + "update the resource list in the db |
| 185 | + params - resources coll of mcp resources extracted from last tool call" |
| 186 | + [{:keys [db* producer]} resources] |
| 187 | + (when (seq resources) |
| 188 | + (swap! db* update :mcp.prompts/resources update-matched-resources resources) |
| 189 | + (producer/publish-resource-list-changed producer {}))) |
| 190 | + |
| 191 | +(comment |
| 192 | + (def hey |
| 193 | + (atom |
| 194 | + {:mcp.prompts/resources |
| 195 | + {"memo://insights" |
| 196 | + {:uri "memo://insights" |
| 197 | + :text "No Business Insights" |
| 198 | + :matches "resource:///thread/insights.txt"}}})) |
| 199 | + (update-resources |
| 200 | + {:db* hey |
| 201 | + :producer (reify producer/IProducer (publish-resource-list-changed [_ _] (println "called")))} |
| 202 | + [{:resource {:uri "resource:///thread/insights.txt" :text "updated"}}])) |
| 203 | + |
152 | 204 | (defn mcp-tool-calls
|
153 | 205 | " params
|
154 |
| - db* - uses mcp.prompts/registry and host-dir |
155 |
| - params - tools/call mcp params" |
156 |
| - [db* params] |
| 206 | + db* - uses mcp.prompts/registry and host-dir |
| 207 | + params - tools/call mcp params" |
| 208 | + [{:keys [db*] :as components} params] |
157 | 209 | (volumes/with-volume
|
158 | 210 | (fn [thread-id]
|
159 | 211 | (concat
|
|
171 | 223 | (async/<!!)
|
172 | 224 | (map :content)
|
173 | 225 | (apply str))}]
|
174 |
| - (volumes/pick-up-mcp-resources thread-id))))) |
| 226 | + (volumes/pick-up-mcp-resources thread-id (partial update-resources components)))))) |
175 | 227 |
|
176 |
| -(defmethod lsp.server/receive-request "tools/call" [_ {:keys [db*]} params] |
| 228 | +(defmethod lsp.server/receive-request "tools/call" [_ components params] |
177 | 229 | (logger/info "tools/call")
|
178 | 230 | (logger/info "with params" params)
|
179 | 231 | (lsp.server/discarding-stdout
|
180 |
| - (let [content (mcp-tool-calls db* params)] |
| 232 | + (let [content (mcp-tool-calls components params)] |
181 | 233 | (logger/info "content " (with-out-str (pprint/pprint content)))
|
182 | 234 | ;; TODO with mcp, tool-calls with errors can be explicit
|
183 | 235 | {:content content
|
|
316 | 368 | (swap! db* merge {:log-path log-path} (dissoc opts :in))
|
317 | 369 | ;; register static prompts
|
318 | 370 | (db/add-refs
|
319 |
| - (concat |
320 |
| - (->> (:register opts) |
321 |
| - (map (fn [ref] [:static ref]))) |
| 371 | + (concat |
| 372 | + (->> (:register opts) |
| 373 | + (map (fn [ref] [:static ref]))) |
322 | 374 | ;; register dynamic prompts
|
323 |
| - (when (fs/exists? (fs/file registry)) |
324 |
| - (db/registry-refs registry)))) |
| 375 | + (when (fs/exists? (fs/file registry)) |
| 376 | + (db/registry-refs registry)))) |
325 | 377 | ;; watch dynamic prompts in background
|
326 | 378 | (async/thread
|
327 | 379 | (let [{x :container}
|
|
0 commit comments