add validation and unified schemas
CI / Test + coverage (push) Successful in 1m3s

This commit is contained in:
2026-09-19 12:48:41 -09:00
parent 64d1a35da2
commit aa4f8ea86a
30 changed files with 2027 additions and 77 deletions
+4 -1
View File
@@ -17,4 +17,7 @@
:todos {:status :idle
:items []
:new-title ""
:error nil}})
:error nil}
;; The "Add this MCP server to Claude Code" section: :copied? is true right after the
;; command was copied to the clipboard.
:mcp {:copied? false}})
+21
View File
@@ -167,3 +167,24 @@
(if ok?
{:db (update-in db [:todos :items] (fn [items] (filterv #(not= id (:id %)) items)))}
{:dispatch [::todo-failed resp]})))
;; --- MCP setup section ---
;; Effect: put `text` on the clipboard. The Clipboard API only exists on secure origins (https
;; or localhost), so the view doesn't offer the button without it; failures are ignored.
(rf/reg-fx
::copy-to-clipboard
(fn [text]
(some-> js/navigator.clipboard (.writeText text) (.catch (fn [_] nil)))))
(rf/reg-event-fx
::copy-mcp-command
(fn [{:keys [db]} [_ command]]
{:db (assoc-in db [:mcp :copied?] true)
::copy-to-clipboard command
:dispatch-later [{:ms 2000 :dispatch [::mcp-copied-timeout]}]}))
(rf/reg-event-db
::mcp-copied-timeout
(fn [db _]
(assoc-in db [:mcp :copied?] false)))
+1
View File
@@ -5,3 +5,4 @@
(rf/reg-sub ::result (fn [db _] (get-in db [:calc :result])))
(rf/reg-sub ::user (fn [db _] (:user db)))
(rf/reg-sub ::todos (fn [db _] (:todos db)))
(rf/reg-sub ::mcp-copied? (fn [db _] (get-in db [:mcp :copied?])))
+30
View File
@@ -68,6 +68,35 @@
(when error [:p {:role "alert" :style {:color "crimson"}} error])
[todo-list state]]))
(defn- mcp-command
"The command that adds this server to Claude Code, for the address the page was loaded from
(which is what BASE_URL has to be for the MCP login to work)."
[]
(str "claude mcp add --transport http tbd " js/location.origin "/mcp"))
(defn mcp-setup
"How to add this server to Claude Code: a command to copy, then signing in once."
[]
(let [command (mcp-command)
copied? @(rf/subscribe [::subs/mcp-copied?])]
[:section {:style {:margin-top "2rem"}}
[:h2 "Add this MCP server to Claude Code"]
[:p "Run this in your terminal:"]
[:div {:style {:display "flex" :gap "0.5rem" :align-items "flex-start"}}
[:pre {:style {:flex 1 :margin 0 :padding "0.75rem" :overflow-x "auto"
:border "1px solid GrayText" :border-radius "0.25rem"}}
[:code command]]
(when js/navigator.clipboard
[:button {:type "button" :aria-live "polite"
:on-click #(rf/dispatch [::events/copy-mcp-command command])}
(if copied? "Copied" "Copy")])]
[:p "Then sign in, once:"]
[:ol
[:li "Start " [:code "claude"] " and type " [:code "/mcp"] "."]
[:li "Choose " [:code "tbd"] ", then " [:strong "Authenticate"]
". Your browser opens: log in with Gitea if asked, and click " [:strong "Approve"] "."]
[:li "Back in Claude Code, try: " [:em "\"use the tbd whoami tool\""] "."]]]))
(defn- link-button [props label]
[:button (merge {:type "button"
:style {:background "none" :border 0 :padding 0 :font "inherit"
@@ -92,6 +121,7 @@
[calculator]
(when (= :logged-in (:status user))
[todos])
[mcp-setup]
[:p [:a {:href "/api-docs"} "API docs"] " · " [:a {:href "/swagger.json"} "swagger.json"]
(when-let [link (auth-link user)]
[:<> " · " link])]]))