ocean-brain 0.8.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +115 -0
- package/dist/mcp.js +205 -294
- package/package.json +7 -6
- package/server/client/dist/assets/ArrowRight.es-Bm-ZXpEC.js +1 -0
- package/server/client/dist/assets/Calendar-BsNs3NzO.js +25 -0
- package/server/client/dist/assets/Callout-CviyPHKO.js +1 -0
- package/server/client/dist/assets/Copy.es-BPreRnP9.js +1 -0
- package/server/client/dist/assets/Graph-vXQQ0a-Q.js +1 -0
- package/server/client/dist/assets/Graph-yPE3ljW0.js +46 -0
- package/server/client/dist/assets/Image.es-DnR3qc_c.js +1 -0
- package/server/client/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/server/client/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/server/client/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/server/client/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/server/client/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/server/client/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/server/client/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/server/client/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/server/client/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/server/client/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/server/client/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/server/client/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/server/client/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/server/client/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/server/client/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/server/client/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/server/client/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/server/client/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/server/client/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/server/client/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/server/client/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/server/client/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/server/client/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/server/client/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/server/client/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/server/client/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/server/client/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/server/client/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/server/client/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/server/client/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/server/client/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/server/client/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/server/client/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/server/client/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/server/client/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/server/client/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/server/client/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/server/client/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/server/client/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/server/client/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/server/client/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/server/client/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/server/client/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/server/client/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/server/client/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/server/client/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/server/client/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/server/client/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/server/client/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/server/client/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/server/client/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/server/client/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/server/client/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/server/client/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/server/client/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/server/client/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/server/client/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/server/client/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/server/client/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/server/client/dist/assets/Note-CmreVzvQ.js +25 -0
- package/server/client/dist/assets/NotePropertiesPanel-DXYgE8h2.js +2 -0
- package/server/client/dist/assets/PencilLine.es-B9V6u64t.js +1 -0
- package/server/client/dist/assets/Plus.es-CRRp2u5J.js +1 -0
- package/server/client/dist/assets/Reminders-BOtfNeYD.js +1 -0
- package/server/client/dist/assets/Search-C-FB3pKf.js +1 -0
- package/server/client/dist/assets/SurfaceCard-GMt5xM7v.js +1 -0
- package/server/client/dist/assets/Tag-lBxDRE77.js +1 -0
- package/server/client/dist/assets/TagNotes-Bgorp_rC.js +1 -0
- package/server/client/dist/assets/Trash.es-svHOb5LN.js +1 -0
- package/server/client/dist/assets/ViewNotes-C7d10VFf.js +1 -0
- package/server/client/dist/assets/ViewSectionDialog-Bhw-ILop.js +102 -0
- package/server/client/dist/assets/Views-C7V3saUo.js +1 -0
- package/server/client/dist/assets/abnfDiagram-VRR7QNED-CWlV5dXw.js +1 -0
- package/server/client/dist/assets/app-DQBV36r_.js +50 -0
- package/server/client/dist/assets/app-ZgwR5t_I.css +1 -0
- package/server/client/dist/assets/arc-BV-n6RUX.js +1 -0
- package/server/client/dist/assets/architectureDiagram-ZJ3FMSHR-CekzMXdu.js +36 -0
- package/server/client/dist/assets/blockDiagram-677ZJIJ3-B49yHoxq.js +132 -0
- package/server/client/dist/assets/c4Diagram-LMCZKHZV-0AfB9NBC.js +10 -0
- package/server/client/dist/assets/channel-CigOulR-.js +1 -0
- package/server/client/dist/assets/chunk-2Q5K7J3B-DaPQAU1q.js +1 -0
- package/server/client/dist/assets/chunk-32BRIVSS-CfL3kZAg.js +1 -0
- package/server/client/dist/assets/chunk-5VM5RSS4-DeOwWPE_.js +15 -0
- package/server/client/dist/assets/chunk-EX3LRPZG-C3W1oc9y.js +231 -0
- package/server/client/dist/assets/chunk-JWPE2WC7-_Oqh79OY.js +1 -0
- package/server/client/dist/assets/chunk-MOJQB5TN-DDKNNzI6.js +88 -0
- package/server/client/dist/assets/chunk-RYQCIY6F-DCHah7CC.js +1 -0
- package/server/client/dist/assets/chunk-V7JOEXUC-uUOIzgxj.js +206 -0
- package/server/client/dist/assets/chunk-VR4S4FIN-DDVw2WPs.js +1 -0
- package/server/client/dist/assets/chunk-XXDRQBXY-Coo4UFm2.js +1 -0
- package/server/client/dist/assets/classDiagram-OUVF2IWQ-D13ISIOW.js +1 -0
- package/server/client/dist/assets/classDiagram-v2-EOCWNBFH-D13ISIOW.js +1 -0
- package/server/client/dist/assets/colors-Cc3OSVma.js +1 -0
- package/server/client/dist/assets/cose-bilkent-JH36ORCC-C3pHmHpD.js +1 -0
- package/server/client/dist/assets/cynefin-VYW2F7L2-DU_G4Atc.js +166 -0
- package/server/client/dist/assets/cynefinDiagram-TSTJHNR4-DZ6iiJFo.js +62 -0
- package/server/client/dist/assets/cytoscape.esm-D3_iZ_3b.js +321 -0
- package/server/client/dist/assets/dagre-VKFMJZFB-DNELbhwC.js +4 -0
- package/server/client/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/server/client/dist/assets/diagram-FQU43EPY-CMEvZJ61.js +3 -0
- package/server/client/dist/assets/diagram-G47NLZAW-BYDtf6j9.js +24 -0
- package/server/client/dist/assets/diagram-NH7WQ7WH-CmXfcCQA.js +24 -0
- package/server/client/dist/assets/diagram-OA4YK3LP-BflTKJxU.js +30 -0
- package/server/client/dist/assets/diagram-WEI45ONY-KW3fKqna.js +41 -0
- package/server/client/dist/assets/ebnfDiagram-CCIWWBDH-CbyhY5Nj.js +1 -0
- package/server/client/dist/assets/erDiagram-Q63AITRT-CC2eZV8g.js +85 -0
- package/server/client/dist/assets/floating-ui.react-dom-BWTnA00G.js +1 -0
- package/server/client/dist/assets/flowDiagram-23GEKE2U-unplo7Qm.js +156 -0
- package/server/client/dist/assets/ganttDiagram-NO4QXBWP-BcBK5HcZ.js +292 -0
- package/server/client/dist/assets/gitGraphDiagram-IHSO6WYX-DgVbbb5k.js +106 -0
- package/server/client/dist/assets/graph-C5DFNLrC.js +1 -0
- package/server/client/dist/assets/image-delete-confirmation-D7xrODQV.js +1 -0
- package/server/client/dist/assets/image.api-QmHYjp3z.js +17 -0
- package/server/client/dist/assets/index-CaF9d-zG.js +1 -0
- package/server/client/dist/assets/infoDiagram-FWYZ7A6U-N_D9RdfG.js +2 -0
- package/server/client/dist/assets/init-Gi6I4Gst.js +1 -0
- package/server/client/dist/assets/isObject-U9U2HXiq.js +1 -0
- package/server/client/dist/assets/ishikawaDiagram-FXEZZL3T-BBKrzmB2.js +70 -0
- package/server/client/dist/assets/journeyDiagram-5HDEW3XC-NdCxLmQB.js +139 -0
- package/server/client/dist/assets/kanban-definition-HUTT4EX6-Ck7t4-tt.js +89 -0
- package/server/client/dist/assets/katex-DywYRkvg.css +1 -0
- package/server/client/dist/assets/katex-HP8lGamR.js +257 -0
- package/server/client/dist/assets/katex-x288kqHd.js +257 -0
- package/server/client/dist/assets/layout-Cz0MDlGi.js +1 -0
- package/server/client/dist/assets/linear-D6yRgDLq.js +1 -0
- package/server/client/dist/assets/manage-image-DCDF8AM9.js +1 -0
- package/server/client/dist/assets/manage-image-detail-e0ViRAj-.js +1 -0
- package/server/client/dist/assets/map-Mm1V-_Na.js +1 -0
- package/server/client/dist/assets/mcp-admin-adapter-D39G3udJ.js +1 -0
- package/server/client/dist/assets/{mcp-Dd98N9Hq.js → mcp-uFZkdhCi.js} +4 -4
- package/server/client/dist/assets/mermaid.core-B14ZAmWA.js +308 -0
- package/server/client/dist/assets/min-tv6RD-6E.js +1 -0
- package/server/client/dist/assets/mindmap-definition-LN4V7U3C-BTUqSdcP.js +96 -0
- package/server/client/dist/assets/note-core-DUTqUh5N.js +13 -0
- package/server/client/dist/assets/note-runtime-zN5ddTba.js +168 -0
- package/server/client/dist/assets/note-ui-CxK8ymUc.js +2 -0
- package/server/client/dist/assets/{index-DPI6YoDt.css → note-ui-DL8owV-U.css} +1 -1
- package/server/client/dist/assets/note.api-BQovzEzd.js +356 -0
- package/server/client/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/server/client/dist/assets/pegDiagram-2B236MQR-Cjcu3mVz.js +1 -0
- package/server/client/dist/assets/pieDiagram-ENE6RG2P-BxI_c22Z.js +39 -0
- package/server/client/dist/assets/placeholder-D3b8lZGE.js +34 -0
- package/server/client/dist/assets/preload-helper-BXl3LOEh.js +1 -0
- package/server/client/dist/assets/properties-M-yWq1D7.js +1 -0
- package/server/client/dist/assets/quadrantDiagram-ABIIQ3AL-DGm0a-yR.js +7 -0
- package/server/client/dist/assets/railroadDiagram-RFXS5EU6-DmCDu89O.js +1 -0
- package/server/client/dist/assets/requirementDiagram-TGXJPOKE-CBpUt7Ig.js +84 -0
- package/server/client/dist/assets/route-preload.js +2 -0
- package/server/client/dist/assets/sankeyDiagram-HTMAVEWB-BjGnDZlp.js +40 -0
- package/server/client/dist/assets/search-DFnzZKLd.js +1 -0
- package/server/client/dist/assets/sequenceDiagram-DBY2YBRQ-B7rpCfS6.js +162 -0
- package/server/client/dist/assets/sizeCapture-X5ZJPWSS-CUJhD0uW.js +1 -0
- package/server/client/dist/assets/stateDiagram-2N3HPSRC-Y9-3XqRX.js +1 -0
- package/server/client/dist/assets/stateDiagram-v2-6OUMAXLB-DbHXhT02.js +1 -0
- package/server/client/dist/assets/swimlanes-5IMT3BWC-Cs1NO9ng.js +2 -0
- package/server/client/dist/assets/swimlanesDiagram-G3AALYLV-DQnrq2ZC.js +8 -0
- package/server/client/dist/assets/{tag.api-CwmC8crB.js → tag.api-DUfjr8bg.js} +3 -3
- package/server/client/dist/assets/time-CfAYZipR.js +9 -0
- package/server/client/dist/assets/timeline-definition-FHXFAJF6-8fqD4WNL.js +120 -0
- package/server/client/dist/assets/toNumber-DE3J3kMD.js +1 -0
- package/server/client/dist/assets/transform-BUB79_za.js +1 -0
- package/server/client/dist/assets/trash-CClKfP1x.js +1 -0
- package/server/client/dist/assets/url-9nrbQs7Z.js +1 -0
- package/server/client/dist/assets/{useReminderMutate-DuVKxpBh.js → useReminderMutate--omDLmBV.js} +2 -2
- package/server/client/dist/assets/vennDiagram-L72KCM5P-BPqKkd5a.js +34 -0
- package/server/client/dist/assets/wardleyDiagram-EHGQE667-Cq11kU2M.js +78 -0
- package/server/client/dist/assets/xychartDiagram-FW5EYKEG-DJ6UzFj-.js +7 -0
- package/server/client/dist/index.html +10 -5
- package/server/dist/app.js +1 -1
- package/server/dist/features/image/services/delete.js +25 -1
- package/server/dist/features/note/graphql/note.mutation.resolver.js +38 -12
- package/server/dist/features/note/graphql/note.type-defs.js +1 -1
- package/server/dist/features/note/http/mcp.js +1 -60
- package/server/dist/features/note/services/authoring.js +2 -70
- package/server/dist/features/note/services/markdown-patch.js +80 -8
- package/server/dist/features/note/services/properties.js +21 -1
- package/server/dist/features/note/services/property-restore.js +26 -0
- package/server/dist/features/note/services/snapshot.js +79 -59
- package/server/dist/features/note/services/trash.js +16 -31
- package/server/dist/features/search/embedding-api-key-store.js +57 -0
- package/server/dist/features/search/embedding-client.js +179 -0
- package/server/dist/features/search/graphql/index.js +6 -0
- package/server/dist/features/search/graphql/resolver.js +20 -0
- package/server/dist/features/search/graphql/type-defs.js +30 -0
- package/server/dist/features/search/http/handlers.js +108 -0
- package/server/dist/features/search/hybrid-ranking.js +55 -0
- package/server/dist/features/search/hybrid-search.js +174 -0
- package/server/dist/features/search/note-change.js +16 -0
- package/server/dist/features/search/note-chunking.js +75 -0
- package/server/dist/features/search/search-config.js +150 -0
- package/server/dist/features/search/search-manager.js +431 -0
- package/server/dist/features/search/semantic-indexer.js +138 -0
- package/server/dist/features/search/sqlite-vector-index.js +636 -0
- package/server/dist/features/search/url-normalization.js +10 -0
- package/server/dist/features/tag/services/organization.js +3 -0
- package/server/dist/features/view/services/workspace.js +1 -1
- package/server/dist/main.js +2 -0
- package/server/dist/modules/blocknote.js +266 -80
- package/server/dist/modules/error-handler.js +8 -0
- package/server/dist/paths.js +7 -2
- package/server/dist/routes/api.js +37 -0
- package/server/dist/routes/mcp.js +0 -5
- package/server/dist/schema/index.js +3 -0
- package/server/client/dist/assets/ArrowRight.es-CbA8ey6a.js +0 -1
- package/server/client/dist/assets/Calendar-CgWgr1BV.js +0 -25
- package/server/client/dist/assets/Callout-DKruIT0f.js +0 -1
- package/server/client/dist/assets/Copy.es-DFbgpaNJ.js +0 -1
- package/server/client/dist/assets/Graph-kCltGRlY.js +0 -1
- package/server/client/dist/assets/Image-7W0DZwMo.js +0 -1
- package/server/client/dist/assets/Image.es-5pXLx9TU.js +0 -1
- package/server/client/dist/assets/Note-BW6Np_5g.js +0 -20
- package/server/client/dist/assets/Plus.es-B0sg64VJ.js +0 -1
- package/server/client/dist/assets/Reminders-C10fHQcl.js +0 -1
- package/server/client/dist/assets/Search-tf2nV4v0.js +0 -1
- package/server/client/dist/assets/SurfaceCard-pydtvHfu.js +0 -1
- package/server/client/dist/assets/Tag-uCoYbISy.js +0 -1
- package/server/client/dist/assets/TagNotes-CqOcQiRw.js +0 -1
- package/server/client/dist/assets/Trash.es-w32Goxfa.js +0 -1
- package/server/client/dist/assets/ViewNotes-slPXXoOY.js +0 -1
- package/server/client/dist/assets/ViewSectionDialog-brTrsF6G.js +0 -102
- package/server/client/dist/assets/Views-B_wsb-XJ.js +0 -1
- package/server/client/dist/assets/graph-vendor-BAF95C0g.js +0 -46
- package/server/client/dist/assets/image.api-CYsjXiMr.js +0 -17
- package/server/client/dist/assets/index-BR05aSXr.js +0 -342
- package/server/client/dist/assets/index-D83QbOcw.js +0 -1
- package/server/client/dist/assets/manage-image-BYIhWu56.js +0 -1
- package/server/client/dist/assets/manage-image-detail-6KgmQvbN.js +0 -1
- package/server/client/dist/assets/mcp-admin-adapter-DOgBsYpl.js +0 -1
- package/server/client/dist/assets/note-core-CMGUaW1v.js +0 -176
- package/server/client/dist/assets/note-vendor-Hoz9Q4-4.js +0 -54
- package/server/client/dist/assets/placeholder-siHGg9aK.js +0 -34
- package/server/client/dist/assets/properties-CyVRKgcT.js +0 -1
- package/server/client/dist/assets/trash-wojQBK9Z.js +0 -1
package/dist/mcp.js
CHANGED
|
@@ -6,7 +6,7 @@ import path from "path";
|
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
8
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
|
-
import { z as
|
|
9
|
+
import { z as z3 } from "zod";
|
|
10
10
|
|
|
11
11
|
// src/mcp-note-output.ts
|
|
12
12
|
var formatBackReferenceLink = (backReference) => {
|
|
@@ -58,65 +58,86 @@ var formatMcpReadNoteOutput = ({
|
|
|
58
58
|
};
|
|
59
59
|
|
|
60
60
|
// src/mcp-intent-write-tools.ts
|
|
61
|
+
import { z as z2 } from "zod";
|
|
62
|
+
|
|
63
|
+
// src/mcp-tool-support.ts
|
|
61
64
|
import { z } from "zod";
|
|
62
|
-
var
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
var noteLayoutSchema = z.enum(["narrow", "wide", "full"]);
|
|
66
|
+
var createMcpTextToolResult = (text) => ({
|
|
67
|
+
content: [
|
|
68
|
+
{
|
|
69
|
+
type: "text",
|
|
70
|
+
text
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
});
|
|
74
|
+
var createMcpJsonToolResult = (value) => {
|
|
75
|
+
return createMcpTextToolResult(JSON.stringify(value, null, 2));
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// src/mcp-intent-write-tools.ts
|
|
79
|
+
var markdownPatchSelectorSchema = z2.discriminatedUnion("type", [
|
|
80
|
+
z2.object({
|
|
81
|
+
type: z2.literal("exact_text"),
|
|
82
|
+
text: z2.string(),
|
|
83
|
+
before: z2.string().optional(),
|
|
84
|
+
after: z2.string().optional()
|
|
68
85
|
}),
|
|
69
|
-
|
|
70
|
-
type:
|
|
71
|
-
text:
|
|
72
|
-
matchIndex:
|
|
73
|
-
lineStart:
|
|
74
|
-
matchSha256:
|
|
75
|
-
surroundingHash:
|
|
76
|
-
positionHint:
|
|
86
|
+
z2.object({
|
|
87
|
+
type: z2.literal("match_candidate"),
|
|
88
|
+
text: z2.string(),
|
|
89
|
+
matchIndex: z2.number().int().nonnegative(),
|
|
90
|
+
lineStart: z2.number().int().positive(),
|
|
91
|
+
matchSha256: z2.string(),
|
|
92
|
+
surroundingHash: z2.string(),
|
|
93
|
+
positionHint: z2.enum(["first", "last"]).optional()
|
|
77
94
|
})
|
|
78
95
|
]);
|
|
79
|
-
var markdownPatchOperationSchema =
|
|
80
|
-
|
|
81
|
-
type:
|
|
82
|
-
replacement:
|
|
96
|
+
var markdownPatchOperationSchema = z2.discriminatedUnion("type", [
|
|
97
|
+
z2.object({
|
|
98
|
+
type: z2.literal("replace"),
|
|
99
|
+
replacement: z2.string()
|
|
83
100
|
}),
|
|
84
|
-
|
|
85
|
-
type:
|
|
86
|
-
insertion:
|
|
101
|
+
z2.object({
|
|
102
|
+
type: z2.literal("insert_before"),
|
|
103
|
+
insertion: z2.string()
|
|
87
104
|
}),
|
|
88
|
-
|
|
89
|
-
type:
|
|
90
|
-
insertion:
|
|
105
|
+
z2.object({
|
|
106
|
+
type: z2.literal("insert_after"),
|
|
107
|
+
insertion: z2.string()
|
|
91
108
|
})
|
|
92
109
|
]);
|
|
93
|
-
var markdownAppendPlacementSchema =
|
|
94
|
-
|
|
95
|
-
type:
|
|
110
|
+
var markdownAppendPlacementSchema = z2.discriminatedUnion("type", [
|
|
111
|
+
z2.object({
|
|
112
|
+
type: z2.literal("end")
|
|
96
113
|
}),
|
|
97
|
-
|
|
98
|
-
type:
|
|
99
|
-
heading:
|
|
100
|
-
level:
|
|
114
|
+
z2.object({
|
|
115
|
+
type: z2.literal("after_heading"),
|
|
116
|
+
heading: z2.string(),
|
|
117
|
+
level: z2.number().int().min(1).max(6).optional()
|
|
101
118
|
})
|
|
102
119
|
]);
|
|
103
|
-
var markdownWritePolicySchema =
|
|
104
|
-
allowNoop:
|
|
105
|
-
preserveTags:
|
|
106
|
-
preserveReferences:
|
|
120
|
+
var markdownWritePolicySchema = z2.object({
|
|
121
|
+
allowNoop: z2.boolean().optional(),
|
|
122
|
+
preserveTags: z2.union([z2.boolean(), z2.literal("warn")]).optional(),
|
|
123
|
+
preserveReferences: z2.union([z2.boolean(), z2.literal("warn")]).optional()
|
|
107
124
|
}).optional();
|
|
125
|
+
var markdownWriteBaselineFields = {
|
|
126
|
+
expectedUpdatedAt: z2.string().optional().describe("Expected note updatedAt from the read you based this edit on. Required unless baseMarkdownSha256 is provided."),
|
|
127
|
+
baseMarkdownSha256: z2.string().optional().describe("SHA-256 of the current markdown. Optional alternative to expectedUpdatedAt.")
|
|
128
|
+
};
|
|
108
129
|
var MCP_METADATA_PROPERTY_PATCH_LIMIT = 50;
|
|
109
130
|
var normalizeMetadataPropertyKey = (key) => key.trim().toLowerCase().replace(/\s+/g, "-");
|
|
110
|
-
var metadataPropertyPatchSchema =
|
|
111
|
-
set:
|
|
112
|
-
key:
|
|
113
|
-
value:
|
|
131
|
+
var metadataPropertyPatchSchema = z2.object({
|
|
132
|
+
set: z2.array(z2.object({
|
|
133
|
+
key: z2.string().min(1).describe("Property key from ocean_brain_list_properties."),
|
|
134
|
+
value: z2.string().describe("String value: select option.value, date YYYY-MM-DD, boolean true/false, number finite string, url http(s).")
|
|
114
135
|
})).max(MCP_METADATA_PROPERTY_PATCH_LIMIT).optional().describe("Set up to 50 property values. Keys must be unique."),
|
|
115
|
-
deleteKeys:
|
|
136
|
+
deleteKeys: z2.array(z2.string().min(1)).max(MCP_METADATA_PROPERTY_PATCH_LIMIT).optional().describe("Remove up to 50 property values from this note. Keys must be unique and not also in set.")
|
|
116
137
|
}).superRefine((patch, context) => {
|
|
117
138
|
if ((patch.set?.length ?? 0) === 0 && (patch.deleteKeys?.length ?? 0) === 0) {
|
|
118
139
|
context.addIssue({
|
|
119
|
-
code:
|
|
140
|
+
code: z2.ZodIssueCode.custom,
|
|
120
141
|
message: "properties must include set or deleteKeys."
|
|
121
142
|
});
|
|
122
143
|
return;
|
|
@@ -126,7 +147,7 @@ var metadataPropertyPatchSchema = z.object({
|
|
|
126
147
|
const key = normalizeMetadataPropertyKey(item.key);
|
|
127
148
|
if (setKeys.has(key)) {
|
|
128
149
|
context.addIssue({
|
|
129
|
-
code:
|
|
150
|
+
code: z2.ZodIssueCode.custom,
|
|
130
151
|
message: `properties.set contains duplicate key ${key}.`
|
|
131
152
|
});
|
|
132
153
|
}
|
|
@@ -137,13 +158,13 @@ var metadataPropertyPatchSchema = z.object({
|
|
|
137
158
|
const key = normalizeMetadataPropertyKey(rawKey);
|
|
138
159
|
if (deleteKeys.has(key)) {
|
|
139
160
|
context.addIssue({
|
|
140
|
-
code:
|
|
161
|
+
code: z2.ZodIssueCode.custom,
|
|
141
162
|
message: `properties.deleteKeys contains duplicate key ${key}.`
|
|
142
163
|
});
|
|
143
164
|
}
|
|
144
165
|
if (setKeys.has(key)) {
|
|
145
166
|
context.addIssue({
|
|
146
|
-
code:
|
|
167
|
+
code: z2.ZodIssueCode.custom,
|
|
147
168
|
message: `properties cannot set and delete ${key} in one request.`
|
|
148
169
|
});
|
|
149
170
|
}
|
|
@@ -161,10 +182,9 @@ var registerIntentWriteTools = (server, {
|
|
|
161
182
|
tools.patchNoteMarkdown,
|
|
162
183
|
"Patch a specific part of a note, like finding text with `rg` and editing only that match. Use for localized edits: fix one sentence, replace one paragraph, insert before/after exact text, or edit one section. Do not use for whole-note rewrites; use ocean_brain_replace_note_markdown instead.",
|
|
163
184
|
{
|
|
164
|
-
id:
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
intent: z.string().describe("Human-readable reason for the patch."),
|
|
185
|
+
id: z2.string().describe("Note ID to patch"),
|
|
186
|
+
...markdownWriteBaselineFields,
|
|
187
|
+
intent: z2.string().describe("Human-readable reason for the patch."),
|
|
168
188
|
selector: markdownPatchSelectorSchema.describe("Exact text or previously returned match candidate."),
|
|
169
189
|
operation: markdownPatchOperationSchema.describe("replace, insert_before, or insert_after."),
|
|
170
190
|
policy: markdownWritePolicySchema
|
|
@@ -181,25 +201,19 @@ var registerIntentWriteTools = (server, {
|
|
|
181
201
|
...policy ? { policy } : {}
|
|
182
202
|
};
|
|
183
203
|
const result = await jsonRequest2(serverUrl, writeToken, "/api/mcp/notes/patch-markdown", payload);
|
|
184
|
-
return
|
|
185
|
-
content: [{
|
|
186
|
-
type: "text",
|
|
187
|
-
text: JSON.stringify(result, null, 2)
|
|
188
|
-
}]
|
|
189
|
-
};
|
|
204
|
+
return createMcpJsonToolResult(result);
|
|
190
205
|
}
|
|
191
206
|
);
|
|
192
207
|
server.tool(
|
|
193
208
|
tools.appendNoteMarkdown,
|
|
194
209
|
"Append markdown without changing existing content, like `cat addition.md >> note.md`. Use for adding logs, status updates, meeting notes, or new sections at the end or after a heading. Do not use to modify or replace existing body content.",
|
|
195
210
|
{
|
|
196
|
-
id:
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
insertion: z.string().describe("Markdown to append. Tags are body tokens such as [@tag] or [#tag]."),
|
|
211
|
+
id: z2.string().describe("Note ID to append to"),
|
|
212
|
+
...markdownWriteBaselineFields,
|
|
213
|
+
intent: z2.string().describe("Human-readable reason for the append."),
|
|
214
|
+
insertion: z2.string().describe("Markdown to append. Tags are body tokens such as [@tag]."),
|
|
201
215
|
placement: markdownAppendPlacementSchema.optional().describe("Default is end. after_heading requires one unique matching heading."),
|
|
202
|
-
separator:
|
|
216
|
+
separator: z2.enum(["\n\n", "\n"]).optional().describe("Separator inserted around appended markdown. Defaults to a blank line."),
|
|
203
217
|
policy: markdownWritePolicySchema
|
|
204
218
|
},
|
|
205
219
|
async ({ id, expectedUpdatedAt, baseMarkdownSha256, intent, insertion, placement, separator, policy }) => {
|
|
@@ -215,22 +229,17 @@ var registerIntentWriteTools = (server, {
|
|
|
215
229
|
...policy ? { policy } : {}
|
|
216
230
|
};
|
|
217
231
|
const result = await jsonRequest2(serverUrl, writeToken, "/api/mcp/notes/append-markdown", payload);
|
|
218
|
-
return
|
|
219
|
-
content: [{
|
|
220
|
-
type: "text",
|
|
221
|
-
text: JSON.stringify(result, null, 2)
|
|
222
|
-
}]
|
|
223
|
-
};
|
|
232
|
+
return createMcpJsonToolResult(result);
|
|
224
233
|
}
|
|
225
234
|
);
|
|
226
235
|
server.tool(
|
|
227
236
|
tools.updateNoteMetadata,
|
|
228
237
|
"Update note metadata only: title, layout, or properties. This does not edit the note body. For properties, call ocean_brain_list_properties first. Use existing property key and string value only; do not send valueType. select=option.value, date=YYYY-MM-DD, boolean=true/false, number=finite string, url=http(s). Use deleteKeys to remove values. Definitions are not created.",
|
|
229
238
|
{
|
|
230
|
-
id:
|
|
231
|
-
expectedUpdatedAt:
|
|
232
|
-
title:
|
|
233
|
-
layout:
|
|
239
|
+
id: z2.string().describe("Note ID to update"),
|
|
240
|
+
expectedUpdatedAt: z2.string().describe("Expected note updatedAt from the read you based this metadata edit on. Required to prevent stale writes."),
|
|
241
|
+
title: z2.string().optional().describe("New note title"),
|
|
242
|
+
layout: noteLayoutSchema.optional().describe("New note layout"),
|
|
234
243
|
properties: metadataPropertyPatchSchema.describe("Patch existing shared property values. Include set and/or deleteKeys; empty patches are rejected.")
|
|
235
244
|
},
|
|
236
245
|
async ({ id, expectedUpdatedAt, title, layout, properties }) => {
|
|
@@ -243,23 +252,17 @@ var registerIntentWriteTools = (server, {
|
|
|
243
252
|
...properties ? { properties } : {}
|
|
244
253
|
};
|
|
245
254
|
const result = await jsonRequest2(serverUrl, writeToken, "/api/mcp/notes/metadata", payload);
|
|
246
|
-
return
|
|
247
|
-
content: [{
|
|
248
|
-
type: "text",
|
|
249
|
-
text: JSON.stringify(result, null, 2)
|
|
250
|
-
}]
|
|
251
|
-
};
|
|
255
|
+
return createMcpJsonToolResult(result);
|
|
252
256
|
}
|
|
253
257
|
);
|
|
254
258
|
server.tool(
|
|
255
259
|
tools.replaceNoteMarkdown,
|
|
256
260
|
"Replace the entire note body, like overwriting a markdown file with `cat new.md > note.md`. Use when the user explicitly asks to rewrite, replace, regenerate, restructure, or overwrite the whole note. Do not use for localized edits such as fixing one sentence, changing one paragraph, or inserting under a heading; use ocean_brain_patch_note_markdown instead.",
|
|
257
261
|
{
|
|
258
|
-
id:
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
replacement: z.string().describe("Complete replacement markdown body. Tags are body tokens such as [@tag] or [#tag]."),
|
|
262
|
+
id: z2.string().describe("Note ID to replace"),
|
|
263
|
+
...markdownWriteBaselineFields,
|
|
264
|
+
intent: z2.string().describe("Human-readable reason for the full replace."),
|
|
265
|
+
replacement: z2.string().describe("Complete replacement markdown body. Tags are body tokens such as [@tag]."),
|
|
263
266
|
policy: markdownWritePolicySchema
|
|
264
267
|
},
|
|
265
268
|
async ({ id, expectedUpdatedAt, baseMarkdownSha256, intent, replacement, policy }) => {
|
|
@@ -273,12 +276,7 @@ var registerIntentWriteTools = (server, {
|
|
|
273
276
|
...policy ? { policy } : {}
|
|
274
277
|
};
|
|
275
278
|
const result = await jsonRequest2(serverUrl, writeToken, "/api/mcp/notes/replace-markdown", payload);
|
|
276
|
-
return
|
|
277
|
-
content: [{
|
|
278
|
-
type: "text",
|
|
279
|
-
text: JSON.stringify(result, null, 2)
|
|
280
|
-
}]
|
|
281
|
-
};
|
|
279
|
+
return createMcpJsonToolResult(result);
|
|
282
280
|
}
|
|
283
281
|
);
|
|
284
282
|
};
|
|
@@ -331,7 +329,6 @@ var OCEAN_BRAIN_MCP_TOOLS = {
|
|
|
331
329
|
searchNotes: "ocean_brain_search_notes",
|
|
332
330
|
readNote: "ocean_brain_read_note",
|
|
333
331
|
createNote: "ocean_brain_create_note",
|
|
334
|
-
updateNote: "ocean_brain_update_note",
|
|
335
332
|
patchNoteMarkdown: "ocean_brain_patch_note_markdown",
|
|
336
333
|
appendNoteMarkdown: "ocean_brain_append_note_markdown",
|
|
337
334
|
updateNoteMetadata: "ocean_brain_update_note_metadata",
|
|
@@ -346,12 +343,11 @@ var OCEAN_BRAIN_MCP_TOOLS = {
|
|
|
346
343
|
createTag: "ocean_brain_create_tag",
|
|
347
344
|
deleteNote: "ocean_brain_delete_note"
|
|
348
345
|
};
|
|
349
|
-
var
|
|
350
|
-
var
|
|
351
|
-
var
|
|
352
|
-
var
|
|
353
|
-
var
|
|
354
|
-
var viewSortOrderSchema = z2.enum(["asc", "desc"]);
|
|
346
|
+
var tagMatchModeSchema = z3.enum(["and", "or"]);
|
|
347
|
+
var propertyValueTypeSchema = z3.enum(["text", "url", "number", "date", "boolean", "select"]);
|
|
348
|
+
var propertyFilterOperatorSchema = z3.enum(["equals", "before", "after", "exists", "notExists"]);
|
|
349
|
+
var viewSortBySchema = z3.enum(["updatedAt", "createdAt", "title"]);
|
|
350
|
+
var viewSortOrderSchema = z3.enum(["asc", "desc"]);
|
|
355
351
|
var formatMcpGraphqlError = (error) => {
|
|
356
352
|
const suffix = [];
|
|
357
353
|
if (error.extensions?.code) {
|
|
@@ -365,17 +361,20 @@ var formatMcpGraphqlError = (error) => {
|
|
|
365
361
|
}
|
|
366
362
|
return `GraphQL error: ${error.message} (${suffix.join(", ")})`;
|
|
367
363
|
};
|
|
368
|
-
var propertyFilterSchema =
|
|
369
|
-
key:
|
|
364
|
+
var propertyFilterSchema = z3.object({
|
|
365
|
+
key: z3.string().describe("Property key from ocean_brain_list_properties, e.g. state"),
|
|
370
366
|
valueType: propertyValueTypeSchema.describe("Property value type from the property definition"),
|
|
371
367
|
operator: propertyFilterOperatorSchema.describe("Filter operator. before/after are only valid for date or number properties."),
|
|
372
|
-
value:
|
|
368
|
+
value: z3.string().nullable().optional().describe("Filter value. Required unless operator is exists or notExists. select=option.value, date=YYYY-MM-DD, boolean=true/false, number=finite, url=http(s).")
|
|
373
369
|
});
|
|
374
370
|
var normalizeOceanBrainTagName = (name) => {
|
|
375
371
|
const trimmedName = name.trim();
|
|
376
372
|
if (!trimmedName) {
|
|
377
373
|
throw new Error("Tag name is required.");
|
|
378
374
|
}
|
|
375
|
+
if (trimmedName.startsWith("#")) {
|
|
376
|
+
throw new Error("Ocean Brain tags use @, not #. Example: @project");
|
|
377
|
+
}
|
|
379
378
|
const normalizedName = trimmedName.startsWith("@") ? trimmedName : `@${trimmedName}`;
|
|
380
379
|
if (normalizedName === "@" || /\s/.test(normalizedName.slice(1))) {
|
|
381
380
|
throw new Error("Ocean Brain tag names must be a single token. Example: @project");
|
|
@@ -441,17 +440,13 @@ var requireWriteToken = (token, toolName) => {
|
|
|
441
440
|
}
|
|
442
441
|
throw new Error(`${toolName} requires an MCP bearer token. Set --token or --token-file.`);
|
|
443
442
|
};
|
|
444
|
-
|
|
445
|
-
const server = new McpServer({
|
|
446
|
-
name: "ocean-brain",
|
|
447
|
-
version: pkg.version
|
|
448
|
-
});
|
|
443
|
+
var registerMcpTools = (server, serverUrl, token) => {
|
|
449
444
|
server.tool(
|
|
450
445
|
OCEAN_BRAIN_MCP_TOOLS.searchNotes,
|
|
451
446
|
"Search Ocean Brain notes by keyword. Returns matching note titles and tags. Use ocean_brain_read_note to get full content for a specific note.",
|
|
452
447
|
{
|
|
453
|
-
query:
|
|
454
|
-
limit:
|
|
448
|
+
query: z3.string().describe("Search keyword"),
|
|
449
|
+
limit: z3.number().optional().default(10).describe("Max results (default: 10)")
|
|
455
450
|
},
|
|
456
451
|
async ({ query, limit }) => {
|
|
457
452
|
const data = await graphql(serverUrl, token, `
|
|
@@ -479,20 +474,15 @@ async function startMcpServer(serverUrl, token) {
|
|
|
479
474
|
tags: note.tags.map((t) => t.name),
|
|
480
475
|
preview: note.contentAsMarkdown.slice(0, 100)
|
|
481
476
|
}));
|
|
482
|
-
return {
|
|
483
|
-
content: [{
|
|
484
|
-
type: "text",
|
|
485
|
-
text: JSON.stringify({ totalCount: result.totalCount, notes }, null, 2)
|
|
486
|
-
}]
|
|
487
|
-
};
|
|
477
|
+
return createMcpJsonToolResult({ totalCount: result.totalCount, notes });
|
|
488
478
|
}
|
|
489
479
|
);
|
|
490
480
|
server.tool(
|
|
491
481
|
OCEAN_BRAIN_MCP_TOOLS.readNote,
|
|
492
482
|
"Read an Ocean Brain note by ID. Returns properties, tags, truncated content by default (1000 chars), and a back-reference summary. Set maxLength to 0 only when full content is necessary.",
|
|
493
483
|
{
|
|
494
|
-
id:
|
|
495
|
-
maxLength:
|
|
484
|
+
id: z3.string().describe("Note ID"),
|
|
485
|
+
maxLength: z3.number().optional().default(1e3).describe("Max content length in characters. 0 for full content. (default: 1000)")
|
|
496
486
|
},
|
|
497
487
|
async ({ id, maxLength }) => {
|
|
498
488
|
const data = await graphql(serverUrl, token, `
|
|
@@ -519,20 +509,15 @@ async function startMcpServer(serverUrl, token) {
|
|
|
519
509
|
backReferences,
|
|
520
510
|
maxLength
|
|
521
511
|
});
|
|
522
|
-
return
|
|
523
|
-
content: [{
|
|
524
|
-
type: "text",
|
|
525
|
-
text: output
|
|
526
|
-
}]
|
|
527
|
-
};
|
|
512
|
+
return createMcpTextToolResult(output);
|
|
528
513
|
}
|
|
529
514
|
);
|
|
530
515
|
server.tool(
|
|
531
516
|
OCEAN_BRAIN_MCP_TOOLS.createNote,
|
|
532
517
|
"Create an Ocean Brain note from markdown. Search/read first to avoid duplicates; prefer patching an existing note when the intent is a local change.",
|
|
533
518
|
{
|
|
534
|
-
title:
|
|
535
|
-
markdown:
|
|
519
|
+
title: z3.string().describe("Note title"),
|
|
520
|
+
markdown: z3.string().optional().default("").describe("Markdown body for the new note. In MCP markdown, tags must use [@tag]. Defaults to an empty note body."),
|
|
536
521
|
layout: noteLayoutSchema.optional().describe("Optional note layout: narrow, wide, or full. Prefer wide for most notes unless the user explicitly wants narrow or full.")
|
|
537
522
|
},
|
|
538
523
|
async ({ title, markdown, layout }) => {
|
|
@@ -542,37 +527,7 @@ async function startMcpServer(serverUrl, token) {
|
|
|
542
527
|
markdown,
|
|
543
528
|
...layout ? { layout } : {}
|
|
544
529
|
});
|
|
545
|
-
return
|
|
546
|
-
content: [{
|
|
547
|
-
type: "text",
|
|
548
|
-
text: JSON.stringify(result, null, 2)
|
|
549
|
-
}]
|
|
550
|
-
};
|
|
551
|
-
}
|
|
552
|
-
);
|
|
553
|
-
server.tool(
|
|
554
|
-
OCEAN_BRAIN_MCP_TOOLS.updateNote,
|
|
555
|
-
"Legacy full-field note update kept for backward compatibility. Prefer ocean_brain_patch_note_markdown for localized body edits, ocean_brain_append_note_markdown for additions, ocean_brain_replace_note_markdown for whole-body rewrites, and ocean_brain_update_note_metadata for title/layout/properties. Use this only when a legacy client needs combined field updates.",
|
|
556
|
-
{
|
|
557
|
-
id: z2.string().describe("Note ID to update"),
|
|
558
|
-
title: z2.string().optional().describe("New note title"),
|
|
559
|
-
markdown: z2.string().optional().describe("Replacement markdown body. In MCP markdown, tags must use [@tag] or [#tag]."),
|
|
560
|
-
layout: noteLayoutSchema.optional().describe("Optional note layout: narrow, wide, or full. Prefer wide for most notes unless the user explicitly wants narrow or full.")
|
|
561
|
-
},
|
|
562
|
-
async ({ id, title, markdown, layout }) => {
|
|
563
|
-
const writeToken = requireWriteToken(token, OCEAN_BRAIN_MCP_TOOLS.updateNote);
|
|
564
|
-
const result = await jsonRequest(serverUrl, writeToken, "/api/mcp/notes/update", {
|
|
565
|
-
id,
|
|
566
|
-
...title !== void 0 ? { title } : {},
|
|
567
|
-
...markdown !== void 0 ? { markdown } : {},
|
|
568
|
-
...layout ? { layout } : {}
|
|
569
|
-
});
|
|
570
|
-
return {
|
|
571
|
-
content: [{
|
|
572
|
-
type: "text",
|
|
573
|
-
text: JSON.stringify(result, null, 2)
|
|
574
|
-
}]
|
|
575
|
-
};
|
|
530
|
+
return createMcpJsonToolResult(result);
|
|
576
531
|
}
|
|
577
532
|
);
|
|
578
533
|
registerIntentWriteTools(server, {
|
|
@@ -603,21 +558,16 @@ async function startMcpServer(serverUrl, token) {
|
|
|
603
558
|
pagination: { limit: 100, offset: 0 }
|
|
604
559
|
});
|
|
605
560
|
const result = data?.allTags;
|
|
606
|
-
return
|
|
607
|
-
content: [{
|
|
608
|
-
type: "text",
|
|
609
|
-
text: JSON.stringify(result, null, 2)
|
|
610
|
-
}]
|
|
611
|
-
};
|
|
561
|
+
return createMcpJsonToolResult(result);
|
|
612
562
|
}
|
|
613
563
|
);
|
|
614
564
|
server.tool(
|
|
615
565
|
OCEAN_BRAIN_MCP_TOOLS.listProperties,
|
|
616
566
|
"List shared Ocean Brain property definitions. Use this before property queries so keys, value types, and select option values are valid.",
|
|
617
567
|
{
|
|
618
|
-
query:
|
|
619
|
-
limit:
|
|
620
|
-
offset:
|
|
568
|
+
query: z3.string().optional().default("").describe("Optional property key/name search query"),
|
|
569
|
+
limit: z3.number().optional().default(50).describe("Max results (default: 50, server max: 100)"),
|
|
570
|
+
offset: z3.number().optional().default(0).describe("Pagination offset (default: 0)")
|
|
621
571
|
},
|
|
622
572
|
async ({ query, limit, offset }) => {
|
|
623
573
|
const data = await graphql(serverUrl, token, `
|
|
@@ -639,42 +589,32 @@ async function startMcpServer(serverUrl, token) {
|
|
|
639
589
|
pagination: { limit, offset }
|
|
640
590
|
});
|
|
641
591
|
const result = data?.notePropertyKeys;
|
|
642
|
-
return {
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
totalCount: result.totalCount,
|
|
647
|
-
propertyKeys: result.keys
|
|
648
|
-
}, null, 2)
|
|
649
|
-
}]
|
|
650
|
-
};
|
|
592
|
+
return createMcpJsonToolResult({
|
|
593
|
+
totalCount: result.totalCount,
|
|
594
|
+
propertyKeys: result.keys
|
|
595
|
+
});
|
|
651
596
|
}
|
|
652
597
|
);
|
|
653
598
|
server.tool(
|
|
654
599
|
OCEAN_BRAIN_MCP_TOOLS.listNotesByTag,
|
|
655
600
|
"List Ocean Brain notes for a specific tag name. The tool accepts either @tag or tag and resolves it to the canonical Ocean Brain tag name first.",
|
|
656
601
|
{
|
|
657
|
-
tag:
|
|
658
|
-
limit:
|
|
659
|
-
offset:
|
|
602
|
+
tag: z3.string().describe("Tag name to inspect. You can pass @project or project."),
|
|
603
|
+
limit: z3.number().optional().default(20).describe("Max results (default: 20)"),
|
|
604
|
+
offset: z3.number().optional().default(0).describe("Pagination offset (default: 0)")
|
|
660
605
|
},
|
|
661
606
|
async ({ tag, limit, offset }) => {
|
|
662
607
|
const normalizedTag = normalizeOceanBrainTagName(tag);
|
|
663
608
|
const tagResult = await fetchOceanBrainTags(serverUrl, token, normalizedTag);
|
|
664
609
|
const exactMatches = tagResult.tags.filter((item) => item.name === normalizedTag);
|
|
665
610
|
if (exactMatches.length === 0) {
|
|
666
|
-
return {
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
noteCount: 0,
|
|
674
|
-
notes: []
|
|
675
|
-
}, null, 2)
|
|
676
|
-
}]
|
|
677
|
-
};
|
|
611
|
+
return createMcpJsonToolResult({
|
|
612
|
+
requestedTag: tag,
|
|
613
|
+
normalizedTag,
|
|
614
|
+
tagFound: false,
|
|
615
|
+
noteCount: 0,
|
|
616
|
+
notes: []
|
|
617
|
+
});
|
|
678
618
|
}
|
|
679
619
|
const selectedTag = exactMatches[0];
|
|
680
620
|
const notesData = await graphql(serverUrl, token, `
|
|
@@ -694,35 +634,30 @@ async function startMcpServer(serverUrl, token) {
|
|
|
694
634
|
pagination: { limit, offset }
|
|
695
635
|
});
|
|
696
636
|
const noteResult = notesData?.tagNotes;
|
|
697
|
-
return {
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
tags: note.tags.map((item) => item.name)
|
|
712
|
-
}))
|
|
713
|
-
}, null, 2)
|
|
714
|
-
}]
|
|
715
|
-
};
|
|
637
|
+
return createMcpJsonToolResult({
|
|
638
|
+
requestedTag: tag,
|
|
639
|
+
normalizedTag,
|
|
640
|
+
tagFound: true,
|
|
641
|
+
duplicateExactMatchCount: exactMatches.length,
|
|
642
|
+
tag: selectedTag,
|
|
643
|
+
totalCount: noteResult.totalCount,
|
|
644
|
+
notes: noteResult.notes.map((note) => ({
|
|
645
|
+
id: note.id,
|
|
646
|
+
title: note.title,
|
|
647
|
+
updatedAt: note.updatedAt,
|
|
648
|
+
tags: note.tags.map((item) => item.name)
|
|
649
|
+
}))
|
|
650
|
+
});
|
|
716
651
|
}
|
|
717
652
|
);
|
|
718
653
|
server.tool(
|
|
719
654
|
OCEAN_BRAIN_MCP_TOOLS.listNotesByTags,
|
|
720
655
|
"List Ocean Brain notes for multiple tag names. Supports AND/OR matching and resolves each input tag to the canonical Ocean Brain tag name first.",
|
|
721
656
|
{
|
|
722
|
-
tags:
|
|
657
|
+
tags: z3.array(z3.string()).min(1).describe("Tag names to inspect. You can pass @project or project values."),
|
|
723
658
|
mode: tagMatchModeSchema.optional().default("and").describe("Tag match mode. Use and for intersection, or for union. Defaults to and."),
|
|
724
|
-
limit:
|
|
725
|
-
offset:
|
|
659
|
+
limit: z3.number().optional().default(20).describe("Max results (default: 20)"),
|
|
660
|
+
offset: z3.number().optional().default(0).describe("Pagination offset (default: 0)")
|
|
726
661
|
},
|
|
727
662
|
async ({ tags, mode, limit, offset }) => {
|
|
728
663
|
const normalizedTags = normalizeOceanBrainTagNames(tags);
|
|
@@ -770,44 +705,40 @@ async function startMcpServer(serverUrl, token) {
|
|
|
770
705
|
});
|
|
771
706
|
noteResult = notesData?.notesByTagNames;
|
|
772
707
|
}
|
|
773
|
-
return {
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
tags: note.tags.map((item) => item.name)
|
|
789
|
-
}))
|
|
790
|
-
}, null, 2)
|
|
791
|
-
}]
|
|
792
|
-
};
|
|
708
|
+
return createMcpJsonToolResult({
|
|
709
|
+
requestedTags: tags,
|
|
710
|
+
normalizedTags,
|
|
711
|
+
mode,
|
|
712
|
+
allTagsFound: missingTags.length === 0,
|
|
713
|
+
missingTags,
|
|
714
|
+
tags: matchedTags,
|
|
715
|
+
totalCount: noteResult.totalCount,
|
|
716
|
+
notes: noteResult.notes.map((note) => ({
|
|
717
|
+
id: note.id,
|
|
718
|
+
title: note.title,
|
|
719
|
+
updatedAt: note.updatedAt,
|
|
720
|
+
tags: note.tags.map((item) => item.name)
|
|
721
|
+
}))
|
|
722
|
+
});
|
|
793
723
|
}
|
|
794
724
|
);
|
|
795
725
|
server.tool(
|
|
796
726
|
OCEAN_BRAIN_MCP_TOOLS.queryNotesByProperties,
|
|
797
727
|
"Call ocean_brain_list_properties first. Requires >=1 propertyFilter; use search/recent for broad lists. Use key/valueType from the property definition. Values are strings: select=option.value, date=YYYY-MM-DD, boolean=true/false, number=finite, url=http(s). exists/notExists need no value. Property filters use AND; tagNames use mode. Summaries are returned by default; use propertyKeys for needed property details.",
|
|
798
728
|
{
|
|
799
|
-
propertyFilters:
|
|
800
|
-
tagNames:
|
|
729
|
+
propertyFilters: z3.array(propertyFilterSchema).min(1).max(10).describe("Required property filters. Multiple property filters are combined with AND."),
|
|
730
|
+
tagNames: z3.array(z3.string()).optional().default([]).describe("Optional tag filters. You can pass @project or project."),
|
|
801
731
|
mode: tagMatchModeSchema.optional().default("and").describe("Tag match mode for tagNames only. Property filters are always combined with AND."),
|
|
802
732
|
sortBy: viewSortBySchema.optional().default("updatedAt").describe("Sort field (default: updatedAt)"),
|
|
803
733
|
sortOrder: viewSortOrderSchema.optional().default("desc").describe("Sort order (default: desc)"),
|
|
804
|
-
includeProperties:
|
|
805
|
-
propertyKeys:
|
|
806
|
-
limit:
|
|
807
|
-
offset:
|
|
734
|
+
includeProperties: z3.boolean().optional().default(false).describe("Include returned note properties. Defaults to false to reduce tokens."),
|
|
735
|
+
propertyKeys: z3.array(z3.string()).optional().default([]).describe("Property keys to include in output; automatically includes properties."),
|
|
736
|
+
limit: z3.number().optional().default(20).describe("Max results (default: 20, server max: 50)"),
|
|
737
|
+
offset: z3.number().optional().default(0).describe("Pagination offset (default: 0)")
|
|
808
738
|
},
|
|
809
739
|
async ({ propertyFilters, tagNames, mode, sortBy, sortOrder, includeProperties, propertyKeys, limit, offset }) => {
|
|
810
740
|
const shouldIncludeProperties = includeProperties || propertyKeys.length > 0;
|
|
741
|
+
const normalizedTagNames = normalizeOceanBrainTagNames(tagNames);
|
|
811
742
|
const data = await graphql(serverUrl, token, `
|
|
812
743
|
query ($input: NotesByPropertiesInput!, $pagination: PaginationInput, $includeProperties: Boolean!) {
|
|
813
744
|
notesByProperties(input: $input, pagination: $pagination) {
|
|
@@ -824,7 +755,7 @@ async function startMcpServer(serverUrl, token) {
|
|
|
824
755
|
}
|
|
825
756
|
`, {
|
|
826
757
|
input: {
|
|
827
|
-
tagNames,
|
|
758
|
+
tagNames: normalizedTagNames,
|
|
828
759
|
mode,
|
|
829
760
|
propertyFilters,
|
|
830
761
|
sortBy,
|
|
@@ -834,36 +765,27 @@ async function startMcpServer(serverUrl, token) {
|
|
|
834
765
|
includeProperties: shouldIncludeProperties
|
|
835
766
|
});
|
|
836
767
|
const result = data?.notesByProperties;
|
|
837
|
-
return {
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
},
|
|
852
|
-
includeProperties: shouldIncludeProperties,
|
|
853
|
-
propertyKeys
|
|
854
|
-
}),
|
|
855
|
-
null,
|
|
856
|
-
2
|
|
857
|
-
)
|
|
858
|
-
}]
|
|
859
|
-
};
|
|
768
|
+
return createMcpJsonToolResult(formatPropertyQueryResponse({
|
|
769
|
+
result,
|
|
770
|
+
query: {
|
|
771
|
+
propertyFilters,
|
|
772
|
+
tagNames: normalizedTagNames,
|
|
773
|
+
mode,
|
|
774
|
+
sortBy,
|
|
775
|
+
sortOrder,
|
|
776
|
+
limit,
|
|
777
|
+
offset
|
|
778
|
+
},
|
|
779
|
+
includeProperties: shouldIncludeProperties,
|
|
780
|
+
propertyKeys
|
|
781
|
+
}));
|
|
860
782
|
}
|
|
861
783
|
);
|
|
862
784
|
server.tool(
|
|
863
785
|
OCEAN_BRAIN_MCP_TOOLS.listRecentNotes,
|
|
864
786
|
"List recently updated Ocean Brain notes. Returns titles and tags only. Use ocean_brain_read_note to get full content for a specific note.",
|
|
865
787
|
{
|
|
866
|
-
limit:
|
|
788
|
+
limit: z3.number().optional().default(10).describe("Max results (default: 10)")
|
|
867
789
|
},
|
|
868
790
|
async ({ limit }) => {
|
|
869
791
|
const data = await graphql(serverUrl, token, `
|
|
@@ -889,21 +811,16 @@ async function startMcpServer(serverUrl, token) {
|
|
|
889
811
|
updatedAt: note.updatedAt,
|
|
890
812
|
tags: note.tags.map((t) => t.name)
|
|
891
813
|
}));
|
|
892
|
-
return {
|
|
893
|
-
content: [{
|
|
894
|
-
type: "text",
|
|
895
|
-
text: JSON.stringify({ totalCount: result.totalCount, notes }, null, 2)
|
|
896
|
-
}]
|
|
897
|
-
};
|
|
814
|
+
return createMcpJsonToolResult({ totalCount: result.totalCount, notes });
|
|
898
815
|
}
|
|
899
816
|
);
|
|
900
817
|
server.tool(
|
|
901
818
|
OCEAN_BRAIN_MCP_TOOLS.findNoteCleanupCandidates,
|
|
902
819
|
"Find Ocean Brain note cleanup candidates for temporary or draft notes before deletion. This is optional discovery; ocean_brain_delete_note moves a single note to trash directly.",
|
|
903
820
|
{
|
|
904
|
-
keywords:
|
|
905
|
-
limit:
|
|
906
|
-
offset:
|
|
821
|
+
keywords: z3.string().optional().default("temp tmp draft test wip").describe("Keywords that mark a note as a cleanup candidate. Comma or space separated."),
|
|
822
|
+
limit: z3.number().optional().default(20).describe("Max results (default: 20)"),
|
|
823
|
+
offset: z3.number().optional().default(0).describe("Pagination offset (default: 0)")
|
|
907
824
|
},
|
|
908
825
|
async ({ keywords, limit, offset }) => {
|
|
909
826
|
const normalizedKeywords = keywords.split(/[,\s]+/).map((keyword) => keyword.trim()).filter(Boolean);
|
|
@@ -928,61 +845,54 @@ async function startMcpServer(serverUrl, token) {
|
|
|
928
845
|
pagination: { limit, offset }
|
|
929
846
|
});
|
|
930
847
|
const result = data?.noteCleanupCandidates;
|
|
931
|
-
return {
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
candidateCount: result.length,
|
|
939
|
-
notes: result
|
|
940
|
-
}, null, 2)
|
|
941
|
-
}]
|
|
942
|
-
};
|
|
848
|
+
return createMcpJsonToolResult({
|
|
849
|
+
keywords: normalizedKeywords,
|
|
850
|
+
limit,
|
|
851
|
+
offset,
|
|
852
|
+
candidateCount: result.length,
|
|
853
|
+
notes: result
|
|
854
|
+
});
|
|
943
855
|
}
|
|
944
856
|
);
|
|
945
857
|
server.tool(
|
|
946
858
|
OCEAN_BRAIN_MCP_TOOLS.createTag,
|
|
947
859
|
"Create an Ocean Brain tag through the MCP write path. The tool accepts either @tag or tag and creates the canonical Ocean Brain tag if it does not already exist.",
|
|
948
860
|
{
|
|
949
|
-
name:
|
|
861
|
+
name: z3.string().describe("Tag name to create. You can pass @project or project.")
|
|
950
862
|
},
|
|
951
863
|
async ({ name }) => {
|
|
952
864
|
const writeToken = requireWriteToken(token, OCEAN_BRAIN_MCP_TOOLS.createTag);
|
|
865
|
+
const normalizedName = normalizeOceanBrainTagName(name);
|
|
953
866
|
const result = await jsonRequest(serverUrl, writeToken, "/api/mcp/tags/create", {
|
|
954
|
-
name
|
|
867
|
+
name: normalizedName
|
|
955
868
|
});
|
|
956
|
-
return
|
|
957
|
-
content: [{
|
|
958
|
-
type: "text",
|
|
959
|
-
text: JSON.stringify(result, null, 2)
|
|
960
|
-
}]
|
|
961
|
-
};
|
|
869
|
+
return createMcpJsonToolResult(result);
|
|
962
870
|
}
|
|
963
871
|
);
|
|
964
872
|
server.tool(
|
|
965
873
|
OCEAN_BRAIN_MCP_TOOLS.deleteNote,
|
|
966
874
|
"Move an Ocean Brain note to trash, like `mv note.md trash/`. This is a recoverable trash move, not permanent deletion. Deleted-note tag names are kept as restore metadata; orphan tags are not a blocking condition.",
|
|
967
875
|
{
|
|
968
|
-
id:
|
|
876
|
+
id: z3.string().describe("Note ID to move to trash")
|
|
969
877
|
},
|
|
970
878
|
async ({ id }) => {
|
|
971
879
|
const writeToken = requireWriteToken(token, OCEAN_BRAIN_MCP_TOOLS.deleteNote);
|
|
972
880
|
try {
|
|
973
881
|
const result = await jsonRequest(serverUrl, writeToken, "/api/mcp/notes/delete", { id });
|
|
974
|
-
return
|
|
975
|
-
content: [{
|
|
976
|
-
type: "text",
|
|
977
|
-
text: JSON.stringify(result, null, 2)
|
|
978
|
-
}]
|
|
979
|
-
};
|
|
882
|
+
return createMcpJsonToolResult(result);
|
|
980
883
|
} catch (error) {
|
|
981
884
|
const message = error instanceof Error ? error.message : "Unknown MCP note delete error";
|
|
982
885
|
throw new Error(message);
|
|
983
886
|
}
|
|
984
887
|
}
|
|
985
888
|
);
|
|
889
|
+
};
|
|
890
|
+
async function startMcpServer(serverUrl, token) {
|
|
891
|
+
const server = new McpServer({
|
|
892
|
+
name: "ocean-brain",
|
|
893
|
+
version: pkg.version
|
|
894
|
+
});
|
|
895
|
+
registerMcpTools(server, serverUrl, token);
|
|
986
896
|
const transport = new StdioServerTransport();
|
|
987
897
|
await server.connect(transport);
|
|
988
898
|
}
|
|
@@ -993,5 +903,6 @@ export {
|
|
|
993
903
|
OCEAN_BRAIN_MCP_TOOLS,
|
|
994
904
|
OCEAN_BRAIN_MCP_VERSION_HEADER,
|
|
995
905
|
createMcpRequestHeaders,
|
|
906
|
+
normalizeOceanBrainTagName,
|
|
996
907
|
startMcpServer
|
|
997
908
|
};
|