Skip to content

Commit 170f851

Browse files
Add resource registrations
1 parent 828d338 commit 170f851

File tree

6 files changed

+137
-27
lines changed

6 files changed

+137
-27
lines changed

dev/catalog.clj

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
(ns catalog
2+
(:require
3+
[clj-yaml.core :as yaml]
4+
git
5+
[medley.core :as medley]
6+
prompts))
7+
8+
(defn f->prompt [f]
9+
(prompts/get-prompts {:prompts f}))
10+
11+
(defn tile-metadata [m]
12+
{:tools (-> m :functions)
13+
:prompts (count (:messages m))
14+
:resources (or (:resources m) {})})
15+
16+
(defn extra-metadata [vals]
17+
(->> vals
18+
(map :ref)
19+
(map git/prompt-file)
20+
(map f->prompt)
21+
(map tile-metadata)))
22+
23+
(spit "catalog.updated.yaml"
24+
(yaml/generate-string
25+
(let [catalog (yaml/parse-string (slurp "prompts/catalog.yaml"))]
26+
(medley/deep-merge
27+
catalog
28+
{:registry
29+
(->> (apply interleave ((juxt keys (comp extra-metadata vals)) (seq (:registry catalog))))
30+
(partition 2)
31+
(map #(into [] %))
32+
(into {})
33+
)}))))
34+

prompts/catalog.yaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,13 @@ registry:
7373
command: [-c, 'echo ''{{insight|safe}}'' >> /thread/insights.txt']
7474
volumes: ['mcp-test:/mcp']
7575
prompts: 1
76-
resources: {}
76+
resources:
77+
- name: Business Insights Memo
78+
description: A living document of discovered business insights
79+
uri: memo://insights
80+
mimeType: text/plain
81+
matches: resource:///thread/insights.txt
82+
default: {text: No business insights have been discovered yet.}
7783
curl:
7884
description: Demonstration of curl tool (use curl to fetch GitHub gists for a user)
7985
ref: github:docker/labs-ai-tools-for-devs?ref=main&path=prompts/examples/curl.md

src/graph.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
(let [[chunk-handler sample] (providers (llm-provider (or (:model metadata) model)))
4949
[c h] (chunk-handler)
5050
request (merge
51-
(dissoc metadata :agent :host-dir :workdir :prompt-format :description :name :parameter-values :arguments) ; TODO should we just select relevant keys instead of removing bad ones
51+
(dissoc metadata :agent :host-dir :workdir :prompt-format :description :name :parameter-values :arguments :resources) ; TODO should we just select relevant keys instead of removing bad ones
5252
{:messages messages
5353
:level level}
5454
(when (seq functions) {:tools functions})

src/jsonrpc/db.clj

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
[jsonrpc.logger :as logger]
66
prompts))
77

8+
(set! *warn-on-reflection* true)
9+
810
(def db* (atom {}))
911

1012
(defn get-prompt-data [{:keys [register] :as opts}]
@@ -14,16 +16,29 @@
1416
[(or (-> m :metadata :name) ref) m])))
1517
(into {})))
1618

19+
(defn extract-resources [m]
20+
(->> (vals m)
21+
(map (comp :resources :metadata))
22+
(filter identity)
23+
(map (fn [{:keys [uri] :as entry}]
24+
[uri (merge
25+
entry
26+
(when (and (not (contains? entry :text)) (-> entry :default :text))
27+
{:text (-> entry :default :text)}))]))
28+
(into {})))
29+
1730
(defn add-static-prompts [db m]
1831
(-> db
1932
(update :mcp.prompts/registry (fnil merge {}) m)
20-
(assoc :mcp.prompts/static m)))
33+
(assoc :mcp.prompts/static m)
34+
(update :mcp.prompts/resources (fnil merge {}) (extract-resources m))))
2135

2236
(defn add-dynamic-prompts [db m]
2337
(logger/info "dynamic keys" (keys (:mcp.prompts/registry db)))
2438
(logger/info "static keys" (keys (:mcp.prompts/static db)))
2539
(-> db
26-
(assoc :mcp.prompts/registry (merge m (:mcp.prompts/static db)))))
40+
(assoc :mcp.prompts/registry (merge m (:mcp.prompts/static db)))
41+
(update :mcp.prompts/resources (fnil merge {}) (extract-resources m))))
2742

