Fase 9 (2/N): daemon-hosted MCP server, retire electron/mcp_server.cljs
Completes Fase 9's "consolidar MCP" half: the daemon now serves its own MCP endpoint at /mcp, exposing every semantic-routes operation as an MCP tool against the daemon's authoritative graph, and the old Electron-local MCP server is retired since it's now redundant. New: src/main/frontend/worker/mcp_server.cljs The CF-Worker's own MCP endpoint (deps/db-sync/worker/entry.mjs) generates its tools from the OpenAPI document via @cloudflare/codemode's openApiMcpServer, a package that only exists inside Cloudflare Workers, plus a pile of ChatGPT-App-specific tooling (asset image resources, upload_asset, Cognito protected-resource metadata) this daemon has no use for -- none of that ports to Node. So this registers one tool per operation by hand instead, using @modelcontextprotocol/sdk directly, the same pattern electron/mcp_server.cljs already used for its own 6 tools (registerTool with an explicit title/description/inputSchema/handler) -- just generated from semantic-routes' operation table and operation-docs (now public) instead of writing 35 one-off registrations by hand, so the MCP tool surface can't drift from the HTTP routes or the OpenAPI document. Each tool's Zod input shape is assembled from path params (auto-derived from the route's :param segments) plus a per-operation-id map of the extra query/body fields; a handful of shared fragments (pagination, time filters, the recursive block-tree shape, property-value/task-status selectors) cover the repetition across list/create/update operations. Each tool's handler makes a loopback HTTP call to this same daemon's own /v1/semantic/* route, forwarding the /mcp request's own Authorization header -- reusing the already-tested HTTP-layer logic (repo-error validation, lock-owner assertion, response shaping) instead of a second request path directly against the thread-api. Session/transport plumbing (handle-post/get/delete-request, the transports-by-session-id map) is adapted from electron/mcp_server.cljs's Fastify-based version: that one gets req/res already unwrapped-to-Node-primitives via `.raw` with the body pre-parsed by Fastify; this one is called straight from the daemon's own raw http.createServer callback, so req/res already are what StreamableHTTPServerTransport wants, and the body needs parsing here instead. No separate auth check either -- the daemon's existing bearer-token check on every route (request-authorized? in db_worker_node.cljs) already gates /mcp before this is reached. Wires POST/GET/DELETE /mcp into db_worker_node.cljs's make-server cond chain, alongside the existing /v1/semantic/* branch. Dependencies: adds @modelcontextprotocol/sdk and zod (zod/v3 -- the SDK doesn't work with zod 4's default export, same constraint electron/mcp_server.cljs already worked around) to the root package.json; previously only resources/package.json (Electron's own bundle) had them. Retired: electron/mcp_server.cljs and its wiring (electron/server.cljs's initialize-mcp-routes, its 3 POST/GET/DELETE /mcp route registrations, the :mcp-enabled? config field; components/server.cljs's "copy MCP URL" popup link; components/settings.cljs's entire AI settings tab, which had nothing in it besides the MCP-server toggle). Electron already talks to the daemon (Fase 3) rather than driving itself locally, so a second, separately-configured MCP surface bolted onto Electron's own local automation API server (which otherwise stays -- /api is a distinct, still-used concern) was redundant with the daemon's new one. Removed the 4 associated i18n keys (server/mcp-url-copied, settings/ai, settings.ai/enable-mcp-server[-desc]) from all ~26 locale dicts. New: src/test/frontend/worker/mcp_server_test.cljs Connects a real @modelcontextprotocol/sdk client over HTTP to a live daemon's /mcp endpoint: lists all registered tools, round-trips createPage/getPage and a recursive captureToToday block tree, and checks a tool call against a nonexistent page surfaces as MCP error content rather than throwing. 4 tests, 11 assertions. Verification: all 5 shadow-cljs targets compile with 0 warnings. Full `bb dev:test` run: the only failures are the same 7 pre-existing, sandboxed-network-related persist_db_test.cljs failures already documented in the Fase 8 and Fase 9 (1/N) commits -- identical test names, identical ECONNREFUSED/Invalid URL errors. Every other shard, including all three daemon-HTTP worker test files sharing one test.js process (db_worker_node_test.cljs, semantic_test.cljs, mcp_server_test.cljs), passes clean. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -151,6 +151,7 @@
|
|||||||
"@js-joda/timezone": "2.5.0",
|
"@js-joda/timezone": "2.5.0",
|
||||||
"@logseq/react-tweet-embed": "1.3.1-1",
|
"@logseq/react-tweet-embed": "1.3.1-1",
|
||||||
"@logseq/simple-wave-record": "^0.0.3",
|
"@logseq/simple-wave-record": "^0.0.3",
|
||||||
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
||||||
"@sentry/react": "^10.51.0",
|
"@sentry/react": "^10.51.0",
|
||||||
"@sqlite.org/sqlite-wasm": "3.51.2-build9",
|
"@sqlite.org/sqlite-wasm": "3.51.2-build9",
|
||||||
"@tabler/icons-react": "3.44.0",
|
"@tabler/icons-react": "3.44.0",
|
||||||
@@ -198,7 +199,8 @@
|
|||||||
"uniqolor": "1.1.1",
|
"uniqolor": "1.1.1",
|
||||||
"url": "^0.11.4",
|
"url": "^0.11.4",
|
||||||
"util": "^0.12.5",
|
"util": "^0.12.5",
|
||||||
"yup": "^1.7.1"
|
"yup": "^1.7.1",
|
||||||
|
"zod": "^4.3.6"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@zvec/bindings-darwin-arm64": "0.5.0",
|
"@zvec/bindings-darwin-arm64": "0.5.0",
|
||||||
|
|||||||
505
pnpm-lock.yaml
generated
505
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,231 +0,0 @@
|
|||||||
(ns electron.mcp-server
|
|
||||||
"MCP server routes for the desktop API server."
|
|
||||||
(:require ["@modelcontextprotocol/sdk/server/mcp.js" :refer [McpServer]]
|
|
||||||
["@modelcontextprotocol/sdk/server/streamableHttp.js" :refer [StreamableHTTPServerTransport]]
|
|
||||||
["@modelcontextprotocol/sdk/types.js" :refer [isInitializeRequest]]
|
|
||||||
["zod/v3" :as z] ;; zod 4 doesn't work w/ mcp - https://github.com/modelcontextprotocol/typescript-sdk/issues/925
|
|
||||||
[promesa.core :as p]))
|
|
||||||
|
|
||||||
;; Server util fns
|
|
||||||
;; ===============
|
|
||||||
;; "Stores transports by session ID"
|
|
||||||
(defonce ^:private transports
|
|
||||||
(atom {}))
|
|
||||||
|
|
||||||
(declare create-mcp-api-server)
|
|
||||||
|
|
||||||
;; See https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http
|
|
||||||
;; for how to respond to different MCP requests
|
|
||||||
(defn handle-post-request [api-fn {:keys [port host]} req res]
|
|
||||||
(let [session-id (aget (.-headers req) "mcp-session-id")]
|
|
||||||
(js/console.log "POST /mcp request" session-id (pr-str (.-body req)))
|
|
||||||
(cond
|
|
||||||
(and session-id (@transports session-id))
|
|
||||||
(let [^js transport (@transports session-id)]
|
|
||||||
(.handleRequest transport (.-raw req) (.-raw res) (.-body req)))
|
|
||||||
|
|
||||||
(and (not session-id)
|
|
||||||
(isInitializeRequest (.-body req)))
|
|
||||||
(let [transport (StreamableHTTPServerTransport.
|
|
||||||
#js {:sessionIdGenerator (comp str random-uuid)
|
|
||||||
:enableDnsRebindingProtection true
|
|
||||||
:allowedHosts #js [(str host ":" port)]})
|
|
||||||
mcp-server (create-mcp-api-server api-fn)]
|
|
||||||
(set! (.-onclose transport)
|
|
||||||
(fn []
|
|
||||||
(js/console.log "Transport closed" (.-sessionId transport))
|
|
||||||
(swap! transports dissoc (.-sessionId transport))))
|
|
||||||
(.connect mcp-server transport)
|
|
||||||
(.handleRequest transport (.-raw req) (.-raw res) (.-body req))
|
|
||||||
(js/console.log "Initialize sessionId" (.-sessionId transport))
|
|
||||||
(if (.-sessionId transport)
|
|
||||||
(swap! transports assoc (.-sessionId transport) transport)
|
|
||||||
(js/console.error "No sessionId to initialize!"))
|
|
||||||
res)
|
|
||||||
|
|
||||||
:else
|
|
||||||
(do
|
|
||||||
(.code res 400)
|
|
||||||
(.send res #js {:jsonrpc "2.0"
|
|
||||||
:error #js {:code -32000
|
|
||||||
:message "Bad Request: No valid session ID provided"}
|
|
||||||
:id nil})))))
|
|
||||||
|
|
||||||
(defn handle-get-request
|
|
||||||
[req res]
|
|
||||||
(let [session-id (aget (.-headers req) "mcp-session-id")]
|
|
||||||
(js/console.log "GET /mcp" session-id)
|
|
||||||
(if-let [transport (and session-id (@transports session-id))]
|
|
||||||
(.handleRequest ^js transport (.-raw req) (.-raw res))
|
|
||||||
(-> res (.code 400) (.send "Invalid or missing session ID")))))
|
|
||||||
|
|
||||||
(defn handle-delete-request
|
|
||||||
[req res]
|
|
||||||
(let [session-id (aget (.-headers req) "mcp-session-id")]
|
|
||||||
(js/console.log "DELETE /mcp" session-id)
|
|
||||||
(if-let [transport (and session-id (@transports session-id))]
|
|
||||||
(do
|
|
||||||
(.close transport)
|
|
||||||
(-> res (.code 200) (.send #js {:ok true})))
|
|
||||||
(-> res (.code 400) (.send "Invalid or missing session ID")))))
|
|
||||||
|
|
||||||
(defn mcp-error-response [msg]
|
|
||||||
#js {:content
|
|
||||||
#js [#js {:type "text"
|
|
||||||
:text msg}]})
|
|
||||||
|
|
||||||
(defn mcp-success-response [data]
|
|
||||||
(clj->js {:content
|
|
||||||
[{:type "text"
|
|
||||||
:text (js/JSON.stringify (clj->js data))}]}))
|
|
||||||
|
|
||||||
;; API tool fns
|
|
||||||
;; ============
|
|
||||||
(defn- unexpected-api-error [error]
|
|
||||||
#js {:content
|
|
||||||
#js [#js {:type "text"
|
|
||||||
:text (str "Unexpected API error: " (.-message error))}]})
|
|
||||||
|
|
||||||
(defn- api-tool
|
|
||||||
"Calls API method w/ args and returns a MCP response"
|
|
||||||
[api-fn api-method method-args]
|
|
||||||
(-> (p/let [body (api-fn api-method method-args)]
|
|
||||||
(if-let [error (and body (aget body "error"))]
|
|
||||||
(mcp-error-response (str "API Error: " error))
|
|
||||||
(mcp-success-response body)))
|
|
||||||
(p/catch unexpected-api-error)))
|
|
||||||
|
|
||||||
(defn- api-get-page
|
|
||||||
[call-api-fn args]
|
|
||||||
(call-api-fn "logseq.cli.getPageData" [(aget args "pageName")]))
|
|
||||||
|
|
||||||
(defn- api-list-pages
|
|
||||||
[call-api-fn args]
|
|
||||||
(call-api-fn "logseq.cli.listPages" [#js {:expand (aget args "expand")}]))
|
|
||||||
|
|
||||||
(defn- api-list-tags
|
|
||||||
[call-api-fn args]
|
|
||||||
(call-api-fn "logseq.cli.listTags" [#js {:expand (aget args "expand")}]))
|
|
||||||
|
|
||||||
(defn- api-list-properties
|
|
||||||
[call-api-fn args]
|
|
||||||
(call-api-fn "logseq.cli.listProperties" [#js {:expand (aget args "expand")}]))
|
|
||||||
|
|
||||||
(defn- api-search-blocks
|
|
||||||
[call-api-fn args]
|
|
||||||
(call-api-fn "logseq.app.search" [(aget args "searchTerm") #js {:enable-snippet? false}]))
|
|
||||||
|
|
||||||
(defn- api-upsert-nodes
|
|
||||||
[call-api-fn args]
|
|
||||||
(call-api-fn "logseq.cli.upsertNodes" [(aget args "operations") #js {:dry-run (aget args "dry-run")}]))
|
|
||||||
|
|
||||||
(def ^:large-vars/data-var api-tools
|
|
||||||
"MCP Tools when calling API server"
|
|
||||||
{:listPages
|
|
||||||
{:fn api-list-pages
|
|
||||||
:config #js {:title "List Pages"
|
|
||||||
:description "List all pages in a graph"
|
|
||||||
:inputSchema
|
|
||||||
#js {:expand (-> (z/boolean) .optional (.describe "Provide additional detail on each page"))}}}
|
|
||||||
:getPage
|
|
||||||
{:fn api-get-page
|
|
||||||
:config #js {:title "Get Page"
|
|
||||||
:description "Get a page's content including its blocks. A property and a tag are pages."
|
|
||||||
:inputSchema #js {:pageName (-> (z/string) (.describe "The page's name or uuid"))}}}
|
|
||||||
:upsertNodes
|
|
||||||
{:fn api-upsert-nodes
|
|
||||||
:config
|
|
||||||
#js {:title "Upsert Nodes"
|
|
||||||
:description
|
|
||||||
"This tool must be called at most once per user request. Never re-call it unless explicitly asked.
|
|
||||||
It takes an object with field :operations, which is an array of operation objects.
|
|
||||||
Each operation creates or edits a page, block, tag or property. Each operation is a object
|
|
||||||
that must have :operation, :entityType and :data fields. More about fields in an operation object:
|
|
||||||
* :operation - Either :add or :edit
|
|
||||||
* :entityType - What type of node, e.g. :block, :page, :tag or :property
|
|
||||||
* :id - For :edit, this _must_ be a string uuid. For :add, use a temporary unique string if the new page is referenced by later operations e.g. add blocks
|
|
||||||
* :data - A map of fields to set or update. This map can have the following keys:
|
|
||||||
* :title - A page/tag/property's name or a block's content
|
|
||||||
* :page-id - A page string uuid of a block. Required when adding a block.
|
|
||||||
* :tags - A list of tags as string uuids
|
|
||||||
* :property-type - A property's type
|
|
||||||
* :property-cardinality - A property's cardinality. Must be :one or :many
|
|
||||||
* :property-classes - A property's list of allowed tags, each being a uuid string or a tag's name
|
|
||||||
* :class-extends - List of parent tags, each being a uuid string or a tag's name
|
|
||||||
* :class-properties - A tag's list of properties, each eing a uuid string or a property's name
|
|
||||||
|
|
||||||
Example inputs with their prompt, description and data as clojure EDN:
|
|
||||||
|
|
||||||
Description: This input adds a new block to page with id '119268a6-704f-4e9e-8c34-36dfc6133729' and update the title of a page with uuid '119268a6-704f-4e9e-8c34-36dfc6133729':
|
|
||||||
|
|
||||||
{:operations
|
|
||||||
[{:operation :add
|
|
||||||
:entityType :block
|
|
||||||
:id nil
|
|
||||||
:data {:page-id \"119268a6-704f-4e9e-8c34-36dfc6133729\"
|
|
||||||
:title \"New block text\"}}
|
|
||||||
{:operation :edit
|
|
||||||
:entity :page
|
|
||||||
:id \"119268a6-704f-4e9e-8c34-36dfc6133729\"
|
|
||||||
:data {:title \"Revised page title\"}}]}
|
|
||||||
|
|
||||||
Prompt: Add task 't1' to new page 'Inbox'
|
|
||||||
Description: This input creates a page 'Inbox' and adds a 't1' block with tag \"00000002-1282-1814-5700-000000000000\" (task) to it:
|
|
||||||
|
|
||||||
{:operations
|
|
||||||
[{:operation :add
|
|
||||||
:entityType :page
|
|
||||||
:id \"temp-Inbox\"
|
|
||||||
:data {:title \"Inbox\"}}
|
|
||||||
{:operation :add
|
|
||||||
:entityType :block
|
|
||||||
:data {:page-id \"temp-Inbox\"
|
|
||||||
:title \"t1\"
|
|
||||||
:tags [\"00000002-1282-1814-5700-000000000000\"]}}]}
|
|
||||||
|
|
||||||
Additional advice for building operations:
|
|
||||||
* Before creating any page, tag or property, check that it exists with getPage"
|
|
||||||
:inputSchema
|
|
||||||
#js {:operations
|
|
||||||
(z/array
|
|
||||||
(z/object
|
|
||||||
#js {:operation (z/enum #js ["add" "edit"])
|
|
||||||
:entityType (z/enum #js ["block" "page" "tag" "property"])
|
|
||||||
:id (.optional (z/union #js [(z/string) (z/number) (z/null)]))
|
|
||||||
:data (-> (z/object #js {}) (.passthrough))}))
|
|
||||||
:dry-run (-> (z/boolean) .optional (.describe "Pretend to do batch update. Does everything except actually commit change to db e.g. validation."))}}}
|
|
||||||
:searchBlocks
|
|
||||||
{:fn api-search-blocks
|
|
||||||
:config #js {:title "Search Blocks"
|
|
||||||
:description "Search graph for blocks containing search term"
|
|
||||||
:inputSchema #js {:searchTerm (z/string)}}}
|
|
||||||
:listTags
|
|
||||||
{:fn api-list-tags
|
|
||||||
:config #js {:title "List Tags"
|
|
||||||
:description "List all tags in a graph"
|
|
||||||
:inputSchema
|
|
||||||
#js {:expand (-> (z/boolean) .optional (.describe "Provide additional detail on each tag e.g. their parents (extends) and tag properties"))}}}
|
|
||||||
:listProperties
|
|
||||||
{:fn api-list-properties
|
|
||||||
:config #js {:title "List Properties"
|
|
||||||
:description "List all properties in a graph"
|
|
||||||
:inputSchema
|
|
||||||
#js {:expand (-> (z/boolean) .optional (.describe "Provide additional detail on each property e.g. property type, cardinality"))}}}})
|
|
||||||
|
|
||||||
(defn call-api-tool [tool-fn api-fn args]
|
|
||||||
(tool-fn (partial api-tool api-fn) args))
|
|
||||||
|
|
||||||
;; Server fns
|
|
||||||
;; ==========
|
|
||||||
(defn create-mcp-server []
|
|
||||||
(McpServer. #js {:name "Logseq MCP Server"
|
|
||||||
:version "0.1.0"}))
|
|
||||||
|
|
||||||
(defn create-mcp-api-server [api-fn]
|
|
||||||
(let [mcp-server (create-mcp-server)]
|
|
||||||
(doseq [[k v] api-tools]
|
|
||||||
(.registerTool mcp-server
|
|
||||||
(name k)
|
|
||||||
(:config v)
|
|
||||||
(partial call-api-tool (:fn v) api-fn)))
|
|
||||||
mcp-server))
|
|
||||||
@@ -9,7 +9,6 @@
|
|||||||
[clojure.string :as string]
|
[clojure.string :as string]
|
||||||
[electron.configs :as cfgs]
|
[electron.configs :as cfgs]
|
||||||
[electron.logger :as logger]
|
[electron.logger :as logger]
|
||||||
[electron.mcp-server :as desktop-mcp-server]
|
|
||||||
[electron.utils :as utils]
|
[electron.utils :as utils]
|
||||||
[electron.window :as window]
|
[electron.window :as window]
|
||||||
[promesa.core :as p]))
|
[promesa.core :as p]))
|
||||||
@@ -36,8 +35,7 @@
|
|||||||
:host (get-host)
|
:host (get-host)
|
||||||
:port (get-port)
|
:port (get-port)
|
||||||
:tokens (normalize-tokens (cfgs/get-item :server/tokens))
|
:tokens (normalize-tokens (cfgs/get-item :server/tokens))
|
||||||
:autostart (cfgs/get-item :server/autostart)
|
:autostart (cfgs/get-item :server/autostart)}))
|
||||||
:mcp-enabled? (cfgs/get-item :server/mcp-enabled?)}))
|
|
||||||
|
|
||||||
(defn- set-status!
|
(defn- set-status!
|
||||||
([status] (set-status! status nil))
|
([status] (set-status! status nil))
|
||||||
@@ -141,18 +139,6 @@
|
|||||||
(p/catch (fn [^js e]
|
(p/catch (fn [^js e]
|
||||||
(set-status! :running e))))))
|
(set-status! :running e))))))
|
||||||
|
|
||||||
(defn- initialize-mcp-routes [^js server]
|
|
||||||
(let [api-fn (fn api-fn [meth args]
|
|
||||||
(if-let [meth' (resolve-real-api-method meth)]
|
|
||||||
(invoke-logseq-api! meth' args)
|
|
||||||
#js {:error (str "No method found for " (pr-str meth))}))]
|
|
||||||
(logger/debug "[server] MCP routes initialized")
|
|
||||||
(.post server "/mcp"
|
|
||||||
#(desktop-mcp-server/handle-post-request api-fn {:port (get-port)
|
|
||||||
:host (get-host)} %1 %2))
|
|
||||||
(.get server "/mcp" desktop-mcp-server/handle-get-request)
|
|
||||||
(.delete server "/mcp" desktop-mcp-server/handle-delete-request)))
|
|
||||||
|
|
||||||
(defn start!
|
(defn start!
|
||||||
[]
|
[]
|
||||||
(-> (p/let [_ (close!)
|
(-> (p/let [_ (close!)
|
||||||
@@ -175,8 +161,6 @@
|
|||||||
(string/replace-first "${PORT}" PORT))]
|
(string/replace-first "${PORT}" PORT))]
|
||||||
(doto rep (.type "text/html")
|
(doto rep (.type "text/html")
|
||||||
(.send html))))))
|
(.send html))))))
|
||||||
_ (when (:mcp-enabled? @*state)
|
|
||||||
(initialize-mcp-routes s))
|
|
||||||
;; listen port
|
;; listen port
|
||||||
_ (.listen s (bean/->js (select-keys @*state [:host :port])))]
|
_ (.listen s (bean/->js (select-keys @*state [:host :port])))]
|
||||||
(reset! *server s)
|
(reset! *server s)
|
||||||
|
|||||||
@@ -144,7 +144,7 @@
|
|||||||
#(js/clearTimeout t))))
|
#(js/clearTimeout t))))
|
||||||
[])
|
[])
|
||||||
|
|
||||||
(let [{:keys [status error mcp-enabled?]} server-state
|
(let [{:keys [status error]} server-state
|
||||||
status (keyword (util/safe-lower-case status))
|
status (keyword (util/safe-lower-case status))
|
||||||
running? (= :running status)
|
running? (= :running status)
|
||||||
href (and running? (str "http://" (:host server-state) ":" (:port server-state)))]
|
href (and running? (str "http://" (:host server-state) ":" (:port server-state)))]
|
||||||
@@ -192,14 +192,7 @@
|
|||||||
(server-status-label status)
|
(server-status-label status)
|
||||||
[:span.flex.flex-col.gap-1.text-xs.font-mono
|
[:span.flex.flex-col.gap-1.text-xs.font-mono
|
||||||
[:a.hover:underline.flex.items-center {:href href}
|
[:a.hover:underline.flex.items-center {:href href}
|
||||||
href (shui/tabler-icon "external-link" {:size 12 :class "inline-block ml-1 pt-[1px]"})]
|
href (shui/tabler-icon "external-link" {:size 12 :class "inline-block ml-1 pt-[1px]"})]])]]
|
||||||
(when mcp-enabled?
|
|
||||||
[:a.hover:underline.flex.items-center
|
|
||||||
{:on-click (fn []
|
|
||||||
(util/copy-to-clipboard! (str href "/mcp"))
|
|
||||||
(notification/show! (t :server/mcp-url-copied) :success))}
|
|
||||||
(str href "/mcp")
|
|
||||||
(shui/tabler-icon "copy" {:size 12 :class "inline-block ml-1 mt-[1px]"})])])]]
|
|
||||||
(for [{:keys [hr? title options icon]} items]
|
(for [{:keys [hr? title options icon]} items]
|
||||||
(cond
|
(cond
|
||||||
hr?
|
hr?
|
||||||
|
|||||||
@@ -1222,42 +1222,6 @@
|
|||||||
|
|
||||||
[:<>])
|
[:<>])
|
||||||
|
|
||||||
(hsx/defc mcp-server-row
|
|
||||||
[t]
|
|
||||||
(let [server-state (rfx/use-sub [:electron/server])
|
|
||||||
[checked set-checked!] (hooks/use-state false)]
|
|
||||||
|
|
||||||
(hooks/use-effect!
|
|
||||||
(fn []
|
|
||||||
(let [initial (:mcp-enabled? server-state)]
|
|
||||||
(set-checked! initial)))
|
|
||||||
[server-state])
|
|
||||||
|
|
||||||
(let [on-toggle (fn []
|
|
||||||
(let [new-val (not checked)]
|
|
||||||
(set-checked! new-val)
|
|
||||||
;; Enable HTTP server to simplify starting MCP
|
|
||||||
(when (and new-val (not (storage/get ::storage-spec/http-server-enabled)))
|
|
||||||
(storage/set ::storage-spec/http-server-enabled true))
|
|
||||||
(-> (ipc/ipc :server/set-config {:mcp-enabled? new-val})
|
|
||||||
;; Don't start server if it's not running
|
|
||||||
(p/then #(when (= "running" (state/get-state [:electron/server :status]))
|
|
||||||
(p/let [_ (p/delay 1000)]
|
|
||||||
(ipc/ipc :server/do :restart))))
|
|
||||||
(p/catch #(notification/show! (str %) :error)))))]
|
|
||||||
(toggle "mcp-server"
|
|
||||||
(t :settings.ai/enable-mcp-server)
|
|
||||||
checked
|
|
||||||
on-toggle
|
|
||||||
[:span.text-sm.opacity-50
|
|
||||||
(t :settings.ai/enable-mcp-server-desc)]))))
|
|
||||||
|
|
||||||
(hsx/defc settings-ai
|
|
||||||
[]
|
|
||||||
[:div.panel-wrap
|
|
||||||
(when (util/electron?)
|
|
||||||
(mcp-server-row t))])
|
|
||||||
|
|
||||||
(hsx/defc ^:large-vars/cleanup-todo settings
|
(hsx/defc ^:large-vars/cleanup-todo settings
|
||||||
[_active-tab]
|
[_active-tab]
|
||||||
(let [current-repo (rfx/use-sub [:git/current-repo])
|
(let [current-repo (rfx/use-sub [:git/current-repo])
|
||||||
@@ -1270,11 +1234,8 @@
|
|||||||
(fn []
|
(fn []
|
||||||
(state/load-app-user-cfgs)
|
(state/load-app-user-cfgs)
|
||||||
(state/load-electron-app-base-info)
|
(state/load-electron-app-base-info)
|
||||||
(let [active-tab (if (and (= _active-tab :ai) (not (util/electron?)))
|
(when (keyword? _active-tab)
|
||||||
:advanced
|
(reset! *active [_active-tab nil]))
|
||||||
_active-tab)]
|
|
||||||
(when (keyword? active-tab)
|
|
||||||
(reset! *active [active-tab nil])))
|
|
||||||
#(state/close-settings!))
|
#(state/close-settings!))
|
||||||
[])
|
[])
|
||||||
|
|
||||||
@@ -1291,9 +1252,6 @@
|
|||||||
[:general (t :settings/general) (ui/icon "adjustments")]
|
[:general (t :settings/general) (ui/icon "adjustments")]
|
||||||
[:editor (t :settings/editor) (ui/icon "writing")]
|
[:editor (t :settings/editor) (ui/icon "writing")]
|
||||||
[:keymap (t :settings/keymap) (ui/icon "keyboard")]
|
[:keymap (t :settings/keymap) (ui/icon "keyboard")]
|
||||||
(when (util/electron?)
|
|
||||||
[:ai (t :settings/ai) (ui/icon "wand")])
|
|
||||||
|
|
||||||
[:advanced (t :settings/advanced) (ui/icon "bulb")]
|
[:advanced (t :settings/advanced) (ui/icon "bulb")]
|
||||||
[:features (t :settings/features) (ui/icon "app-feature")]
|
[:features (t :settings/features) (ui/icon "app-feature")]
|
||||||
|
|
||||||
@@ -1348,9 +1306,4 @@
|
|||||||
:features
|
:features
|
||||||
(settings-features)
|
(settings-features)
|
||||||
|
|
||||||
:ai
|
|
||||||
(if (util/electron?)
|
|
||||||
(settings-ai)
|
|
||||||
(settings-advanced))
|
|
||||||
|
|
||||||
nil)]]]))
|
nil)]]]))
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
[frontend.worker.db-worker-node-auth :as auth]
|
[frontend.worker.db-worker-node-auth :as auth]
|
||||||
[frontend.worker.db-worker-node-lock :as db-lock]
|
[frontend.worker.db-worker-node-lock :as db-lock]
|
||||||
[frontend.worker.db-worker-node-ws :as db-ws]
|
[frontend.worker.db-worker-node-ws :as db-ws]
|
||||||
|
[frontend.worker.mcp-server :as mcp-server]
|
||||||
[frontend.worker.platform.node :as platform-node]
|
[frontend.worker.platform.node :as platform-node]
|
||||||
[frontend.worker.semantic-routes :as semantic-routes]
|
[frontend.worker.semantic-routes :as semantic-routes]
|
||||||
[frontend.worker.state :as worker-state]
|
[frontend.worker.state :as worker-state]
|
||||||
@@ -676,6 +677,15 @@
|
|||||||
(string/starts-with? request-path "/v1/semantic/")
|
(string/starts-with? request-path "/v1/semantic/")
|
||||||
(semantic-request-handler req res bound-repo proxy parsed-url request-path method)
|
(semantic-request-handler req res bound-repo proxy parsed-url request-path method)
|
||||||
|
|
||||||
|
(and (= request-path "/mcp") (= method "POST"))
|
||||||
|
(mcp-server/handle-post-request {:host host :port (effective-port port)} req res)
|
||||||
|
|
||||||
|
(and (= request-path "/mcp") (= method "GET"))
|
||||||
|
(mcp-server/handle-get-request req res)
|
||||||
|
|
||||||
|
(and (= request-path "/mcp") (= method "DELETE"))
|
||||||
|
(mcp-server/handle-delete-request req res)
|
||||||
|
|
||||||
(= request-path "/v1/invoke-batch")
|
(= request-path "/v1/invoke-batch")
|
||||||
(if (= method "POST")
|
(if (= method "POST")
|
||||||
(-> (p/let [body (<read-body req)
|
(-> (p/let [body (<read-body req)
|
||||||
|
|||||||
280
src/main/frontend/worker/mcp_server.cljs
Normal file
280
src/main/frontend/worker/mcp_server.cljs
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
(ns frontend.worker.mcp-server
|
||||||
|
"MCP server for the daemon (Fase 9), exposing the semantic API
|
||||||
|
(semantic-routes/operations) as MCP tools. The CF-Worker's equivalent
|
||||||
|
(deps/db-sync/worker/entry.mjs) auto-generates its tools from the OpenAPI
|
||||||
|
document via @cloudflare/codemode's openApiMcpServer, a package that only
|
||||||
|
exists inside Cloudflare Workers -- not portable to Node -- plus a pile of
|
||||||
|
ChatGPT-App-specific tooling (asset image resources, upload_asset,
|
||||||
|
Cognito protected-resource metadata) this daemon has no use for. So this
|
||||||
|
registers one tool per operation by hand instead, the same pattern
|
||||||
|
electron/mcp_server.cljs already used for its own 6 tools (registerTool
|
||||||
|
with an explicit title/description/inputSchema/handler) -- just generated
|
||||||
|
from semantic-routes' operation table and docs instead of writing 34
|
||||||
|
one-off registrations, so the MCP tool surface can't drift from the HTTP
|
||||||
|
routes and the OpenAPI document.
|
||||||
|
|
||||||
|
Each tool's handler makes a loopback HTTP call to this same daemon's own
|
||||||
|
/v1/semantic/* route (see db_worker_node.cljs), forwarding the
|
||||||
|
Authorization header the /mcp request itself carried -- reusing the
|
||||||
|
already-tested HTTP-layer logic (repo-error validation, lock-owner
|
||||||
|
assertion, response shaping) rather than re-implementing a second
|
||||||
|
request path directly against the thread-api."
|
||||||
|
(:require ["@modelcontextprotocol/sdk/server/mcp.js" :refer [McpServer]]
|
||||||
|
["@modelcontextprotocol/sdk/server/streamableHttp.js" :refer [StreamableHTTPServerTransport]]
|
||||||
|
["@modelcontextprotocol/sdk/types.js" :refer [isInitializeRequest]]
|
||||||
|
["zod/v3" :as z]
|
||||||
|
[clojure.string :as string]
|
||||||
|
[frontend.worker.semantic-routes :as semantic-routes]
|
||||||
|
[lambdaisland.glogi :as log]
|
||||||
|
[promesa.core :as p]))
|
||||||
|
|
||||||
|
;; Shared Zod field fragments
|
||||||
|
;; ==========================
|
||||||
|
(def ^:private pagination-fields
|
||||||
|
{:limit (-> (z/number) .int .optional (.describe "Max items per page (default 50, max 200)"))
|
||||||
|
:cursor (-> (z/string) .optional (.describe "Opaque pagination cursor from a previous response's next-cursor"))})
|
||||||
|
|
||||||
|
(def ^:private time-filter-fields
|
||||||
|
{:created-after (-> (z/number) .int .optional (.describe "Unix epoch ms; only entities created strictly after this"))
|
||||||
|
:updated-after (-> (z/number) .int .optional (.describe "Unix epoch ms; only entities updated strictly after this"))})
|
||||||
|
|
||||||
|
(def ^:private listing-fields (merge pagination-fields time-filter-fields))
|
||||||
|
|
||||||
|
(def ^:private block-tree-schema
|
||||||
|
"Self-recursive, so it needs z/lazy -- the thunk closes over this same var,
|
||||||
|
resolved once def completes, the standard cljs recursive-def idiom."
|
||||||
|
(z/lazy (fn []
|
||||||
|
(z/object #js {:title (z/string)
|
||||||
|
:children (-> (z/array block-tree-schema) .optional)}))))
|
||||||
|
|
||||||
|
(def ^:private property-value-schema
|
||||||
|
(-> (z/union #js [(z/string) (z/number) (z/boolean)
|
||||||
|
(z/array (z/union #js [(z/string) (z/number)]))])
|
||||||
|
(.describe (str "For ref properties, an existing entity's UUID, ident, or title. Status/Priority also "
|
||||||
|
"accept built-in aliases like TODO or HIGH. Class properties like Tags accept one "
|
||||||
|
"selector or an array of them. Scalar properties take their JSON value directly."))))
|
||||||
|
|
||||||
|
(def ^:private task-status-field
|
||||||
|
(-> (z/string) .optional
|
||||||
|
(.describe "todo|doing|in-review|done|canceled|backlog, or an existing Status choice's UUID/ident/title")))
|
||||||
|
|
||||||
|
(def ^:private task-priority-field
|
||||||
|
(-> (z/string) .optional
|
||||||
|
(.describe "low|medium|high|urgent, or an existing Priority choice's UUID/ident/title")))
|
||||||
|
|
||||||
|
(def ^:private property-type-field
|
||||||
|
(-> (z/enum #js ["default" "number" "date" "datetime" "checkbox" "url" "node" "asset" "map" "json" "string"])
|
||||||
|
.optional))
|
||||||
|
|
||||||
|
(def ^:private property-cardinality-field
|
||||||
|
(-> (z/enum #js ["db.cardinality/one" "db.cardinality/many"]) .optional))
|
||||||
|
|
||||||
|
;; Per-operation extra fields (beyond the path params every operation gets
|
||||||
|
;; automatically -- see path-arg-fields below)
|
||||||
|
;; ============================================================================
|
||||||
|
(def ^:private extra-fields
|
||||||
|
{"listPages" listing-fields
|
||||||
|
"createPage" {:title (z/string)}
|
||||||
|
"listPageBlocks" listing-fields
|
||||||
|
"listPageReferences" listing-fields
|
||||||
|
"updatePage" {:title (z/string)}
|
||||||
|
"updateBlock" {:title (z/string)}
|
||||||
|
"moveBlocks" {:block-ids (z/array (z/string))
|
||||||
|
:target-id (z/string)
|
||||||
|
:position (z/enum #js ["before" "after" "first-child" "last-child"])}
|
||||||
|
"insertBlockChildren" {:position (z/enum #js ["append" "prepend"])
|
||||||
|
:blocks (z/array block-tree-schema)}
|
||||||
|
"insertBlockTree" {:target-id (z/string)
|
||||||
|
:position (-> (z/enum #js ["append" "prepend"]) .optional)
|
||||||
|
:blocks (z/array block-tree-schema)}
|
||||||
|
"setBlockProperty" {:value property-value-schema}
|
||||||
|
"batchSetBlockProperty" {:isResetExistingValues (-> (z/boolean) .optional)
|
||||||
|
:entries (z/array (z/object #js {:block-id (z/string)
|
||||||
|
:property-id (z/string)
|
||||||
|
:value property-value-schema}))}
|
||||||
|
"batchDeleteBlockProperty" {:entries (z/array (z/object #js {:block-id (z/string) :property-id (z/string)}))}
|
||||||
|
"captureToToday" {:blocks (z/array block-tree-schema)}
|
||||||
|
"listTasks" (merge listing-fields
|
||||||
|
{:status task-status-field
|
||||||
|
:priority task-priority-field
|
||||||
|
:content (-> (z/string) .optional (.describe "Case-insensitive task title filter"))})
|
||||||
|
"createTask" {:title (z/string)
|
||||||
|
:page-id (-> (z/string) .optional (.describe "Destination page UUID; omit for today's journal"))
|
||||||
|
:status task-status-field
|
||||||
|
:priority task-priority-field
|
||||||
|
:scheduled (-> (z/number) .int .optional (.describe "Scheduled date as YYYYMMDD"))
|
||||||
|
:deadline (-> (z/number) .int .optional (.describe "Deadline date as YYYYMMDD"))}
|
||||||
|
"listTags" listing-fields
|
||||||
|
"createTag" {:title (z/string)}
|
||||||
|
"listTagObjects" listing-fields
|
||||||
|
"updateTag" {:title (z/string)}
|
||||||
|
"listProperties" listing-fields
|
||||||
|
"createProperty" {:title (z/string) :type property-type-field :cardinality property-cardinality-field}
|
||||||
|
"updateProperty" {:title (-> (z/string) .optional) :type property-type-field :cardinality property-cardinality-field}
|
||||||
|
"listAssets" listing-fields
|
||||||
|
"createAsset" {:file-name (z/string)
|
||||||
|
:size (-> (z/number) .int (.describe "Exact decoded byte size of the file, max 100MB"))
|
||||||
|
:checksum (-> (z/string) (.regex (js/RegExp. "^[0-9a-fA-F]{64}$"))
|
||||||
|
(.describe "SHA-256 hex digest of the decoded file"))
|
||||||
|
:title (-> (z/string) .optional)
|
||||||
|
:page-id (-> (z/string) .optional (.describe "Destination page UUID; omit for today's journal"))
|
||||||
|
:content (-> (z/string) (.describe "Base64-encoded file contents"))}
|
||||||
|
"searchGraph" (merge listing-fields
|
||||||
|
{:q (z/string)
|
||||||
|
:types (-> (z/string) .optional
|
||||||
|
(.describe "Comma-separated subset of blocks,tags,properties,assets"))})})
|
||||||
|
|
||||||
|
;; Request construction
|
||||||
|
;; ====================
|
||||||
|
(defn- path-param-names [path]
|
||||||
|
(mapv second (re-seq #":([^/]+)" path)))
|
||||||
|
|
||||||
|
(defn- path-arg-fields [path]
|
||||||
|
(into {}
|
||||||
|
(map (fn [param]
|
||||||
|
[(keyword param) (-> (z/string) (.describe (str "The " param)))]))
|
||||||
|
(path-param-names path)))
|
||||||
|
|
||||||
|
(defn- build-path [path args]
|
||||||
|
(reduce (fn [p param]
|
||||||
|
(string/replace p (str ":" param) (js/encodeURIComponent (str (get args (keyword param))))))
|
||||||
|
path
|
||||||
|
(path-param-names path)))
|
||||||
|
|
||||||
|
(defn- query-string [args]
|
||||||
|
(let [pairs (->> args
|
||||||
|
(remove (fn [[_ v]] (nil? v)))
|
||||||
|
(map (fn [[k v]] (str (js/encodeURIComponent (name k)) "=" (js/encodeURIComponent (str v))))))]
|
||||||
|
(if (seq pairs) (str "?" (string/join "&" pairs)) "")))
|
||||||
|
|
||||||
|
(defn- request-for
|
||||||
|
[{:keys [operation-id method path]} args]
|
||||||
|
(let [path-names (set (map keyword (path-param-names path)))
|
||||||
|
rest-args (apply dissoc args path-names)
|
||||||
|
url-path (build-path path args)]
|
||||||
|
(cond
|
||||||
|
(= operation-id "createAsset")
|
||||||
|
{:method method
|
||||||
|
:path (str url-path (query-string (assoc (dissoc rest-args :content) :encoding "base64")))
|
||||||
|
:body (:content rest-args)
|
||||||
|
:content-type "text/plain"}
|
||||||
|
|
||||||
|
(contains? #{"GET" "DELETE"} method)
|
||||||
|
{:method method :path (str url-path (query-string rest-args)) :body nil :content-type nil}
|
||||||
|
|
||||||
|
:else
|
||||||
|
{:method method :path url-path :body (js/JSON.stringify (clj->js rest-args)) :content-type "application/json"})))
|
||||||
|
|
||||||
|
(defn- call-semantic-operation!
|
||||||
|
[{:keys [host port authorization]} operation args]
|
||||||
|
(let [{:keys [method path body content-type]} (request-for operation args)
|
||||||
|
url (str "http://" host ":" port path)]
|
||||||
|
(-> (js/fetch url (clj->js (cond-> {:method method
|
||||||
|
:headers (cond-> {}
|
||||||
|
authorization (assoc "Authorization" authorization)
|
||||||
|
content-type (assoc "Content-Type" content-type))}
|
||||||
|
body (assoc :body body))))
|
||||||
|
(p/then (fn [^js res]
|
||||||
|
(p/let [text (.text res)]
|
||||||
|
{:status (.-status res) :ok (.-ok res) :text text}))))))
|
||||||
|
|
||||||
|
(defn- mcp-result [{:keys [ok text]}]
|
||||||
|
#js {:content #js [#js {:type "text" :text (if (string/blank? text) "{}" text)}]
|
||||||
|
:isError (not ok)})
|
||||||
|
|
||||||
|
(defn- mcp-error-result [error]
|
||||||
|
#js {:content #js [#js {:type "text" :text (str "Unexpected MCP tool error: " (or (.-message error) error))}]
|
||||||
|
:isError true})
|
||||||
|
|
||||||
|
;; Server construction
|
||||||
|
;; ===================
|
||||||
|
(defn- register-operation-tool!
|
||||||
|
[^js mcp-server ctx operation]
|
||||||
|
(let [{:keys [operation-id]} operation
|
||||||
|
[title description] (get semantic-routes/operation-docs operation-id)
|
||||||
|
input-shape (clj->js (merge (path-arg-fields (:path operation)) (get extra-fields operation-id {})))]
|
||||||
|
(.registerTool mcp-server operation-id
|
||||||
|
#js {:title title
|
||||||
|
:description description
|
||||||
|
:inputSchema input-shape}
|
||||||
|
(fn [args]
|
||||||
|
(-> (call-semantic-operation! ctx operation (js->clj args :keywordize-keys true))
|
||||||
|
(p/then mcp-result)
|
||||||
|
(p/catch mcp-error-result))))))
|
||||||
|
|
||||||
|
(defn create-mcp-api-server
|
||||||
|
[ctx]
|
||||||
|
(let [mcp-server (McpServer. #js {:name "Logseq Daemon Semantic API" :version "1.0.0"})]
|
||||||
|
(doseq [operation semantic-routes/operations]
|
||||||
|
(register-operation-tool! mcp-server ctx operation))
|
||||||
|
mcp-server))
|
||||||
|
|
||||||
|
;; HTTP glue -- adapted from electron/mcp_server.cljs's Fastify-based version:
|
||||||
|
;; that one gets req/res already wrapped (`.raw` unwraps back to the
|
||||||
|
;; underlying Node primitives, `.body` is pre-parsed by Fastify's body
|
||||||
|
;; parser); the daemon calls this straight from its own raw
|
||||||
|
;; http.createServer callback, so req/res ARE already the Node primitives
|
||||||
|
;; StreamableHTTPServerTransport wants, and the body needs parsing here.
|
||||||
|
;; Auth is the daemon's existing bearer-token check on every route (see
|
||||||
|
;; db_worker_node.cljs's request-authorized?) -- already done by the time
|
||||||
|
;; this is reached, so unlike the CF-Worker/Electron versions there's no
|
||||||
|
;; separate auth check in here.
|
||||||
|
(defonce ^:private *transports (atom {}))
|
||||||
|
|
||||||
|
(defn- <read-body [^js req]
|
||||||
|
(p/create
|
||||||
|
(fn [resolve reject]
|
||||||
|
(let [chunks (array)]
|
||||||
|
(.on req "data" (fn [chunk] (.push chunks chunk)))
|
||||||
|
(.on req "end" (fn [] (resolve (.toString (js/Buffer.concat chunks) "utf8"))))
|
||||||
|
(.on req "error" reject)))))
|
||||||
|
|
||||||
|
(defn handle-post-request
|
||||||
|
[{:keys [host port]} ^js req ^js res]
|
||||||
|
(let [session-id (aget (.-headers req) "mcp-session-id")
|
||||||
|
authorization (.. req -headers -authorization)]
|
||||||
|
(p/let [body-text (<read-body req)
|
||||||
|
body (when-not (string/blank? body-text) (js/JSON.parse body-text))]
|
||||||
|
(cond
|
||||||
|
(and session-id (@*transports session-id))
|
||||||
|
(let [^js transport (@*transports session-id)]
|
||||||
|
(.handleRequest transport req res body))
|
||||||
|
|
||||||
|
(and (not session-id) (isInitializeRequest body))
|
||||||
|
(let [transport (StreamableHTTPServerTransport.
|
||||||
|
#js {:sessionIdGenerator (comp str random-uuid)
|
||||||
|
:enableDnsRebindingProtection true
|
||||||
|
:allowedHosts #js [(str host ":" port)]})
|
||||||
|
mcp-server (create-mcp-api-server {:host host :port port :authorization authorization})]
|
||||||
|
(set! (.-onclose transport)
|
||||||
|
(fn [] (swap! *transports dissoc (.-sessionId transport))))
|
||||||
|
(p/do!
|
||||||
|
(.connect mcp-server transport)
|
||||||
|
(.handleRequest transport req res body)
|
||||||
|
(if (.-sessionId transport)
|
||||||
|
(swap! *transports assoc (.-sessionId transport) transport)
|
||||||
|
(log/error :mcp-server-no-session-id-after-initialize nil))))
|
||||||
|
|
||||||
|
:else
|
||||||
|
(do
|
||||||
|
(.writeHead res 400 #js {"Content-Type" "application/json"})
|
||||||
|
(.end res (js/JSON.stringify
|
||||||
|
#js {:jsonrpc "2.0"
|
||||||
|
:error #js {:code -32000 :message "Bad Request: No valid session ID provided"}
|
||||||
|
:id nil})))))))
|
||||||
|
|
||||||
|
(defn handle-get-request
|
||||||
|
[^js req ^js res]
|
||||||
|
(let [session-id (aget (.-headers req) "mcp-session-id")]
|
||||||
|
(if-let [^js transport (and session-id (@*transports session-id))]
|
||||||
|
(.handleRequest transport req res)
|
||||||
|
(do (.writeHead res 400) (.end res "Invalid or missing session ID")))))
|
||||||
|
|
||||||
|
(defn handle-delete-request
|
||||||
|
[^js req ^js res]
|
||||||
|
(let [session-id (aget (.-headers req) "mcp-session-id")]
|
||||||
|
(if-let [^js transport (and session-id (@*transports session-id))]
|
||||||
|
(do (.close transport)
|
||||||
|
(.writeHead res 200 #js {"Content-Type" "application/json"})
|
||||||
|
(.end res (js/JSON.stringify #js {:ok true})))
|
||||||
|
(do (.writeHead res 400) (.end res "Invalid or missing session ID")))))
|
||||||
@@ -83,7 +83,10 @@
|
|||||||
{:method "GET" :path "/v1/semantic/search"
|
{:method "GET" :path "/v1/semantic/search"
|
||||||
:handler :semantic/search :operation-id "searchGraph" :success-status 200}])
|
:handler :semantic/search :operation-id "searchGraph" :success-status 200}])
|
||||||
|
|
||||||
(def ^:private operation-docs
|
(def operation-docs
|
||||||
|
"Public so worker.mcp-server can source MCP tool titles/descriptions from
|
||||||
|
the same place the OpenAPI document does, instead of a second copy that
|
||||||
|
could drift."
|
||||||
{"listPages" ["List pages" "Returns a cursor-paginated list of page blocks in the graph."]
|
{"listPages" ["List pages" "Returns a cursor-paginated list of page blocks in the graph."]
|
||||||
"createPage" ["Create a page" "Creates a page block with the supplied title."]
|
"createPage" ["Create a page" "Creates a page block with the supplied title."]
|
||||||
"listPageBlocks" ["List a page's blocks" "Returns a cursor-paginated list of the page's top-level blocks, including each selected block's descendant tree."]
|
"listPageBlocks" ["List a page's blocks" "Returns a cursor-paginated list of the page's top-level blocks, including each selected block's descendant tree."]
|
||||||
|
|||||||
@@ -1516,7 +1516,6 @@
|
|||||||
:select/new-option "+ Nuwe opsie: {1}"
|
:select/new-option "+ Nuwe opsie: {1}"
|
||||||
|
|
||||||
:server/error-notification "[Server] {1}"
|
:server/error-notification "[Server] {1}"
|
||||||
:server/mcp-url-copied "MCP-URL gekopieerd"
|
|
||||||
:server/start "Begin"
|
:server/start "Begin"
|
||||||
:server/stop "Stop"
|
:server/stop "Stop"
|
||||||
:server/title "HTTP API-server"
|
:server/title "HTTP API-server"
|
||||||
@@ -1543,7 +1542,6 @@
|
|||||||
|
|
||||||
:settings/account "Rekening"
|
:settings/account "Rekening"
|
||||||
:settings/advanced "Gevorderd"
|
:settings/advanced "Gevorderd"
|
||||||
:settings/ai "KI"
|
|
||||||
:settings/collaboration "Samewerking"
|
:settings/collaboration "Samewerking"
|
||||||
:settings/editor "Redigeerder"
|
:settings/editor "Redigeerder"
|
||||||
:settings/encryption "Enkripsie"
|
:settings/encryption "Enkripsie"
|
||||||
@@ -1564,8 +1562,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "Deaktiveer ongelukrapportering"
|
:settings.advanced/disable-sentry-desc "Deaktiveer ongelukrapportering"
|
||||||
:settings.advanced/network-proxy "Netwerkvolmag"
|
:settings.advanced/network-proxy "Netwerkvolmag"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "Aktiveer MCP-bediener"
|
|
||||||
:settings.ai/enable-mcp-server-desc "MCP-server aktiveer vir AI-integratie"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "Blokreferenties outomaties uitklappen"
|
:settings.editor/auto-expand-block-refs "Blokreferenties outomaties uitklappen"
|
||||||
:settings.editor/auto-expand-block-refs-tip "Klik om blokreferenties outomaties uit te klappen"
|
:settings.editor/auto-expand-block-refs-tip "Klik om blokreferenties outomaties uit te klappen"
|
||||||
|
|||||||
@@ -1516,7 +1516,6 @@
|
|||||||
:select/new-option "+ خيار جديد: {1}"
|
:select/new-option "+ خيار جديد: {1}"
|
||||||
|
|
||||||
:server/error-notification "[الخادم] {1}"
|
:server/error-notification "[الخادم] {1}"
|
||||||
:server/mcp-url-copied "تم نسخ رابط MCP"
|
|
||||||
:server/start "بدء"
|
:server/start "بدء"
|
||||||
:server/stop "إيقاف"
|
:server/stop "إيقاف"
|
||||||
:server/title "خادم HTTP API"
|
:server/title "خادم HTTP API"
|
||||||
@@ -1543,7 +1542,6 @@
|
|||||||
|
|
||||||
:settings/account "الحساب"
|
:settings/account "الحساب"
|
||||||
:settings/advanced "متقدم"
|
:settings/advanced "متقدم"
|
||||||
:settings/ai "الذكاء الاصطناعي"
|
|
||||||
:settings/collaboration "التعاون"
|
:settings/collaboration "التعاون"
|
||||||
:settings/editor "المحرر"
|
:settings/editor "المحرر"
|
||||||
:settings/encryption "التشفير"
|
:settings/encryption "التشفير"
|
||||||
@@ -1564,8 +1562,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "تعطيل تقارير الأعطال"
|
:settings.advanced/disable-sentry-desc "تعطيل تقارير الأعطال"
|
||||||
:settings.advanced/network-proxy "وكيل الشبكة"
|
:settings.advanced/network-proxy "وكيل الشبكة"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "تفعيل خادم MCP"
|
|
||||||
:settings.ai/enable-mcp-server-desc "تفعيل خادم MCP لتكامل الذكاء الاصطناعي"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "توسيع مراجع الكتل تلقائياً"
|
:settings.editor/auto-expand-block-refs "توسيع مراجع الكتل تلقائياً"
|
||||||
:settings.editor/auto-expand-block-refs-tip "انقر لتوسيع مراجع الكتل تلقائياً"
|
:settings.editor/auto-expand-block-refs-tip "انقر لتوسيع مراجع الكتل تلقائياً"
|
||||||
|
|||||||
@@ -1516,7 +1516,6 @@
|
|||||||
:select/new-option "+ Opció nova: {1}"
|
:select/new-option "+ Opció nova: {1}"
|
||||||
|
|
||||||
:server/error-notification "[Servidor] {1}"
|
:server/error-notification "[Servidor] {1}"
|
||||||
:server/mcp-url-copied "URL MCP copiada"
|
|
||||||
:server/start "Iniciar"
|
:server/start "Iniciar"
|
||||||
:server/stop "Aturar"
|
:server/stop "Aturar"
|
||||||
:server/title "Servidor d'API HTTP"
|
:server/title "Servidor d'API HTTP"
|
||||||
@@ -1543,7 +1542,6 @@
|
|||||||
|
|
||||||
:settings/account "Compte"
|
:settings/account "Compte"
|
||||||
:settings/advanced "Avançat"
|
:settings/advanced "Avançat"
|
||||||
:settings/ai "IA"
|
|
||||||
:settings/collaboration "Col·laboració"
|
:settings/collaboration "Col·laboració"
|
||||||
:settings/editor "Editor"
|
:settings/editor "Editor"
|
||||||
:settings/encryption "Encriptació"
|
:settings/encryption "Encriptació"
|
||||||
@@ -1564,8 +1562,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc " Logseq mai recol·lectarà la seva base de dades de grafs local o vendrà les seves dades."
|
:settings.advanced/disable-sentry-desc " Logseq mai recol·lectarà la seva base de dades de grafs local o vendrà les seves dades."
|
||||||
:settings.advanced/network-proxy "Proxy de xarxa"
|
:settings.advanced/network-proxy "Proxy de xarxa"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "Activar servidor MCP"
|
|
||||||
:settings.ai/enable-mcp-server-desc "Activar servidor MCP per integració amb IA"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "Expandir referències de blocs automàticament"
|
:settings.editor/auto-expand-block-refs "Expandir referències de blocs automàticament"
|
||||||
:settings.editor/auto-expand-block-refs-tip "Expandir referències de blocs automàticament en fer clic"
|
:settings.editor/auto-expand-block-refs-tip "Expandir referències de blocs automàticament en fer clic"
|
||||||
|
|||||||
@@ -1516,7 +1516,6 @@
|
|||||||
:select/new-option "+ Nová možnost: {1}"
|
:select/new-option "+ Nová možnost: {1}"
|
||||||
|
|
||||||
:server/error-notification "[Server] {1}"
|
:server/error-notification "[Server] {1}"
|
||||||
:server/mcp-url-copied "URL MCP zkopírováno"
|
|
||||||
:server/start "Spustit"
|
:server/start "Spustit"
|
||||||
:server/stop "Zastavit"
|
:server/stop "Zastavit"
|
||||||
:server/title "HTTP API server"
|
:server/title "HTTP API server"
|
||||||
@@ -1543,7 +1542,6 @@
|
|||||||
|
|
||||||
:settings/account "Účet"
|
:settings/account "Účet"
|
||||||
:settings/advanced "Pokročilé"
|
:settings/advanced "Pokročilé"
|
||||||
:settings/ai "AI"
|
|
||||||
:settings/collaboration "Spolupráce"
|
:settings/collaboration "Spolupráce"
|
||||||
:settings/editor "Editor"
|
:settings/editor "Editor"
|
||||||
:settings/encryption "Šifrování"
|
:settings/encryption "Šifrování"
|
||||||
@@ -1564,8 +1562,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "Společnost Logseq nikdy nebude shromažďovat vaši místní databázi grafů ani prodávat vaše data."
|
:settings.advanced/disable-sentry-desc "Společnost Logseq nikdy nebude shromažďovat vaši místní databázi grafů ani prodávat vaše data."
|
||||||
:settings.advanced/network-proxy "Síťový proxy server"
|
:settings.advanced/network-proxy "Síťový proxy server"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "Zapnout server MCP"
|
|
||||||
:settings.ai/enable-mcp-server-desc "Zapnout server MCP pro integraci s AI"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "Automaticky rozbalit odkazy na bloky"
|
:settings.editor/auto-expand-block-refs "Automaticky rozbalit odkazy na bloky"
|
||||||
:settings.editor/auto-expand-block-refs-tip "Automaticky rozbalit odkazy na bloky při kliknutí"
|
:settings.editor/auto-expand-block-refs-tip "Automaticky rozbalit odkazy na bloky při kliknutí"
|
||||||
|
|||||||
@@ -1516,7 +1516,6 @@
|
|||||||
:select/new-option "+ Neue Option: {1}"
|
:select/new-option "+ Neue Option: {1}"
|
||||||
|
|
||||||
:server/error-notification "[Server] {1}"
|
:server/error-notification "[Server] {1}"
|
||||||
:server/mcp-url-copied "MCP-URL kopiert"
|
|
||||||
:server/start "Starten"
|
:server/start "Starten"
|
||||||
:server/stop "Stoppen"
|
:server/stop "Stoppen"
|
||||||
:server/title "HTTP-API-Server"
|
:server/title "HTTP-API-Server"
|
||||||
@@ -1543,7 +1542,6 @@
|
|||||||
|
|
||||||
:settings/account "Konto"
|
:settings/account "Konto"
|
||||||
:settings/advanced "Erweitert"
|
:settings/advanced "Erweitert"
|
||||||
:settings/ai "KI"
|
|
||||||
:settings/collaboration "Zusammenarbeit"
|
:settings/collaboration "Zusammenarbeit"
|
||||||
:settings/editor "Editor"
|
:settings/editor "Editor"
|
||||||
:settings/encryption "Verschlüsselung"
|
:settings/encryption "Verschlüsselung"
|
||||||
@@ -1564,8 +1562,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "Logseq wird niemals Ihre lokale Graphdatenbank sammeln oder Ihre Daten verkaufen."
|
:settings.advanced/disable-sentry-desc "Logseq wird niemals Ihre lokale Graphdatenbank sammeln oder Ihre Daten verkaufen."
|
||||||
:settings.advanced/network-proxy "Netzwerk-Proxy"
|
:settings.advanced/network-proxy "Netzwerk-Proxy"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "MCP-Server aktivieren"
|
|
||||||
:settings.ai/enable-mcp-server-desc "MCP-Server für KI-Integration aktivieren"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "Automatisch beim Heranzoomen Blockreferenzen erweitern..."
|
:settings.editor/auto-expand-block-refs "Automatisch beim Heranzoomen Blockreferenzen erweitern..."
|
||||||
:settings.editor/auto-expand-block-refs-tip "Diese Option steuert, ob Blockreferenzen beim Heranzoomen automatisch ausgeklappt werden."
|
:settings.editor/auto-expand-block-refs-tip "Diese Option steuert, ob Blockreferenzen beim Heranzoomen automatisch ausgeklappt werden."
|
||||||
|
|||||||
@@ -1658,7 +1658,6 @@
|
|||||||
:select/new-option "+ New option: {1}"
|
:select/new-option "+ New option: {1}"
|
||||||
|
|
||||||
:server/error-notification "[Server] {1}"
|
:server/error-notification "[Server] {1}"
|
||||||
:server/mcp-url-copied "MCP URL copied to clipboard!"
|
|
||||||
:server/start "Start server"
|
:server/start "Start server"
|
||||||
:server/stop "Stop server"
|
:server/stop "Stop server"
|
||||||
:server/title "HTTP API server"
|
:server/title "HTTP API server"
|
||||||
@@ -1685,7 +1684,6 @@
|
|||||||
|
|
||||||
:settings/account "Account"
|
:settings/account "Account"
|
||||||
:settings/advanced "Advanced"
|
:settings/advanced "Advanced"
|
||||||
:settings/ai "AI"
|
|
||||||
:settings/collaboration "Collaboration"
|
:settings/collaboration "Collaboration"
|
||||||
:settings/editor "Editor"
|
:settings/editor "Editor"
|
||||||
:settings/encryption "Encryption"
|
:settings/encryption "Encryption"
|
||||||
@@ -1706,8 +1704,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "Logseq will never collect your local graph database or sell your data."
|
:settings.advanced/disable-sentry-desc "Logseq will never collect your local graph database or sell your data."
|
||||||
:settings.advanced/network-proxy "Network proxy"
|
:settings.advanced/network-proxy "Network proxy"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "MCP Server"
|
|
||||||
:settings.ai/enable-mcp-server-desc "Enable MCP Server to allow commmunication with local AI applications. MCP Server depends on HTTP API server."
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "Expand block references automatically when zoom-in"
|
:settings.editor/auto-expand-block-refs "Expand block references automatically when zoom-in"
|
||||||
:settings.editor/auto-expand-block-refs-tip "This option controls whether to expand the block references automatically when zoom-in."
|
:settings.editor/auto-expand-block-refs-tip "This option controls whether to expand the block references automatically when zoom-in."
|
||||||
|
|||||||
@@ -1516,7 +1516,6 @@
|
|||||||
:select/new-option "+ Nueva opción: {1}"
|
:select/new-option "+ Nueva opción: {1}"
|
||||||
|
|
||||||
:server/error-notification "[Servidor] {1}"
|
:server/error-notification "[Servidor] {1}"
|
||||||
:server/mcp-url-copied "URL MCP copiada"
|
|
||||||
:server/start "Iniciar"
|
:server/start "Iniciar"
|
||||||
:server/stop "Detener"
|
:server/stop "Detener"
|
||||||
:server/title "Servidor API HTTP"
|
:server/title "Servidor API HTTP"
|
||||||
@@ -1543,7 +1542,6 @@
|
|||||||
|
|
||||||
:settings/account "Cuenta"
|
:settings/account "Cuenta"
|
||||||
:settings/advanced "Avanzado"
|
:settings/advanced "Avanzado"
|
||||||
:settings/ai "IA"
|
|
||||||
:settings/collaboration "Colaboración"
|
:settings/collaboration "Colaboración"
|
||||||
:settings/editor "Editor"
|
:settings/editor "Editor"
|
||||||
:settings/encryption "Cifrado"
|
:settings/encryption "Cifrado"
|
||||||
@@ -1564,8 +1562,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc " Logseq nunca va a recolectar tu base de datos de grafos local o vender tus datos."
|
:settings.advanced/disable-sentry-desc " Logseq nunca va a recolectar tu base de datos de grafos local o vender tus datos."
|
||||||
:settings.advanced/network-proxy "Proxy de red"
|
:settings.advanced/network-proxy "Proxy de red"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "Habilitar servidor MCP"
|
|
||||||
:settings.ai/enable-mcp-server-desc "Habilitar servidor MCP para integración con IA"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "Expandir referencias de bloque automáticamente al hacer un acercamiento"
|
:settings.editor/auto-expand-block-refs "Expandir referencias de bloque automáticamente al hacer un acercamiento"
|
||||||
:settings.editor/auto-expand-block-refs-tip "Esta opción controla si expandir el bloque de referencias automáticamente al hacer un acercamiento."
|
:settings.editor/auto-expand-block-refs-tip "Esta opción controla si expandir el bloque de referencias automáticamente al hacer un acercamiento."
|
||||||
|
|||||||
@@ -1516,7 +1516,6 @@
|
|||||||
:select/new-option "+ گزینه جدید: {1}"
|
:select/new-option "+ گزینه جدید: {1}"
|
||||||
|
|
||||||
:server/error-notification "[سرور] {1}"
|
:server/error-notification "[سرور] {1}"
|
||||||
:server/mcp-url-copied "آدرس MCP کپی شد"
|
|
||||||
:server/start "شروع"
|
:server/start "شروع"
|
||||||
:server/stop "توقف"
|
:server/stop "توقف"
|
||||||
:server/title "سرور HTTP API"
|
:server/title "سرور HTTP API"
|
||||||
@@ -1543,7 +1542,6 @@
|
|||||||
|
|
||||||
:settings/account "حساب"
|
:settings/account "حساب"
|
||||||
:settings/advanced "پیشرفته"
|
:settings/advanced "پیشرفته"
|
||||||
:settings/ai "هوش مصنوعی"
|
|
||||||
:settings/collaboration "همکاری"
|
:settings/collaboration "همکاری"
|
||||||
:settings/editor "ویرایشگر"
|
:settings/editor "ویرایشگر"
|
||||||
:settings/encryption "رمزگذاری"
|
:settings/encryption "رمزگذاری"
|
||||||
@@ -1564,8 +1562,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "غیرفعالسازی گزارش خرابی"
|
:settings.advanced/disable-sentry-desc "غیرفعالسازی گزارش خرابی"
|
||||||
:settings.advanced/network-proxy "پروکسی شبکه"
|
:settings.advanced/network-proxy "پروکسی شبکه"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "فعالسازی سرور MCP"
|
|
||||||
:settings.ai/enable-mcp-server-desc "فعالسازی سرور MCP برای ادغام هوش مصنوعی"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "باز کردن خودکار مراجع بلوک"
|
:settings.editor/auto-expand-block-refs "باز کردن خودکار مراجع بلوک"
|
||||||
:settings.editor/auto-expand-block-refs-tip "برای باز کردن خودکار مراجع بلوک کلیک کنید"
|
:settings.editor/auto-expand-block-refs-tip "برای باز کردن خودکار مراجع بلوک کلیک کنید"
|
||||||
|
|||||||
@@ -1516,7 +1516,6 @@
|
|||||||
:select/new-option "+ Nouvelle option : {1}"
|
:select/new-option "+ Nouvelle option : {1}"
|
||||||
|
|
||||||
:server/error-notification "[Serveur] {1}"
|
:server/error-notification "[Serveur] {1}"
|
||||||
:server/mcp-url-copied "URL MCP copiée"
|
|
||||||
:server/start "Démarrer"
|
:server/start "Démarrer"
|
||||||
:server/stop "Arrêter"
|
:server/stop "Arrêter"
|
||||||
:server/title "Serveur API HTTP"
|
:server/title "Serveur API HTTP"
|
||||||
@@ -1543,7 +1542,6 @@
|
|||||||
|
|
||||||
:settings/account "Compte"
|
:settings/account "Compte"
|
||||||
:settings/advanced "Avancé"
|
:settings/advanced "Avancé"
|
||||||
:settings/ai "IA"
|
|
||||||
:settings/collaboration "Collaboration"
|
:settings/collaboration "Collaboration"
|
||||||
:settings/editor "Éditeur"
|
:settings/editor "Éditeur"
|
||||||
:settings/encryption "Chiffrement"
|
:settings/encryption "Chiffrement"
|
||||||
@@ -1564,8 +1562,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "Logseq ne collectera jamais votre base de données de graphes locale ni ne vendra vos données."
|
:settings.advanced/disable-sentry-desc "Logseq ne collectera jamais votre base de données de graphes locale ni ne vendra vos données."
|
||||||
:settings.advanced/network-proxy "Proxy réseau"
|
:settings.advanced/network-proxy "Proxy réseau"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "Activer le serveur MCP"
|
|
||||||
:settings.ai/enable-mcp-server-desc "Activer le serveur MCP pour l'intégration IA"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "Étendre automatiquement les références de bloc lors du zoom"
|
:settings.editor/auto-expand-block-refs "Étendre automatiquement les références de bloc lors du zoom"
|
||||||
:settings.editor/auto-expand-block-refs-tip "Cette option contrôle si les références de bloc sont développées automatiquement lors du zoom."
|
:settings.editor/auto-expand-block-refs-tip "Cette option contrôle si les références de bloc sont développées automatiquement lors du zoom."
|
||||||
|
|||||||
@@ -1516,7 +1516,6 @@
|
|||||||
:select/new-option "+ Opsi baru: {1}"
|
:select/new-option "+ Opsi baru: {1}"
|
||||||
|
|
||||||
:server/error-notification "[Server] {1}"
|
:server/error-notification "[Server] {1}"
|
||||||
:server/mcp-url-copied "URL MCP disalin"
|
|
||||||
:server/start "Mulai"
|
:server/start "Mulai"
|
||||||
:server/stop "Berhenti"
|
:server/stop "Berhenti"
|
||||||
:server/title "Server HTTP API"
|
:server/title "Server HTTP API"
|
||||||
@@ -1543,7 +1542,6 @@
|
|||||||
|
|
||||||
:settings/account "Akun"
|
:settings/account "Akun"
|
||||||
:settings/advanced "Lanjutan"
|
:settings/advanced "Lanjutan"
|
||||||
:settings/ai "AI"
|
|
||||||
:settings/collaboration "Kolaborasi"
|
:settings/collaboration "Kolaborasi"
|
||||||
:settings/editor "Editor"
|
:settings/editor "Editor"
|
||||||
:settings/encryption "Enkripsi"
|
:settings/encryption "Enkripsi"
|
||||||
@@ -1564,8 +1562,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "Logseq tidak akan pernah mengumpulkan database grafik lokal Anda atau menjual data Anda."
|
:settings.advanced/disable-sentry-desc "Logseq tidak akan pernah mengumpulkan database grafik lokal Anda atau menjual data Anda."
|
||||||
:settings.advanced/network-proxy "Proxy jaringan"
|
:settings.advanced/network-proxy "Proxy jaringan"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "Aktifkan server MCP"
|
|
||||||
:settings.ai/enable-mcp-server-desc "Aktifkan server MCP untuk integrasi AI"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "Perluas referensi blok secara otomatis saat zoom-in"
|
:settings.editor/auto-expand-block-refs "Perluas referensi blok secara otomatis saat zoom-in"
|
||||||
:settings.editor/auto-expand-block-refs-tip "Opsi ini mengontrol apakah referensi blok akan diperluas secara otomatis saat zoom-in."
|
:settings.editor/auto-expand-block-refs-tip "Opsi ini mengontrol apakah referensi blok akan diperluas secara otomatis saat zoom-in."
|
||||||
|
|||||||
@@ -1516,7 +1516,6 @@
|
|||||||
:select/new-option "+ Nuova opzione: {1}"
|
:select/new-option "+ Nuova opzione: {1}"
|
||||||
|
|
||||||
:server/error-notification "[Server] {1}"
|
:server/error-notification "[Server] {1}"
|
||||||
:server/mcp-url-copied "URL MCP copiato"
|
|
||||||
:server/start "Avvia"
|
:server/start "Avvia"
|
||||||
:server/stop "Ferma"
|
:server/stop "Ferma"
|
||||||
:server/title "Server API HTTP"
|
:server/title "Server API HTTP"
|
||||||
@@ -1543,7 +1542,6 @@
|
|||||||
|
|
||||||
:settings/account "Account"
|
:settings/account "Account"
|
||||||
:settings/advanced "Avanzate"
|
:settings/advanced "Avanzate"
|
||||||
:settings/ai "IA"
|
|
||||||
:settings/collaboration "Collaborazione"
|
:settings/collaboration "Collaborazione"
|
||||||
:settings/editor "Editor"
|
:settings/editor "Editor"
|
||||||
:settings/encryption "Crittografia"
|
:settings/encryption "Crittografia"
|
||||||
@@ -1564,8 +1562,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "Logseq non raccoglierà mai il database del grafo locale e non venderà mai i tuoi dati."
|
:settings.advanced/disable-sentry-desc "Logseq non raccoglierà mai il database del grafo locale e non venderà mai i tuoi dati."
|
||||||
:settings.advanced/network-proxy "Proxy di rete"
|
:settings.advanced/network-proxy "Proxy di rete"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "Abilita server MCP"
|
|
||||||
:settings.ai/enable-mcp-server-desc "Abilita il server MCP per l'integrazione AI"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "Espandi riferimenti ai blocchi automaticamente quando si ingrandisce"
|
:settings.editor/auto-expand-block-refs "Espandi riferimenti ai blocchi automaticamente quando si ingrandisce"
|
||||||
:settings.editor/auto-expand-block-refs-tip "Questa opzione controlla se espandere i riferimenti ai blocchi automaticamente quando si ingrandisce."
|
:settings.editor/auto-expand-block-refs-tip "Questa opzione controlla se espandere i riferimenti ai blocchi automaticamente quando si ingrandisce."
|
||||||
|
|||||||
@@ -1529,7 +1529,6 @@
|
|||||||
:select/new-option "+ 新しいオプション: {1}"
|
:select/new-option "+ 新しいオプション: {1}"
|
||||||
|
|
||||||
:server/error-notification "[サーバー] {1}"
|
:server/error-notification "[サーバー] {1}"
|
||||||
:server/mcp-url-copied "MCP URLをコピーしました"
|
|
||||||
:server/start "開始"
|
:server/start "開始"
|
||||||
:server/stop "停止"
|
:server/stop "停止"
|
||||||
:server/title "HTTP APIサーバー"
|
:server/title "HTTP APIサーバー"
|
||||||
@@ -1556,7 +1555,6 @@
|
|||||||
|
|
||||||
:settings/account "アカウント"
|
:settings/account "アカウント"
|
||||||
:settings/advanced "高度な設定"
|
:settings/advanced "高度な設定"
|
||||||
:settings/ai "AI"
|
|
||||||
:settings/collaboration "コラボレーション"
|
:settings/collaboration "コラボレーション"
|
||||||
:settings/editor "エディタ"
|
:settings/editor "エディタ"
|
||||||
:settings/encryption "暗号化"
|
:settings/encryption "暗号化"
|
||||||
@@ -1577,8 +1575,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "Logseqはあなたのローカルなグラフを決して取得しませんし、あなたのデータを売ることも決してありません。 "
|
:settings.advanced/disable-sentry-desc "Logseqはあなたのローカルなグラフを決して取得しませんし、あなたのデータを売ることも決してありません。 "
|
||||||
:settings.advanced/network-proxy "ネットワークプロキシ"
|
:settings.advanced/network-proxy "ネットワークプロキシ"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "MCPサーバーを有効化"
|
|
||||||
:settings.ai/enable-mcp-server-desc "MCPサーバーを有効にしてAI連携を可能にします"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "ブロック参照を自動展開"
|
:settings.editor/auto-expand-block-refs "ブロック参照を自動展開"
|
||||||
:settings.editor/auto-expand-block-refs-tip "このオプションは、ズームインしたときにブロック参照を展開するかどうかを指定します。"
|
:settings.editor/auto-expand-block-refs-tip "このオプションは、ズームインしたときにブロック参照を展開するかどうかを指定します。"
|
||||||
|
|||||||
@@ -1516,7 +1516,6 @@
|
|||||||
:select/new-option "+ 새 옵션: {1}"
|
:select/new-option "+ 새 옵션: {1}"
|
||||||
|
|
||||||
:server/error-notification "[서버] {1}"
|
:server/error-notification "[서버] {1}"
|
||||||
:server/mcp-url-copied "MCP URL이 복사되었습니다"
|
|
||||||
:server/start "시작"
|
:server/start "시작"
|
||||||
:server/stop "중지"
|
:server/stop "중지"
|
||||||
:server/title "HTTP API 서버"
|
:server/title "HTTP API 서버"
|
||||||
@@ -1543,7 +1542,6 @@
|
|||||||
|
|
||||||
:settings/account "계정"
|
:settings/account "계정"
|
||||||
:settings/advanced "고급"
|
:settings/advanced "고급"
|
||||||
:settings/ai "AI"
|
|
||||||
:settings/collaboration "협업"
|
:settings/collaboration "협업"
|
||||||
:settings/editor "편집기"
|
:settings/editor "편집기"
|
||||||
:settings/encryption "암호화"
|
:settings/encryption "암호화"
|
||||||
@@ -1564,8 +1562,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "Logseq는 사용자의 로컬 그래프 데이터베이스 정보를 수집하지 않습니다."
|
:settings.advanced/disable-sentry-desc "Logseq는 사용자의 로컬 그래프 데이터베이스 정보를 수집하지 않습니다."
|
||||||
:settings.advanced/network-proxy "네트워크 프록시"
|
:settings.advanced/network-proxy "네트워크 프록시"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "MCP 서버 활성화"
|
|
||||||
:settings.ai/enable-mcp-server-desc "MCP 서버를 활성화하여 AI 연동을 가능하게 합니다"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "블록 참조 자동 확장"
|
:settings.editor/auto-expand-block-refs "블록 참조 자동 확장"
|
||||||
:settings.editor/auto-expand-block-refs-tip "이 설정은 블록을 확대할 때 블록 참조를 자동으로 펼칠지를 결정합니다."
|
:settings.editor/auto-expand-block-refs-tip "이 설정은 블록을 확대할 때 블록 참조를 자동으로 펼칠지를 결정합니다."
|
||||||
|
|||||||
@@ -1516,7 +1516,6 @@
|
|||||||
:select/new-option "+ Nytt alternativ: {1}"
|
:select/new-option "+ Nytt alternativ: {1}"
|
||||||
|
|
||||||
:server/error-notification "[Server] {1}"
|
:server/error-notification "[Server] {1}"
|
||||||
:server/mcp-url-copied "MCP-URL kopiert"
|
|
||||||
:server/start "Start"
|
:server/start "Start"
|
||||||
:server/stop "Stopp"
|
:server/stop "Stopp"
|
||||||
:server/title "HTTP API-server"
|
:server/title "HTTP API-server"
|
||||||
@@ -1543,7 +1542,6 @@
|
|||||||
|
|
||||||
:settings/account "Konto"
|
:settings/account "Konto"
|
||||||
:settings/advanced "Avansert"
|
:settings/advanced "Avansert"
|
||||||
:settings/ai "AI"
|
|
||||||
:settings/collaboration "Samarbeid"
|
:settings/collaboration "Samarbeid"
|
||||||
:settings/editor "Redigering"
|
:settings/editor "Redigering"
|
||||||
:settings/encryption "Kryptering"
|
:settings/encryption "Kryptering"
|
||||||
@@ -1564,8 +1562,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "Deaktiver krasjrapportering"
|
:settings.advanced/disable-sentry-desc "Deaktiver krasjrapportering"
|
||||||
:settings.advanced/network-proxy "Nettverksproxy"
|
:settings.advanced/network-proxy "Nettverksproxy"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "Aktiver MCP-server"
|
|
||||||
:settings.ai/enable-mcp-server-desc "Aktiver MCP-server for AI-integrasjon"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "Utvid blokkreferanser automatisk"
|
:settings.editor/auto-expand-block-refs "Utvid blokkreferanser automatisk"
|
||||||
:settings.editor/auto-expand-block-refs-tip "Klikk for å utvide blokkreferanser automatisk"
|
:settings.editor/auto-expand-block-refs-tip "Klikk for å utvide blokkreferanser automatisk"
|
||||||
|
|||||||
@@ -1516,7 +1516,6 @@
|
|||||||
:select/new-option "+ Nieuwe optie: {1}"
|
:select/new-option "+ Nieuwe optie: {1}"
|
||||||
|
|
||||||
:server/error-notification "[Server] {1}"
|
:server/error-notification "[Server] {1}"
|
||||||
:server/mcp-url-copied "MCP-URL gekopieerd"
|
|
||||||
:server/start "Starten"
|
:server/start "Starten"
|
||||||
:server/stop "Stoppen"
|
:server/stop "Stoppen"
|
||||||
:server/title "HTTP API-server"
|
:server/title "HTTP API-server"
|
||||||
@@ -1543,7 +1542,6 @@
|
|||||||
|
|
||||||
:settings/account "Account"
|
:settings/account "Account"
|
||||||
:settings/advanced "Geavanceerd"
|
:settings/advanced "Geavanceerd"
|
||||||
:settings/ai "AI"
|
|
||||||
:settings/collaboration "Samenwerking"
|
:settings/collaboration "Samenwerking"
|
||||||
:settings/editor "Editor"
|
:settings/editor "Editor"
|
||||||
:settings/encryption "Versleuteling"
|
:settings/encryption "Versleuteling"
|
||||||
@@ -1564,8 +1562,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "Crashrapportage uitschakelen"
|
:settings.advanced/disable-sentry-desc "Crashrapportage uitschakelen"
|
||||||
:settings.advanced/network-proxy "Netwerk proxy"
|
:settings.advanced/network-proxy "Netwerk proxy"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "MCP-server inschakelen"
|
|
||||||
:settings.ai/enable-mcp-server-desc "MCP-server inschakelen voor AI-integratie"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "Blokreferenties automatisch uitklappen"
|
:settings.editor/auto-expand-block-refs "Blokreferenties automatisch uitklappen"
|
||||||
:settings.editor/auto-expand-block-refs-tip "Klik om blokreferenties automatisch uit te klappen"
|
:settings.editor/auto-expand-block-refs-tip "Klik om blokreferenties automatisch uit te klappen"
|
||||||
|
|||||||
@@ -1516,7 +1516,6 @@
|
|||||||
:select/new-option "+ Nowa opcja: {1}"
|
:select/new-option "+ Nowa opcja: {1}"
|
||||||
|
|
||||||
:server/error-notification "[Serwer] {1}"
|
:server/error-notification "[Serwer] {1}"
|
||||||
:server/mcp-url-copied "URL MCP skopiowany"
|
|
||||||
:server/start "Uruchom"
|
:server/start "Uruchom"
|
||||||
:server/stop "Zatrzymaj"
|
:server/stop "Zatrzymaj"
|
||||||
:server/title "Serwer HTTP API"
|
:server/title "Serwer HTTP API"
|
||||||
@@ -1543,7 +1542,6 @@
|
|||||||
|
|
||||||
:settings/account "Konto"
|
:settings/account "Konto"
|
||||||
:settings/advanced "Zaawansowane"
|
:settings/advanced "Zaawansowane"
|
||||||
:settings/ai "AI"
|
|
||||||
:settings/collaboration "Współpraca"
|
:settings/collaboration "Współpraca"
|
||||||
:settings/editor "Edytor"
|
:settings/editor "Edytor"
|
||||||
:settings/encryption "Szyfrowanie"
|
:settings/encryption "Szyfrowanie"
|
||||||
@@ -1564,8 +1562,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "Logseq nigdy nie zbiera danych Twoich lokalnych grafów i nie sprzedaje twoich danych."
|
:settings.advanced/disable-sentry-desc "Logseq nigdy nie zbiera danych Twoich lokalnych grafów i nie sprzedaje twoich danych."
|
||||||
:settings.advanced/network-proxy "Ustawienia proxy"
|
:settings.advanced/network-proxy "Ustawienia proxy"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "Włącz serwer MCP"
|
|
||||||
:settings.ai/enable-mcp-server-desc "Włącz serwer MCP do integracji z AI"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "Automatycznie rozwijaj odniesienia do bloków"
|
:settings.editor/auto-expand-block-refs "Automatycznie rozwijaj odniesienia do bloków"
|
||||||
:settings.editor/auto-expand-block-refs-tip "Automatycznie rozwijaj odniesienia do bloków po kliknięciu"
|
:settings.editor/auto-expand-block-refs-tip "Automatycznie rozwijaj odniesienia do bloków po kliknięciu"
|
||||||
|
|||||||
@@ -1516,7 +1516,6 @@
|
|||||||
:select/new-option "+ Nova opção: {1}"
|
:select/new-option "+ Nova opção: {1}"
|
||||||
|
|
||||||
:server/error-notification "[Servidor] {1}"
|
:server/error-notification "[Servidor] {1}"
|
||||||
:server/mcp-url-copied "URL MCP copiada"
|
|
||||||
:server/start "Iniciar"
|
:server/start "Iniciar"
|
||||||
:server/stop "Parar"
|
:server/stop "Parar"
|
||||||
:server/title "Servidor de API HTTP"
|
:server/title "Servidor de API HTTP"
|
||||||
@@ -1543,7 +1542,6 @@
|
|||||||
|
|
||||||
:settings/account "Conta"
|
:settings/account "Conta"
|
||||||
:settings/advanced "Avançado"
|
:settings/advanced "Avançado"
|
||||||
:settings/ai "IA"
|
|
||||||
:settings/collaboration "Colaboração"
|
:settings/collaboration "Colaboração"
|
||||||
:settings/editor "Editor"
|
:settings/editor "Editor"
|
||||||
:settings/encryption "Criptografia"
|
:settings/encryption "Criptografia"
|
||||||
@@ -1564,8 +1562,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "O Logseq nunca coletará o banco de dados do seu grafo local nem venderá seus dados."
|
:settings.advanced/disable-sentry-desc "O Logseq nunca coletará o banco de dados do seu grafo local nem venderá seus dados."
|
||||||
:settings.advanced/network-proxy "Proxy de rede"
|
:settings.advanced/network-proxy "Proxy de rede"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "Habilitar servidor MCP"
|
|
||||||
:settings.ai/enable-mcp-server-desc "Habilitar servidor MCP para integração com IA"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "Expandir automaticamente as referências de bloco ao dar zoom"
|
:settings.editor/auto-expand-block-refs "Expandir automaticamente as referências de bloco ao dar zoom"
|
||||||
:settings.editor/auto-expand-block-refs-tip "Esta opção controla se as referências de bloco são expandidas automaticamente ao dar zoom."
|
:settings.editor/auto-expand-block-refs-tip "Esta opção controla se as referências de bloco são expandidas automaticamente ao dar zoom."
|
||||||
|
|||||||
@@ -1516,7 +1516,6 @@
|
|||||||
:select/new-option "+ Nova opção: {1}"
|
:select/new-option "+ Nova opção: {1}"
|
||||||
|
|
||||||
:server/error-notification "[Servidor] {1}"
|
:server/error-notification "[Servidor] {1}"
|
||||||
:server/mcp-url-copied "URL MCP copiada"
|
|
||||||
:server/start "Iniciar"
|
:server/start "Iniciar"
|
||||||
:server/stop "Parar"
|
:server/stop "Parar"
|
||||||
:server/title "Servidor de API HTTP"
|
:server/title "Servidor de API HTTP"
|
||||||
@@ -1543,7 +1542,6 @@
|
|||||||
|
|
||||||
:settings/account "Conta"
|
:settings/account "Conta"
|
||||||
:settings/advanced "Avançado"
|
:settings/advanced "Avançado"
|
||||||
:settings/ai "IA"
|
|
||||||
:settings/collaboration "Colaboração"
|
:settings/collaboration "Colaboração"
|
||||||
:settings/editor "Editor"
|
:settings/editor "Editor"
|
||||||
:settings/encryption "Encriptação"
|
:settings/encryption "Encriptação"
|
||||||
@@ -1564,8 +1562,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "A Logseq nunca irá recolher informações sobre a sua base de dados de grafos local ou vender os seus dados."
|
:settings.advanced/disable-sentry-desc "A Logseq nunca irá recolher informações sobre a sua base de dados de grafos local ou vender os seus dados."
|
||||||
:settings.advanced/network-proxy "Proxy de rede"
|
:settings.advanced/network-proxy "Proxy de rede"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "Ativar servidor MCP"
|
|
||||||
:settings.ai/enable-mcp-server-desc "Ativar servidor MCP para integração com IA"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "Expandir referências de blocos automaticamente"
|
:settings.editor/auto-expand-block-refs "Expandir referências de blocos automaticamente"
|
||||||
:settings.editor/auto-expand-block-refs-tip "Expandir referências de blocos automaticamente ao clicar"
|
:settings.editor/auto-expand-block-refs-tip "Expandir referências de blocos automaticamente ao clicar"
|
||||||
|
|||||||
@@ -1516,7 +1516,6 @@
|
|||||||
:select/new-option "+ Новая опция: {1}"
|
:select/new-option "+ Новая опция: {1}"
|
||||||
|
|
||||||
:server/error-notification "[Сервер] {1}"
|
:server/error-notification "[Сервер] {1}"
|
||||||
:server/mcp-url-copied "URL MCP скопирован"
|
|
||||||
:server/start "Запустить"
|
:server/start "Запустить"
|
||||||
:server/stop "Остановить"
|
:server/stop "Остановить"
|
||||||
:server/title "HTTP API сервер"
|
:server/title "HTTP API сервер"
|
||||||
@@ -1543,7 +1542,6 @@
|
|||||||
|
|
||||||
:settings/account "Аккаунт"
|
:settings/account "Аккаунт"
|
||||||
:settings/advanced "Продвинутые"
|
:settings/advanced "Продвинутые"
|
||||||
:settings/ai "ИИ"
|
|
||||||
:settings/collaboration "Совместная работа"
|
:settings/collaboration "Совместная работа"
|
||||||
:settings/editor "Редактор"
|
:settings/editor "Редактор"
|
||||||
:settings/encryption "Шифрование"
|
:settings/encryption "Шифрование"
|
||||||
@@ -1564,8 +1562,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "Logseq никогда не будет собирать вашу локальную базу данных графов или продавать ваши данные"
|
:settings.advanced/disable-sentry-desc "Logseq никогда не будет собирать вашу локальную базу данных графов или продавать ваши данные"
|
||||||
:settings.advanced/network-proxy "Прокси-сервер"
|
:settings.advanced/network-proxy "Прокси-сервер"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "Включить сервер MCP"
|
|
||||||
:settings.ai/enable-mcp-server-desc "Включить сервер MCP для интеграции с ИИ"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "Автораскрытие ссылок на блоки"
|
:settings.editor/auto-expand-block-refs "Автораскрытие ссылок на блоки"
|
||||||
:settings.editor/auto-expand-block-refs-tip "Автоматически раскрывать ссылки на блоки при нажатии"
|
:settings.editor/auto-expand-block-refs-tip "Автоматически раскрывать ссылки на блоки при нажатии"
|
||||||
|
|||||||
@@ -1516,7 +1516,6 @@
|
|||||||
:select/new-option "+ Nová možnosť: {1}"
|
:select/new-option "+ Nová možnosť: {1}"
|
||||||
|
|
||||||
:server/error-notification "[Server] {1}"
|
:server/error-notification "[Server] {1}"
|
||||||
:server/mcp-url-copied "URL MCP skopírované"
|
|
||||||
:server/start "Spustiť"
|
:server/start "Spustiť"
|
||||||
:server/stop "Zastaviť"
|
:server/stop "Zastaviť"
|
||||||
:server/title "HTTP API server"
|
:server/title "HTTP API server"
|
||||||
@@ -1543,7 +1542,6 @@
|
|||||||
|
|
||||||
:settings/account "Účet"
|
:settings/account "Účet"
|
||||||
:settings/advanced "Pokročilé"
|
:settings/advanced "Pokročilé"
|
||||||
:settings/ai "AI"
|
|
||||||
:settings/collaboration "Spolupráca"
|
:settings/collaboration "Spolupráca"
|
||||||
:settings/editor "Editor"
|
:settings/editor "Editor"
|
||||||
:settings/encryption "Šifrovanie"
|
:settings/encryption "Šifrovanie"
|
||||||
@@ -1564,8 +1562,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "Logseq nebude nikdy zhromažďovať vašu lokálnu databázu grafov ani predávať vaše údaje."
|
:settings.advanced/disable-sentry-desc "Logseq nebude nikdy zhromažďovať vašu lokálnu databázu grafov ani predávať vaše údaje."
|
||||||
:settings.advanced/network-proxy "Sieťová proxy"
|
:settings.advanced/network-proxy "Sieťová proxy"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "Zapnúť server MCP"
|
|
||||||
:settings.ai/enable-mcp-server-desc "Zapnout server MCP pro integraci s AI"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "Automaticky rozbalit odkazy na bloky"
|
:settings.editor/auto-expand-block-refs "Automaticky rozbalit odkazy na bloky"
|
||||||
:settings.editor/auto-expand-block-refs-tip "Automaticky rozbalit odkazy na bloky při kliknutí"
|
:settings.editor/auto-expand-block-refs-tip "Automaticky rozbalit odkazy na bloky při kliknutí"
|
||||||
|
|||||||
@@ -1516,7 +1516,6 @@
|
|||||||
:select/new-option "+ Yeni seçenek: {1}"
|
:select/new-option "+ Yeni seçenek: {1}"
|
||||||
|
|
||||||
:server/error-notification "[Sunucu] {1}"
|
:server/error-notification "[Sunucu] {1}"
|
||||||
:server/mcp-url-copied "MCP URL'si kopyalandı"
|
|
||||||
:server/start "Başlat"
|
:server/start "Başlat"
|
||||||
:server/stop "Durdur"
|
:server/stop "Durdur"
|
||||||
:server/title "HTTP API sunucusu"
|
:server/title "HTTP API sunucusu"
|
||||||
@@ -1543,7 +1542,6 @@
|
|||||||
|
|
||||||
:settings/account "Hesap"
|
:settings/account "Hesap"
|
||||||
:settings/advanced "Gelişmiş"
|
:settings/advanced "Gelişmiş"
|
||||||
:settings/ai "AI"
|
|
||||||
:settings/collaboration "İş birliği"
|
:settings/collaboration "İş birliği"
|
||||||
:settings/editor "Editör"
|
:settings/editor "Editör"
|
||||||
:settings/encryption "Şifreleme"
|
:settings/encryption "Şifreleme"
|
||||||
@@ -1564,8 +1562,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "Logseq asla yerel graf veritabanınızı toplamayacak veya verilerinizi satmayacaktır."
|
:settings.advanced/disable-sentry-desc "Logseq asla yerel graf veritabanınızı toplamayacak veya verilerinizi satmayacaktır."
|
||||||
:settings.advanced/network-proxy "Ağ ara sunucusu"
|
:settings.advanced/network-proxy "Ağ ara sunucusu"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "MCP sunucusunu etkinleştir"
|
|
||||||
:settings.ai/enable-mcp-server-desc "AI entegrasyonu için MCP sunucusunu etkinleştir"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "Blok referanslarını otomatik genişlet"
|
:settings.editor/auto-expand-block-refs "Blok referanslarını otomatik genişlet"
|
||||||
:settings.editor/auto-expand-block-refs-tip "Tıklandığında blok referanslarını otomatik genişlet"
|
:settings.editor/auto-expand-block-refs-tip "Tıklandığında blok referanslarını otomatik genişlet"
|
||||||
|
|||||||
@@ -1516,7 +1516,6 @@
|
|||||||
:select/new-option "+ Нова опція: {1}"
|
:select/new-option "+ Нова опція: {1}"
|
||||||
|
|
||||||
:server/error-notification "[Сервер] {1}"
|
:server/error-notification "[Сервер] {1}"
|
||||||
:server/mcp-url-copied "URL MCP скопирован"
|
|
||||||
:server/start "Запустити"
|
:server/start "Запустити"
|
||||||
:server/stop "Зупинити"
|
:server/stop "Зупинити"
|
||||||
:server/title "HTTP API сервер"
|
:server/title "HTTP API сервер"
|
||||||
@@ -1543,7 +1542,6 @@
|
|||||||
|
|
||||||
:settings/account "Обліковий запис"
|
:settings/account "Обліковий запис"
|
||||||
:settings/advanced "Розширення"
|
:settings/advanced "Розширення"
|
||||||
:settings/ai "ШІ"
|
|
||||||
:settings/collaboration "Спільна робота"
|
:settings/collaboration "Спільна робота"
|
||||||
:settings/editor "Редактор"
|
:settings/editor "Редактор"
|
||||||
:settings/encryption "Шифрування"
|
:settings/encryption "Шифрування"
|
||||||
@@ -1564,8 +1562,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "Logseq ніколи не збиратиме вашу локальну базу даних графів і не відправлятиме ваші дані."
|
:settings.advanced/disable-sentry-desc "Logseq ніколи не збиратиме вашу локальну базу даних графів і не відправлятиме ваші дані."
|
||||||
:settings.advanced/network-proxy "Мережевий проксі"
|
:settings.advanced/network-proxy "Мережевий проксі"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "Увімкнути сервер MCP"
|
|
||||||
:settings.ai/enable-mcp-server-desc "Увімкнути сервер MCP для интеграции с ИИ"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "Автораскрытие ссылок на блоки"
|
:settings.editor/auto-expand-block-refs "Автораскрытие ссылок на блоки"
|
||||||
:settings.editor/auto-expand-block-refs-tip "Автоматически раскрывать ссылки на блоки при нажатии"
|
:settings.editor/auto-expand-block-refs-tip "Автоматически раскрывать ссылки на блоки при нажатии"
|
||||||
|
|||||||
@@ -1658,7 +1658,6 @@
|
|||||||
:select/new-option "+ 新建选项:{1}"
|
:select/new-option "+ 新建选项:{1}"
|
||||||
|
|
||||||
:server/error-notification "[服务器] {1}"
|
:server/error-notification "[服务器] {1}"
|
||||||
:server/mcp-url-copied "MCP URL 已复制到剪贴板!"
|
|
||||||
:server/start "启动服务器"
|
:server/start "启动服务器"
|
||||||
:server/stop "停止服务器"
|
:server/stop "停止服务器"
|
||||||
:server/title "HTTP API 服务器"
|
:server/title "HTTP API 服务器"
|
||||||
@@ -1685,7 +1684,6 @@
|
|||||||
|
|
||||||
:settings/account "账户"
|
:settings/account "账户"
|
||||||
:settings/advanced "高级"
|
:settings/advanced "高级"
|
||||||
:settings/ai "AI"
|
|
||||||
:settings/collaboration "协作"
|
:settings/collaboration "协作"
|
||||||
:settings/editor "编辑器"
|
:settings/editor "编辑器"
|
||||||
:settings/encryption "加密"
|
:settings/encryption "加密"
|
||||||
@@ -1706,8 +1704,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "Logseq 从来不会收集或出售你的图谱数据。"
|
:settings.advanced/disable-sentry-desc "Logseq 从来不会收集或出售你的图谱数据。"
|
||||||
:settings.advanced/network-proxy "网络代理"
|
:settings.advanced/network-proxy "网络代理"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "MCP 服务器"
|
|
||||||
:settings.ai/enable-mcp-server-desc "启用 MCP 服务器,允许与本地 AI 应用通信"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "自动展开聚焦块的引用"
|
:settings.editor/auto-expand-block-refs "自动展开聚焦块的引用"
|
||||||
:settings.editor/auto-expand-block-refs-tip "此选项控制是否在页面加载时展开块引用"
|
:settings.editor/auto-expand-block-refs-tip "此选项控制是否在页面加载时展开块引用"
|
||||||
|
|||||||
@@ -1529,7 +1529,6 @@
|
|||||||
:select/new-option "+ 新選項: {1}"
|
:select/new-option "+ 新選項: {1}"
|
||||||
|
|
||||||
:server/error-notification "[伺服器] {1}"
|
:server/error-notification "[伺服器] {1}"
|
||||||
:server/mcp-url-copied "MCP URL 已複製到剪貼簿!"
|
|
||||||
:server/start "啟動伺服器"
|
:server/start "啟動伺服器"
|
||||||
:server/stop "停止伺服器"
|
:server/stop "停止伺服器"
|
||||||
:server/title "HTTP API 伺服器"
|
:server/title "HTTP API 伺服器"
|
||||||
@@ -1556,7 +1555,6 @@
|
|||||||
|
|
||||||
:settings/account "帳戶"
|
:settings/account "帳戶"
|
||||||
:settings/advanced "進階功能"
|
:settings/advanced "進階功能"
|
||||||
:settings/ai "AI"
|
|
||||||
:settings/collaboration "協作"
|
:settings/collaboration "協作"
|
||||||
:settings/editor "編輯器"
|
:settings/editor "編輯器"
|
||||||
:settings/encryption "加密"
|
:settings/encryption "加密"
|
||||||
@@ -1577,8 +1575,6 @@
|
|||||||
:settings.advanced/disable-sentry-desc "Logseq 永遠不會收集你的圖譜資料或出售你的數據。"
|
:settings.advanced/disable-sentry-desc "Logseq 永遠不會收集你的圖譜資料或出售你的數據。"
|
||||||
:settings.advanced/network-proxy "網路代理"
|
:settings.advanced/network-proxy "網路代理"
|
||||||
|
|
||||||
:settings.ai/enable-mcp-server "MCP 伺服器"
|
|
||||||
:settings.ai/enable-mcp-server-desc "啟用 MCP 伺服器,允許與本地 AI 應用程式通訊"
|
|
||||||
|
|
||||||
:settings.editor/auto-expand-block-refs "自動展開聚焦 block 的引用"
|
:settings.editor/auto-expand-block-refs "自動展開聚焦 block 的引用"
|
||||||
:settings.editor/auto-expand-block-refs-tip "此選項控制是否在頁面載入時展開區塊引用"
|
:settings.editor/auto-expand-block-refs-tip "此選項控制是否在頁面載入時展開區塊引用"
|
||||||
|
|||||||
120
src/test/frontend/worker/mcp_server_test.cljs
Normal file
120
src/test/frontend/worker/mcp_server_test.cljs
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
(ns frontend.worker.mcp-server-test
|
||||||
|
"Exercises the daemon's /mcp endpoint (Fase 9) with a real MCP client SDK
|
||||||
|
connection over HTTP against a live daemon -- the same live-daemon
|
||||||
|
pattern db_worker_node_test.cljs and semantic_test.cljs use."
|
||||||
|
(:require ["@modelcontextprotocol/sdk/client/index.js" :refer [Client]]
|
||||||
|
["@modelcontextprotocol/sdk/client/streamableHttp.js" :refer [StreamableHTTPClientTransport]]
|
||||||
|
[cljs.test :refer [async deftest is]]
|
||||||
|
[frontend.test.node-helper :as node-helper]
|
||||||
|
[frontend.worker.db-worker-node :as db-worker-node]
|
||||||
|
[frontend.worker.semantic-routes :as semantic-routes]
|
||||||
|
[promesa.core :as p]))
|
||||||
|
|
||||||
|
;; Same literal credential db_worker_node_test.cljs and semantic_test.cljs
|
||||||
|
;; bootstrap with -- see semantic_test.cljs for why this must not be a
|
||||||
|
;; separate value (all three namespaces share one test.js process).
|
||||||
|
(def ^:private test-daemon-username "db-worker-node-test-user")
|
||||||
|
(def ^:private test-daemon-password "db-worker-node-test-password")
|
||||||
|
|
||||||
|
(aset (.-env js/process) "LOGSEQ_DAEMON_USERNAME" test-daemon-username)
|
||||||
|
(aset (.-env js/process) "LOGSEQ_DAEMON_PASSWORD" test-daemon-password)
|
||||||
|
|
||||||
|
(defn- start-daemon!
|
||||||
|
[opts]
|
||||||
|
(db-worker-node/start-daemon! (update opts :log-level #(or % "error"))))
|
||||||
|
|
||||||
|
(defn- <token!
|
||||||
|
[host port]
|
||||||
|
(p/let [^js res (js/fetch (str "http://" host ":" port "/v1/auth/login")
|
||||||
|
#js {:method "POST"
|
||||||
|
:headers #js {"Content-Type" "application/json"}
|
||||||
|
:body (js/JSON.stringify
|
||||||
|
#js {:username test-daemon-username
|
||||||
|
:password test-daemon-password
|
||||||
|
:deviceLabel "mcp-test"})})
|
||||||
|
^js body (.json res)]
|
||||||
|
(.-token body)))
|
||||||
|
|
||||||
|
(defn- <connected-client!
|
||||||
|
^js
|
||||||
|
[host port token]
|
||||||
|
(p/let [transport (StreamableHTTPClientTransport.
|
||||||
|
(js/URL. (str "http://" host ":" port "/mcp"))
|
||||||
|
#js {:requestInit #js {:headers #js {"Authorization" (str "Bearer " token)}}})
|
||||||
|
^js client (Client. #js {:name "semantic-mcp-test" :version "1.0.0"})
|
||||||
|
_ (.connect client transport)]
|
||||||
|
client))
|
||||||
|
|
||||||
|
(defn- tool-text
|
||||||
|
[^js call-result]
|
||||||
|
(.-text (aget (.-content call-result) 0)))
|
||||||
|
|
||||||
|
(defn- with-mcp-client!
|
||||||
|
"Starts a fresh daemon over a fresh repo, connects an MCP client to /mcp,
|
||||||
|
runs (f client), and stops the daemon afterwards regardless of outcome."
|
||||||
|
[f]
|
||||||
|
(let [daemon (atom nil)
|
||||||
|
data-dir (node-helper/create-tmp-dir "mcp-server")
|
||||||
|
repo (str "logseq_db_mcp_" (subs (str (random-uuid)) 0 8))]
|
||||||
|
(-> (p/let [{:keys [host port stop!]} (start-daemon! {:root-dir data-dir :repo repo})
|
||||||
|
_ (reset! daemon {:stop! stop!})
|
||||||
|
token (<token! host port)
|
||||||
|
client (<connected-client! host port token)]
|
||||||
|
(f client))
|
||||||
|
(p/catch (fn [e]
|
||||||
|
(println "[mcp-server-test] error:" e)
|
||||||
|
(is false (str e))))
|
||||||
|
(p/finally (fn []
|
||||||
|
(when-let [stop! (:stop! @daemon)]
|
||||||
|
(stop!)))))))
|
||||||
|
|
||||||
|
(deftest mcp-lists-all-semantic-tools
|
||||||
|
(async done
|
||||||
|
(-> (with-mcp-client!
|
||||||
|
(fn [^js client]
|
||||||
|
(p/let [^js result (.listTools client)]
|
||||||
|
(let [tools (js->clj (.-tools result) :keywordize-keys true)]
|
||||||
|
(is (= (count semantic-routes/operations) (count tools)))
|
||||||
|
(is (some #(= "createPage" (:name %)) tools))
|
||||||
|
(is (some #(= "searchGraph" (:name %)) tools))
|
||||||
|
(is (not (some #(= "listGraphs" (:name %)) tools)))))))
|
||||||
|
(p/finally done))))
|
||||||
|
|
||||||
|
(deftest mcp-create-and-get-page-round-trip
|
||||||
|
(async done
|
||||||
|
(-> (with-mcp-client!
|
||||||
|
(fn [^js client]
|
||||||
|
(p/let [^js create-result (.callTool client #js {:name "createPage"
|
||||||
|
:arguments #js {:title "MCP Test Page"}})
|
||||||
|
page (js->clj (js/JSON.parse (tool-text create-result)) :keywordize-keys true)
|
||||||
|
^js get-result (.callTool client #js {:name "getPage"
|
||||||
|
:arguments #js {:page-id (:uuid page)}})
|
||||||
|
got (js->clj (js/JSON.parse (tool-text get-result)) :keywordize-keys true)]
|
||||||
|
(is (not (.-isError create-result)))
|
||||||
|
(is (= "MCP Test Page" (:title page)))
|
||||||
|
(is (= "MCP Test Page" (:title got))))))
|
||||||
|
(p/finally done))))
|
||||||
|
|
||||||
|
(deftest mcp-capture-block-tree-round-trip
|
||||||
|
(async done
|
||||||
|
(-> (with-mcp-client!
|
||||||
|
(fn [^js client]
|
||||||
|
(p/let [^js result (.callTool
|
||||||
|
client #js {:name "captureToToday"
|
||||||
|
:arguments #js {:blocks #js [#js {:title "MCP root"
|
||||||
|
:children #js [#js {:title "MCP child"}]}]}})
|
||||||
|
body (js->clj (js/JSON.parse (tool-text result)) :keywordize-keys true)
|
||||||
|
root (first (:blocks body))]
|
||||||
|
(is (not (.-isError result)))
|
||||||
|
(is (= "MCP root" (:title root)))
|
||||||
|
(is (= "MCP child" (:title (first (:children root))))))))
|
||||||
|
(p/finally done))))
|
||||||
|
|
||||||
|
(deftest mcp-unknown-page-surfaces-as-error-content
|
||||||
|
(async done
|
||||||
|
(-> (with-mcp-client!
|
||||||
|
(fn [^js client]
|
||||||
|
(p/let [^js result (.callTool client #js {:name "getPage"
|
||||||
|
:arguments #js {:page-id (str (random-uuid))}})]
|
||||||
|
(is (true? (.-isError result))))))
|
||||||
|
(p/finally done))))
|
||||||
Reference in New Issue
Block a user