init MCP server

This commit is contained in:
Travis Shears 2026-02-28 19:47:00 +01:00
parent fff03fa0d5
commit 8ea0eee299
Signed by: travisshears
GPG key ID: CB9BF1910F3F7469
11 changed files with 253 additions and 21 deletions

9
src/cli/config.clj Normal file
View file

@ -0,0 +1,9 @@
(ns cli.config
(:require
[babashka.fs :as fs]
[clojure.string :as str]))
(def config
(let [project-root (str/join "/" (drop-last 3 (fs/components *file*)))
config-file-path (format "/%s/config.edn" project-root)]
(read-string (slurp config-file-path))))

23
src/cli/create.clj Normal file
View file

@ -0,0 +1,23 @@
(ns cli.create
(:require
[com.travisshears.gum-utils :as utils]
[clojure.string :as str]
[cli.config :refer [config]]
[babashka.http-client :as http]
[cheshire.core :as json]
[babashka.process :refer [shell]]))
(defn create-snippet [{:keys [title slug markdown tags]}]
(http/post (str (:backend-host config) "/api/snippet")
{:headers {:content-type "application/json"}
:body (json/encode {:title title :slug slug :markdown markdown :tags tags})}))
(defn run []
(let [title (utils/prompt-for "title")
slug (utils/prompt-for "slug")
tags (utils/prompt-for-many "tags")
markdown (utils/prompt-for-long-form "markdown")]
(println (format "Created post with title: %s and slug %s" title slug))
(println tags)
(println markdown)
(create-snippet {:title title :slug slug :markdown markdown :tags tags})))

18
src/cli/delete.clj Normal file
View file