2843
(defn update-dynamic [coll]
2944
(swap! db* add-dynamic-prompts (get-prompt-data {:register coll})))
@@ -61,11 +76,12 @@
6176
(logger/info "unknown type " (first coll))))))
6277
;; if registry.yaml is empty
6378
(swap! db* (fn [db] (-> db
64-
(update
65-
:mcp.prompts/registry
66-
(constantly (or (:mcp.prompts/static db) {}))))))))
79+
(update
80+
:mcp.prompts/registry
81+
(constantly (or (:mcp.prompts/static db) {})))))))
82+
(logger/info "resources are " (:mcp.prompts/resources @db*)))
6783

68-
(defn registry-refs
84+
(defn registry-refs
6985
"parse refs from the registry.yaml file - these are dynamic"
7086
[f]
7187
(->>

src/jsonrpc/server.clj

Lines changed: 69 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
(:require
44
[babashka.fs :as fs]
55
[cheshire.core :as json]
6-
[clj-yaml.core :as yaml]
76
[clojure.core :as c]
87
[clojure.core.async :as async]
98
[clojure.pprint :as pprint]
@@ -19,6 +18,7 @@
1918
[lsp4clj.io-chan :as io-chan]
2019
[lsp4clj.io-server :refer [stdio-server]]
2120
[lsp4clj.server :as lsp.server]
21+
[medley.core :as medley]
2222
[promesa.core :as p]
2323
shutdown
2424
state
@@ -118,13 +118,22 @@
118118
{:description (or (:description metadata) name)
119119
:messages (prompt-function (or arguments {}))}))
120120

121-
(defmethod lsp.server/receive-request "resources/list" [_ _ _]
121+
(defmethod lsp.server/receive-request "resources/list" [_ {:keys [db*]} _]
122122
(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]
126132
(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])]))})
128137

129138
(defmethod lsp.server/receive-request "resources/templates/list" [_ _ _]
130139
(logger/info "resources/templates/list")
@@ -149,11 +158,54 @@
149158
(assoc :inputSchema (or (-> m :function :parameters) {:type "object" :properties {}})))))
150159
(into []))})
151160

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+
152204
(defn mcp-tool-calls
153205
" 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]
157209
(volumes/with-volume
158210
(fn [thread-id]
159211
(concat
@@ -171,13 +223,13 @@
171223
(async/<!!)
172224
(map :content)
173225
(apply str))}]
174-
(volumes/pick-up-mcp-resources thread-id)))))
226+
(volumes/pick-up-mcp-resources thread-id (partial update-resources components))))))
175227

176-
(defmethod lsp.server/receive-request "tools/call" [_ {:keys [db*]} params]
228+
(defmethod lsp.server/receive-request "tools/call" [_ components params]
177229
(logger/info "tools/call")
178230
(logger/info "with params" params)
179231
(lsp.server/discarding-stdout
180-
(let [content (mcp-tool-calls db* params)]
232+
(let [content (mcp-tool-calls components params)]
181233
(logger/info "content " (with-out-str (pprint/pprint content)))
182234
;; TODO with mcp, tool-calls with errors can be explicit
183235
{:content content
@@ -316,12 +368,12 @@
316368
(swap! db* merge {:log-path log-path} (dissoc opts :in))
317369
;; register static prompts
318370
(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])))
322374
;; 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))))
325377
;; watch dynamic prompts in background
326378
(async/thread
327379
(let [{x :container}

src/volumes.clj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@
2525
{:uri "resource://example"
2626
:mimeType "text/plain"
2727
:text "Resource Content"}})
28+
2829
(defn pick-up-mcp-resources
2930
" returns a coll of mcp resources picked up for a thread"
30-
[thread-id]
31+
[thread-id callback]
3132
(try
3233
(-> (docker/run-container
3334
{:image "vonwig/bb:latest"
@@ -36,7 +37,8 @@
3637
{:directory "/thread"} keyword)
3738
(script/read-script-at-compile-time "src/volumes/collect.clj")]})
3839
:pty-output
39-
(json/parse-string keyword))
40+
(json/parse-string keyword)
41+
((fn [resources] (callback resources) resources)))
4042
(catch Throwable t
4143
(logger/error t "error collecting mcp resources")
4244
{})))

0 commit comments

Comments
 (0)