@ -0,0 +1,18 @@
(ns cli.delete
(:require
[com.travisshears.gum-utils :as utils]
[cli.config :refer [config]]
[clojure.pprint :refer [pprint]]
[babashka.http-client :as http]
[cheshire.core :as json]))
(defn fetch-snippets []
(let [res (http/get (str (:backend-host config) "/api/snippets?limit=25"))]
(json/parse-string (:body res) true)))
(defn delete-snippet [id]
(http/delete (str (:backend-host config) "/api/snippet") {:query-params {"id" id}}))
(defn run []
(let [selected-snippet (utils/select (map #(hash-map :value (:slug %) :item %) (fetch-snippets)))]
(delete-snippet (:id selected-snippet))))

51
src/cli/edit.clj Normal file
View file

@ -0,0 +1,51 @@
(ns cli.edit
(:require
[babashka.http-client :as http]
[clojure.pprint :refer [pprint]]
[cli.config :refer [config]]
[cli.view :as view]
[cheshire.core :as json]
[com.travisshears.gum-utils :as utils]))
(defn fetch-snippets []
(let [res (http/get (str (:backend-host config) "/api/snippets?limit=25"))]
(json/parse-string (:body res) true)))
(defn prompt-for-edit-type []
(utils/select (map #(hash-map :value (name %) :item %) '(:title
:markdown
:slug
:remove-tag
:add-tags
:done))))
(defn send-patch [id patch]
(http/patch (str (:backend-host config) "/api/snippet")
{:query-params {:id id}
:headers {:content-type "application/json"}
:body (json/encode patch)}))
(defn edit
([snippet]
(edit snippet {}))
([snippet changes]
(case (prompt-for-edit-type)
:title (let [new-title (utils/prompt-for "title" :prefill (:title snippet))]
(edit (assoc snippet :title new-title) (assoc changes :title new-title)))
:slug (let [new-slug (utils/prompt-for "slug" :prefill (:slug snippet))]
(edit (assoc snippet :slug new-slug) (assoc changes :slug new-slug)))
:remove-tag (let [tag-to-remove (utils/select (map #(hash-map :value % :item %) (:tags snippet)))
tags (remove #(= % tag-to-remove) (:tags snippet))]
(edit (assoc snippet :tags tags) (assoc changes :tags tags)))
:add-tags (let [new-tags (utils/prompt-for-many "tags")
tags (concat new-tags (:tags snippet))]
(edit (assoc snippet :tags tags) (assoc changes :tags tags)))
:markdown (let [new-markdown (utils/prompt-for-long-form "markdown" :prefill (:markdown snippet))]
(edit (assoc snippet :markdown new-markdown) (assoc changes :markdown new-markdown)))
:done changes)))
(defn run []
(let [snippet-to-edit (utils/select (map #(hash-map :value (:slug %) :item %) (fetch-snippets)))]
(send-patch (:id snippet-to-edit) (edit snippet-to-edit))
(println "Snippet updated successfully")
(view/view (view/fetch-snippet (:id snippet-to-edit)))))

20
src/cli/main.clj Normal file
View file

@ -0,0 +1,20 @@
(ns cli.main
(:require
[cli.create :as create]
[cli.edit :as edit]
[cli.delete :as delete]
[com.travisshears.gum-utils :as utils]
[cli.view :as view]))
(defn color-test []
(doseq [color (range 0 255)]
(utils/color-print (str "COLOR --- " color) (str color))))
(defn -main [& args]
(case (first args)
"create" (create/run)
"delete" (delete/run)
"edit" (edit/run)
"view" (view/run)
"color-test" (color-test)
(println "Missing command. Try create, edit, or delete.")))

76
src/cli/mcp.clj Normal file
View file

@ -0,0 +1,76 @@
(ns cli.mcp
(:require
[cli.view :as view]
[cheshire.core :as json]))
;; Tool definitions
(def available-tools
[{:name "list_snippets"
:description "List all available code snippets"
:inputSchema {:type "object"
:properties {}
:required []}}])
;; Tool implementations
(defn list-snippets-impl []
(try
(let [snippets (view/fetch-snippets)]
{:success true
:snippets (map #(select-keys % [:id :title :slug :tags]) snippets)})
(catch Exception e
{:success false
:error (.getMessage e)})))
;; Handle tools/list request
(defn handle-tools-list [id]
{:jsonrpc "2.0"
:id id
:result {:tools available-tools}})
;; Handle tools/call request
(defn handle-tools-call [id tool-name tool-input]
{:jsonrpc "2.0"
:id id
:result (case tool-name
"list_snippets" (list-snippets-impl)
{:error (str "Unknown tool: " tool-name)})})
;; Handle initialize request
(defn handle-initialize [id]
{:jsonrpc "2.0"
:id id
:result {:protocolVersion "2024-11-05"
:capabilities {:tools {:listChanged true}
:resources {:listChanged true}
:prompts {:listChanged true}}
:serverInfo {:name "snippets-server"
:version "1.0.0"}}})
;; Main request dispatcher
(defn handle-request [request]
(let [method (:method request)
id (:id request)
params (:params request)]
(case method
"initialize" (handle-initialize id)
"tools/list" (handle-tools-list id)
"tools/call" (handle-tools-call id (:name params) (:arguments params))
{:jsonrpc "2.0"
:id id
:error {:code -32601
:message (str "Method not found: " method)}})))
(defn -main [& args]
;; Read JSON-RPC requests from stdin and process them
(loop []
(let [line (try (read-line) (catch Exception _))]
(when line
(try
(let [request (json/parse-string line true)
response (handle-request request)]
(println (json/encode response)))
(catch Exception e
(println (json/encode {:jsonrpc "2.0"
:error {:code -32700
:message "Parse error"}}))))
(recur)))))

27
src/cli/view.clj Normal file
View file

@ -0,0 +1,27 @@
(ns cli.view
(:require
[babashka.http-client :as http]
[clojure.pprint :refer [pprint]]
[cli.config :refer [config]]
[cheshire.core :as json]
[clojure.string :as str]
[com.travisshears.gum-utils :as utils]))
(defn fetch-snippets []
(let [res (http/get (str (:backend-host config) "/api/snippets?limit=25"))]
(json/parse-string (:body res) true)))
(defn fetch-snippet [id]
(let [res (http/get (str (:backend-host config) "/api/snippet") {:query-params {"id" id}})]
(json/parse-string (:body res) true)))
(defn view [snippet]
(utils/color-print (str "title: " (:title snippet)) "4")
(utils/color-print (str "slug: " (:slug snippet)) "4")
(utils/color-print (str "tags: " (str/join ", " (:tags snippet))) "4")
(utils/color-print "markdown: " "4")
(utils/print-markdown (:markdown snippet)))
(defn run []
(let [snippet-to-view (utils/select (map #(hash-map :value (:slug %) :item %) (fetch-snippets)))]
(view (fetch-snippet (:id snippet-to-view)))))