ocean-brain 0.13.3 → 0.14.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 +66 -2
- package/dist/mcp.js +439 -480
- package/package.json +2 -2
- package/server/client/dist/assets/{ArrowRight.es-BlNsZQFs.js → ArrowRight.es-nBJws3ws.js} +1 -1
- package/server/client/dist/assets/{AuxiliaryPanelHeader-HAttwVby.js → AuxiliaryPanelHeader-BN_2axQu.js} +1 -1
- package/server/client/dist/assets/{Calendar-DwQBllfw.js → Calendar-vA4Xa4Qr.js} +1 -1
- package/server/client/dist/assets/{Callout-CcYyvHQP.js → Callout-Bo2P960P.js} +1 -1
- package/server/client/dist/assets/{CaretUp.es-C8iY5OlB.js → CaretUp.es-DDbqKn78.js} +1 -1
- package/server/client/dist/assets/{CheckCircle.es-Dqr3WImn.js → CheckCircle.es-CyY9aLBF.js} +1 -1
- package/server/client/dist/assets/{Copy.es-fMv0ETir.js → Copy.es-BwX4tEoU.js} +1 -1
- package/server/client/dist/assets/{File.es-CHtXXafR.js → File.es-DeTgvUnI.js} +1 -1
- package/server/client/dist/assets/{FileText.es-DPIwDCEe.js → FileText.es-C-bpw8dQ.js} +1 -1
- package/server/client/dist/assets/{FlowArrow.es-Dxee98ir.js → FlowArrow.es-CKcMBOmX.js} +1 -1
- package/server/client/dist/assets/{Graph-hVj6vAYJ.js → Graph-BNoSRJP3.js} +1 -1
- package/server/client/dist/assets/{Graph-CdEu8eAn.js → Graph-DYYxNAWn.js} +1 -1
- package/server/client/dist/assets/{Image.es-CjXY1JVf.js → Image.es-d4jWnijM.js} +1 -1
- package/server/client/dist/assets/{Info.es-CYxg-Vku.js → Info.es-DVzN2joA.js} +1 -1
- package/server/client/dist/assets/Integration-gDDtIJYJ.js +1 -0
- package/server/client/dist/assets/{LinkSimple.es-B-P-7Iem.js → LinkSimple.es-BD4aGTc3.js} +1 -1
- package/server/client/dist/assets/Note-FL31jb2U.js +19 -0
- package/server/client/dist/assets/{NotePropertiesPanel-6P7UfRBR.js → NotePropertiesPanel-os22Ppnu.js} +1 -1
- package/server/client/dist/assets/{Plus.es-Dy6onD-Z.js → Plus.es-Cl8zOOZa.js} +1 -1
- package/server/client/dist/assets/Reminders-Dt2JuGci.js +1 -0
- package/server/client/dist/assets/{Search-AaD1Mt0d.js → Search-CPPTW2PO.js} +2 -2
- package/server/client/dist/assets/{SurfaceCard-D5QxOte_.js → SurfaceCard-jL2qzrzF.js} +1 -1
- package/server/client/dist/assets/{Tag-B2Qiq1Hv.js → Tag-3DbGmuxa.js} +1 -1
- package/server/client/dist/assets/{TagNotes-B27G1ZVg.js → TagNotes-DLwRF8pT.js} +1 -1
- package/server/client/dist/assets/Trash.es-fxukjAO6.js +1 -0
- package/server/client/dist/assets/{ViewChip-BVkZAXyx.js → ViewChip-CVe_HNyd.js} +1 -1
- package/server/client/dist/assets/Views-8ghLRax7.js +1 -0
- package/server/client/dist/assets/{abnfDiagram-N423BO3Z-D44O4T7q.js → abnfDiagram-N423BO3Z-CPgGmCbn.js} +1 -1
- package/server/client/dist/assets/app-Bcl0U7r9.css +1 -0
- package/server/client/dist/assets/app-DzChh-V0.js +151 -0
- package/server/client/dist/assets/{arc-Cil9fXuW.js → arc-DqTgRpuF.js} +1 -1
- package/server/client/dist/assets/{architectureDiagram-T3A2C74G-Cjq6ayF2.js → architectureDiagram-T3A2C74G-rTYMAQ-J.js} +1 -1
- package/server/client/dist/assets/{blockDiagram-VBNYF7ZC-B2Xm5N0i.js → blockDiagram-VBNYF7ZC-BOluytVz.js} +1 -1
- package/server/client/dist/assets/{c4Diagram-5PPSVZJV-Dp0y-lCR.js → c4Diagram-5PPSVZJV-C8gbPpcb.js} +1 -1
- package/server/client/dist/assets/{calendar-data-wB0ifXdo.js → calendar-data-BQ24n348.js} +1 -1
- package/server/client/dist/assets/channel--aI9fJWX.js +1 -0
- package/server/client/dist/assets/{chunk-2GRJ4B5K-CWaaNkFx.js → chunk-2GRJ4B5K-CRER5Ka0.js} +1 -1
- package/server/client/dist/assets/{chunk-2Q5K7J3B-Ces-xz2S.js → chunk-2Q5K7J3B-hxmJWGu_.js} +1 -1
- package/server/client/dist/assets/{chunk-5RXB4S5H-B-4Chlfc.js → chunk-5RXB4S5H-Bpr5Us8Y.js} +1 -1
- package/server/client/dist/assets/{chunk-5VM5RSS4-C8_HVwuz.js → chunk-5VM5RSS4-CKUz9fAb.js} +1 -1
- package/server/client/dist/assets/{chunk-6Q2QTUOP-xZ8ofRqk.js → chunk-6Q2QTUOP-B8XEOHPN.js} +1 -1
- package/server/client/dist/assets/{chunk-GF5L2VYU-Bpa43a7s.js → chunk-GF5L2VYU-BBUD-HJi.js} +1 -1
- package/server/client/dist/assets/{chunk-JWPE2WC7-DsyTXbuq.js → chunk-JWPE2WC7-Dq6_B8-d.js} +1 -1
- package/server/client/dist/assets/{chunk-KBJHAD2P-BDhjm9-8.js → chunk-KBJHAD2P--2VZEwNJ.js} +1 -1
- package/server/client/dist/assets/{chunk-RYQCIY6F-Cn-jdp6I.js → chunk-RYQCIY6F-CKCwnKiC.js} +1 -1
- package/server/client/dist/assets/{chunk-XXDRQBXY-BKsc_0wh.js → chunk-XXDRQBXY-VDufR1Oi.js} +1 -1
- package/server/client/dist/assets/classDiagram-JCYQIIEL-BsbwE38V.js +1 -0
- package/server/client/dist/assets/classDiagram-v2-OCEON4UE-BsbwE38V.js +1 -0
- package/server/client/dist/assets/{cose-bilkent-JH36ORCC-jmn3Q4zj.js → cose-bilkent-JH36ORCC-D4nqOx8S.js} +1 -1
- package/server/client/dist/assets/{cynefinDiagram-MW4NZA55-BpLstuBJ.js → cynefinDiagram-MW4NZA55-CUuLMs-6.js} +1 -1
- package/server/client/dist/assets/{dagre-VZM6K2ZE-B5ft5Yzm.js → dagre-VZM6K2ZE-CUSkhZgS.js} +1 -1
- package/server/client/dist/assets/{diagram-7IWD3JNH-B_W9IsWZ.js → diagram-7IWD3JNH-D95eVcKx.js} +4 -4
- package/server/client/dist/assets/{diagram-B4RE2ZJO-32chS3GX.js → diagram-B4RE2ZJO-7OH_fb0K.js} +1 -1
- package/server/client/dist/assets/{diagram-LBJQPF4R-Bqupdb3-.js → diagram-LBJQPF4R-D9UwEIMd.js} +1 -1
- package/server/client/dist/assets/{diagram-Q27KOJAE-BD8jKBsv.js → diagram-Q27KOJAE-Dx7WFDuX.js} +1 -1
- package/server/client/dist/assets/{diagram-UB23O5K3-C40jAU8D.js → diagram-UB23O5K3-D2BYwAKT.js} +1 -1
- package/server/client/dist/assets/{ebnfDiagram-BXEA7PRR-BQfW9mFA.js → ebnfDiagram-BXEA7PRR-DbeA-fk4.js} +1 -1
- package/server/client/dist/assets/{erDiagram-JOGREHBK-ILZnLzz2.js → erDiagram-JOGREHBK-jWW-ICGO.js} +1 -1
- package/server/client/dist/assets/{flowDiagram-UKHOOZJN-CDmwr2dI.js → flowDiagram-UKHOOZJN-68iZdxxw.js} +1 -1
- package/server/client/dist/assets/{ganttDiagram-PKOTCBZU-DBcrUc49.js → ganttDiagram-PKOTCBZU-CxwY3VXg.js} +1 -1
- package/server/client/dist/assets/{gitGraphDiagram-DS77QQ5N-_xO6__FK.js → gitGraphDiagram-DS77QQ5N-Ci6o5MfZ.js} +1 -1
- package/server/client/dist/assets/{image.api-ChvlfuZg.js → image.api-CyIIEGkG.js} +1 -1
- package/server/client/dist/assets/index-Iy_BgP1R.js +1 -0
- package/server/client/dist/assets/{infoDiagram-6WML65LV-CuIIcu-L.js → infoDiagram-6WML65LV-DY7aMdjV.js} +1 -1
- package/server/client/dist/assets/integrations-LOdTs1T6.js +1 -0
- package/server/client/dist/assets/{ishikawaDiagram-WSZJBQD7-BVtRGTOj.js → ishikawaDiagram-WSZJBQD7-Bh5qKh1d.js} +1 -1
- package/server/client/dist/assets/{journeyDiagram-NVQOT4AX-D5-Nzb2N.js → journeyDiagram-NVQOT4AX-B_svXTTT.js} +1 -1
- package/server/client/dist/assets/{kanban-definition-27J2QSJJ-DmOhgC8H.js → kanban-definition-27J2QSJJ-CaJslHGg.js} +1 -1
- package/server/client/dist/assets/manage-image-DXedfr7U.js +1 -0
- package/server/client/dist/assets/{manage-image-detail-Dw3tAW3h.js → manage-image-detail-DmifOPNU.js} +1 -1
- package/server/client/dist/assets/{mcp-admin-adapter-D0R8p9az.js → mcp-admin-adapter-CmKsg1vd.js} +1 -1
- package/server/client/dist/assets/mcp-d8IzsoRz.js +5 -0
- package/server/client/dist/assets/{mermaid.core-C2tL9tq7.js → mermaid.core-BfUQM5vF.js} +4 -4
- package/server/client/dist/assets/{mindmap-definition-FAOFIHXS-DuvsPqrh.js → mindmap-definition-FAOFIHXS-DGbkJda2.js} +1 -1
- package/server/client/dist/assets/{note-core-YhYEmI_R.js → note-core-DRBsBf9H.js} +9 -9
- package/server/client/dist/assets/{note-runtime-B3ats-dC.js → note-runtime-ztZvXRIu.js} +16 -16
- package/server/client/dist/assets/{note-ui-BtzPk8gA.js → note-ui-DRnAnwKv.js} +1 -1
- package/server/client/dist/assets/{pegDiagram-VL7TDLO6-D64crEZV.js → pegDiagram-VL7TDLO6-Bip6zQct.js} +1 -1
- package/server/client/dist/assets/{pieDiagram-7S7Q4E2Y--kl7vopC.js → pieDiagram-7S7Q4E2Y-CjfjGY8Y.js} +1 -1
- package/server/client/dist/assets/{placeholder-B5uPsE7-.js → placeholder-G6iEBbDy.js} +1 -1
- package/server/client/dist/assets/{properties-B7Fv95qY.js → properties-BPobx_Qk.js} +1 -1
- package/server/client/dist/assets/{quadrantDiagram-CIZ2JOQS-DuvJ4M2y.js → quadrantDiagram-CIZ2JOQS-B6F1yUT-.js} +1 -1
- package/server/client/dist/assets/{railroadDiagram-AXF67PYL-BMKmCOY7.js → railroadDiagram-AXF67PYL-D-0fLe0F.js} +1 -1
- package/server/client/dist/assets/{requirementDiagram-LRYGKXZP-DY1eJcy0.js → requirementDiagram-LRYGKXZP-tm4mnCyJ.js} +1 -1
- package/server/client/dist/assets/route-preload.js +2 -2
- package/server/client/dist/assets/{sankeyDiagram-W5VNT64P-C3Ka4GKM.js → sankeyDiagram-W5VNT64P-DSBWFurb.js} +1 -1
- package/server/client/dist/assets/{search-ddYl3qH-.js → search-DanMKCC8.js} +1 -1
- package/server/client/dist/assets/{search-admin-adapter-Daf7e2YQ.js → search-admin-adapter-7sTcWTmv.js} +1 -1
- package/server/client/dist/assets/{sequenceDiagram-SI44F4Z6-Bhpwtk2s.js → sequenceDiagram-SI44F4Z6-KrwjjJIV.js} +1 -1
- package/server/client/dist/assets/{sizeCapture-X5ZJPWSS-BFta71TW.js → sizeCapture-X5ZJPWSS-DL2cn_Q6.js} +1 -1
- package/server/client/dist/assets/{stateDiagram-OKZ733FA-D6GCn1io.js → stateDiagram-OKZ733FA-CKuNDH85.js} +1 -1
- package/server/client/dist/assets/{stateDiagram-v2-UEYNNEHI-g4yinGDn.js → stateDiagram-v2-UEYNNEHI-BFaj2wbM.js} +1 -1
- package/server/client/dist/assets/{swimlanes-SLNWSIFB-DLFvOjUl.js → swimlanes-SLNWSIFB-5S17nqhK.js} +2 -2
- package/server/client/dist/assets/swimlanesDiagram-ULZ7WXOC-dexsHJq2.js +8 -0
- package/server/client/dist/assets/{tag.api-DEUROVKQ.js → tag.api-kKZl04yY.js} +1 -1
- package/server/client/dist/assets/{time-PxWila4-.js → time-BwwQGbK-.js} +38 -38
- package/server/client/dist/assets/{timeline-definition-Z64GVDOM-DTSIekSM.js → timeline-definition-Z64GVDOM-CU1hp_7d.js} +3 -3
- package/server/client/dist/assets/{trash-BHida7BG.js → trash-Cepvi7G8.js} +1 -1
- package/server/client/dist/assets/url-Bp8sXMb4.js +1 -0
- package/server/client/dist/assets/{useNoteMutate-BjvZUmtX.js → useNoteMutate-D-mZKXjl.js} +3 -3
- package/server/client/dist/assets/{useReminderMutate-C5n-5LM-.js → useReminderMutate-Cexk1ydX.js} +1 -1
- package/server/client/dist/assets/{vennDiagram-T6HMQDX7-CMNqSI2X.js → vennDiagram-T6HMQDX7-MoiVLnil.js} +1 -1
- package/server/client/dist/assets/{wardleyDiagram-T6FBY63Y-DV-JOYqB.js → wardleyDiagram-T6FBY63Y-cZkfdI6C.js} +1 -1
- package/server/client/dist/assets/{xychartDiagram-ELKLHX3M-BUIniF4m.js → xychartDiagram-ELKLHX3M-B63hxD-r.js} +1 -1
- package/server/client/dist/index.html +6 -6
- package/server/dist/app.js +2 -0
- package/server/dist/features/integration/auth.js +21 -0
- package/server/dist/features/integration/manifest.js +87 -0
- package/server/dist/features/integration/schema.js +54 -0
- package/server/dist/features/integration/service.js +173 -0
- package/server/dist/features/mcp-admin/service.js +19 -83
- package/server/dist/features/note/graphql/note.field.resolver.js +1 -1
- package/server/dist/features/note/graphql/note.query.resolver.js +6 -0
- package/server/dist/features/note/graphql/note.type-defs.js +13 -1
- package/server/dist/features/note/http/authoring.js +493 -0
- package/server/dist/features/note/http/mcp.js +15 -463
- package/server/dist/features/note/services/authoring.js +17 -1
- package/server/dist/features/note/services/markdown-intent-write.js +1 -0
- package/server/dist/features/note/services/markdown-read.js +85 -0
- package/server/dist/features/note/services/properties.js +36 -30
- package/server/dist/features/note/services/search.js +17 -0
- package/server/dist/features/search/graphql/resolver.js +10 -1
- package/server/dist/features/search/graphql/type-defs.js +3 -0
- package/server/dist/features/tag/graphql/tag.query.resolver.js +10 -0
- package/server/dist/features/tag/graphql/tag.type-defs.js +1 -0
- package/server/dist/features/view/graphql/mcp.query.resolver.js +22 -0
- package/server/dist/features/view/graphql/view.query.resolver.js +2 -0
- package/server/dist/features/view/graphql/view.type-defs.js +27 -0
- package/server/dist/features/view/services/mcp-read.js +142 -0
- package/server/dist/features/view/services/workspace.js +12 -4
- package/server/dist/modules/auth-guard.js +24 -1
- package/server/dist/modules/integration-token.js +27 -0
- package/server/dist/modules/mcp-auth.js +37 -23
- package/server/dist/modules/mcp-token.js +6 -25
- package/server/dist/routes/api.js +2 -0
- package/server/dist/routes/graphql.js +2 -1
- package/server/dist/routes/integrations.js +117 -0
- package/server/dist/routes/mcp.js +29 -9
- package/server/prisma/migrations/20260917120000_0020_plugin_installations/migration.sql +39 -0
- package/server/prisma/migrations/20260917150000_0021_integration_connections/migration.sql +29 -0
- package/server/prisma/schema.prisma +34 -10
- package/server/client/dist/assets/Note-CpzLtGVM.js +0 -19
- package/server/client/dist/assets/Reminders-DnuALBL6.js +0 -1
- package/server/client/dist/assets/Trash.es-XEcWG_aq.js +0 -1
- package/server/client/dist/assets/Views-B5oh8GVM.js +0 -1
- package/server/client/dist/assets/app-EwvT7bMw.css +0 -1
- package/server/client/dist/assets/app-NgiBDISG.js +0 -151
- package/server/client/dist/assets/channel-DZFalh-h.js +0 -1
- package/server/client/dist/assets/classDiagram-JCYQIIEL-CkEhFvVV.js +0 -1
- package/server/client/dist/assets/classDiagram-v2-OCEON4UE-CkEhFvVV.js +0 -1
- package/server/client/dist/assets/index-CIEap4bu.js +0 -1
- package/server/client/dist/assets/manage-image-CSJ3Flgc.js +0 -1
- package/server/client/dist/assets/mcp-D8footel.js +0 -5
- package/server/client/dist/assets/swimlanesDiagram-ULZ7WXOC-Ci4mcvmo.js +0 -8
- package/server/client/dist/assets/url-DKUMNVCI.js +0 -1
package/dist/mcp.js
CHANGED
|
@@ -1,61 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/mcp.ts
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
6
|
import fs from "fs";
|
|
5
7
|
import path from "path";
|
|
6
8
|
import { fileURLToPath } from "url";
|
|
7
|
-
import {
|
|
8
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
|
-
import { z as z3 } from "zod";
|
|
10
|
-
|
|
11
|
-
// src/mcp-note-output.ts
|
|
12
|
-
var formatBackReferenceLink = (backReference) => {
|
|
13
|
-
return `[[${backReference.title}]](note:${backReference.id})`;
|
|
14
|
-
};
|
|
15
|
-
var formatBackReferenceLine = (backReference) => {
|
|
16
|
-
const referenceLink = formatBackReferenceLink(backReference);
|
|
17
|
-
if (backReference.updatedAt) {
|
|
18
|
-
return `- ${referenceLink} (updated: ${backReference.updatedAt})`;
|
|
19
|
-
}
|
|
20
|
-
return `- ${referenceLink}`;
|
|
21
|
-
};
|
|
22
|
-
var formatPropertyLine = (property) => {
|
|
23
|
-
const displayValue = (property.option?.label ?? property.value) || "(empty)";
|
|
24
|
-
const rawValue = property.option?.value ?? property.value;
|
|
25
|
-
const rawValueLabel = rawValue ? `, value=${rawValue}` : "";
|
|
26
|
-
return `- ${property.key} (${property.name}): ${displayValue} [${property.valueType}${rawValueLabel}]`;
|
|
27
|
-
};
|
|
28
|
-
var formatMcpReadNoteOutput = ({
|
|
29
|
-
note,
|
|
30
|
-
backReferences,
|
|
31
|
-
maxLength
|
|
32
|
-
}) => {
|
|
33
|
-
let markdown = note.contentAsMarkdown;
|
|
34
|
-
const totalLength = markdown.length;
|
|
35
|
-
const truncated = maxLength > 0 && markdown.length > maxLength;
|
|
36
|
-
if (truncated) {
|
|
37
|
-
markdown = markdown.slice(0, maxLength);
|
|
38
|
-
}
|
|
39
|
-
const backReferenceLines = backReferences.length > 0 ? backReferences.map(formatBackReferenceLine) : ["- (none)"];
|
|
40
|
-
const propertyLines = note.properties && note.properties.length > 0 ? note.properties.map(formatPropertyLine) : ["- (none)"];
|
|
41
|
-
return [
|
|
42
|
-
`# ${note.title}`,
|
|
43
|
-
"",
|
|
44
|
-
`Tags: ${note.tags.map((tag) => tag.name).join(", ") || "(none)"}`,
|
|
45
|
-
"",
|
|
46
|
-
"Properties:",
|
|
47
|
-
...propertyLines,
|
|
48
|
-
`Created: ${note.createdAt}`,
|
|
49
|
-
`Updated: ${note.updatedAt}`,
|
|
50
|
-
...truncated ? [`Content: ${totalLength} chars (showing first ${maxLength})`] : [],
|
|
51
|
-
"",
|
|
52
|
-
"Back References:",
|
|
53
|
-
...backReferenceLines,
|
|
54
|
-
"",
|
|
55
|
-
markdown,
|
|
56
|
-
...truncated ? ["\n... (truncated, use maxLength: 0 to read full content)"] : []
|
|
57
|
-
].join("\n");
|
|
58
|
-
};
|
|
9
|
+
import { z as z6 } from "zod";
|
|
59
10
|
|
|
60
11
|
// src/mcp-intent-write-tools.ts
|
|
61
12
|
import { z as z2 } from "zod";
|
|
@@ -63,6 +14,14 @@ import { z as z2 } from "zod";
|
|
|
63
14
|
// src/mcp-tool-support.ts
|
|
64
15
|
import { z } from "zod";
|
|
65
16
|
var noteLayoutSchema = z.enum(["narrow", "wide", "full"]);
|
|
17
|
+
var pageLimitSchema = (defaultLimit, maximum = 100) => z.number().int().positive().default(defaultLimit).transform((value) => Math.min(value, maximum));
|
|
18
|
+
var pageOffsetSchema = z.number().int().nonnegative().default(0);
|
|
19
|
+
var createPageInfo = (totalCount, limit, offset) => ({
|
|
20
|
+
limit,
|
|
21
|
+
offset,
|
|
22
|
+
hasMore: offset + limit < totalCount,
|
|
23
|
+
nextOffset: offset + limit < totalCount ? offset + limit : null
|
|
24
|
+
});
|
|
66
25
|
var createMcpTextToolResult = (text) => ({
|
|
67
26
|
content: [
|
|
68
27
|
{
|
|
@@ -71,9 +30,11 @@ var createMcpTextToolResult = (text) => ({
|
|
|
71
30
|
}
|
|
72
31
|
]
|
|
73
32
|
});
|
|
74
|
-
var createMcpJsonToolResult = (value) => {
|
|
75
|
-
|
|
76
|
-
|
|
33
|
+
var createMcpJsonToolResult = (value) => ({
|
|
34
|
+
...createMcpTextToolResult(JSON.stringify(value, null, 2)),
|
|
35
|
+
structuredContent: value,
|
|
36
|
+
...value.status === "failed" || value.status === "needs_disambiguation" ? { isError: true } : {}
|
|
37
|
+
});
|
|
77
38
|
|
|
78
39
|
// src/mcp-intent-write-tools.ts
|
|
79
40
|
var markdownPatchSelectorSchema = z2.discriminatedUnion("type", [
|
|
@@ -119,8 +80,8 @@ var markdownAppendPlacementSchema = z2.discriminatedUnion("type", [
|
|
|
119
80
|
]);
|
|
120
81
|
var markdownWritePolicySchema = z2.object({
|
|
121
82
|
allowNoop: z2.boolean().optional(),
|
|
122
|
-
preserveTags: z2.union([z2.boolean(), z2.literal("warn")]).optional(),
|
|
123
|
-
preserveReferences: z2.union([z2.boolean(), z2.literal("warn")]).optional()
|
|
83
|
+
preserveTags: z2.union([z2.boolean(), z2.literal("warn")]).optional().describe("true blocks tag-token count decreases; warn reports decreases. Does not preserve individual tag identities."),
|
|
84
|
+
preserveReferences: z2.union([z2.boolean(), z2.literal("warn")]).optional().describe("true blocks reference-count decreases; warn reports decreases. Does not preserve individual reference identities.")
|
|
124
85
|
}).optional();
|
|
125
86
|
var markdownWriteBaselineFields = {
|
|
126
87
|
expectedUpdatedAt: z2.string().optional().describe("Expected note updatedAt from the read you based this edit on. Required unless baseMarkdownSha256 is provided."),
|
|
@@ -200,7 +161,7 @@ var registerIntentWriteTools = (server, {
|
|
|
200
161
|
operation,
|
|
201
162
|
...policy ? { policy } : {}
|
|
202
163
|
};
|
|
203
|
-
const result = await jsonRequest2(serverUrl, writeToken, "/api/
|
|
164
|
+
const result = await jsonRequest2(serverUrl, writeToken, "/api/integrations/v1/notes/patch-markdown", payload);
|
|
204
165
|
return createMcpJsonToolResult(result);
|
|
205
166
|
}
|
|
206
167
|
);
|
|
@@ -228,7 +189,7 @@ var registerIntentWriteTools = (server, {
|
|
|
228
189
|
...separator ? { separator } : {},
|
|
229
190
|
...policy ? { policy } : {}
|
|
230
191
|
};
|
|
231
|
-
const result = await jsonRequest2(serverUrl, writeToken, "/api/
|
|
192
|
+
const result = await jsonRequest2(serverUrl, writeToken, "/api/integrations/v1/notes/append-markdown", payload);
|
|
232
193
|
return createMcpJsonToolResult(result);
|
|
233
194
|
}
|
|
234
195
|
);
|
|
@@ -251,7 +212,7 @@ var registerIntentWriteTools = (server, {
|
|
|
251
212
|
...layout ? { layout } : {},
|
|
252
213
|
...properties ? { properties } : {}
|
|
253
214
|
};
|
|
254
|
-
const result = await jsonRequest2(serverUrl, writeToken, "/api/
|
|
215
|
+
const result = await jsonRequest2(serverUrl, writeToken, "/api/integrations/v1/notes/metadata", payload);
|
|
255
216
|
return createMcpJsonToolResult(result);
|
|
256
217
|
}
|
|
257
218
|
);
|
|
@@ -275,42 +236,328 @@ var registerIntentWriteTools = (server, {
|
|
|
275
236
|
replacement,
|
|
276
237
|
...policy ? { policy } : {}
|
|
277
238
|
};
|
|
278
|
-
const result = await jsonRequest2(serverUrl, writeToken, "/api/
|
|
239
|
+
const result = await jsonRequest2(serverUrl, writeToken, "/api/integrations/v1/notes/replace-markdown", payload);
|
|
279
240
|
return createMcpJsonToolResult(result);
|
|
280
241
|
}
|
|
281
242
|
);
|
|
282
243
|
};
|
|
283
244
|
|
|
284
|
-
// src/mcp-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
245
|
+
// src/mcp-note-output.ts
|
|
246
|
+
import { z as z3 } from "zod";
|
|
247
|
+
var formatBackReferenceLink = (backReference) => {
|
|
248
|
+
return `[[${backReference.title}]](note:${backReference.id})`;
|
|
249
|
+
};
|
|
250
|
+
var formatBackReferenceLine = (backReference) => {
|
|
251
|
+
const referenceLink = formatBackReferenceLink(backReference);
|
|
252
|
+
if (backReference.updatedAt) {
|
|
253
|
+
return `- ${referenceLink} (updated: ${backReference.updatedAt})`;
|
|
254
|
+
}
|
|
255
|
+
return `- ${referenceLink}`;
|
|
256
|
+
};
|
|
257
|
+
var formatPropertyLine = (property) => {
|
|
258
|
+
const displayValue = (property.option?.label ?? property.value) || "(empty)";
|
|
259
|
+
const rawValue = property.option?.value ?? property.value;
|
|
260
|
+
const rawValueLabel = rawValue ? `, value=${rawValue}` : "";
|
|
261
|
+
return `- ${property.key} (${property.name}): ${displayValue} [${property.valueType}${rawValueLabel}]`;
|
|
262
|
+
};
|
|
263
|
+
var formatMcpReadNoteOutput = ({
|
|
264
|
+
note,
|
|
265
|
+
backReferences,
|
|
266
|
+
maxLength
|
|
267
|
+
}) => {
|
|
268
|
+
let markdown = note.contentAsMarkdown;
|
|
269
|
+
const totalLength = markdown.length;
|
|
270
|
+
const truncated = maxLength > 0 && markdown.length > maxLength;
|
|
271
|
+
if (truncated) {
|
|
272
|
+
markdown = markdown.slice(0, maxLength);
|
|
273
|
+
}
|
|
274
|
+
const backReferenceLines = backReferences.length > 0 ? backReferences.map(formatBackReferenceLine) : ["- (none)"];
|
|
275
|
+
const propertyLines = note.properties && note.properties.length > 0 ? note.properties.map(formatPropertyLine) : ["- (none)"];
|
|
276
|
+
return [
|
|
277
|
+
`# ${note.title}`,
|
|
278
|
+
"",
|
|
279
|
+
`Tags: ${note.tags.map((tag) => tag.name).join(", ") || "(none)"}`,
|
|
280
|
+
"",
|
|
281
|
+
"Properties:",
|
|
282
|
+
...propertyLines,
|
|
283
|
+
`Created: ${note.createdAt}`,
|
|
284
|
+
`Updated: ${note.updatedAt}`,
|
|
285
|
+
...truncated ? [`Content: ${totalLength} chars (showing first ${maxLength})`] : [],
|
|
286
|
+
"",
|
|
287
|
+
"Back References:",
|
|
288
|
+
...backReferenceLines,
|
|
289
|
+
"",
|
|
290
|
+
markdown,
|
|
291
|
+
...truncated ? ["\n... (truncated, use maxLength: 0 to read full content)"] : []
|
|
292
|
+
].join("\n");
|
|
293
|
+
};
|
|
294
|
+
var readResultSchema = z3.object({
|
|
295
|
+
status: z3.enum(["read", "failed", "needs_disambiguation"]),
|
|
296
|
+
reason: z3.string().nullable(),
|
|
297
|
+
message: z3.string().nullable(),
|
|
298
|
+
note: z3.object({ id: z3.string(), title: z3.string(), createdAt: z3.string(), updatedAt: z3.string(), tags: z3.array(z3.object({ id: z3.string(), name: z3.string() })), properties: z3.array(z3.object({ key: z3.string(), name: z3.string(), value: z3.string(), valueType: z3.string(), option: z3.object({ label: z3.string(), value: z3.string() }).nullable() })) }),
|
|
299
|
+
markdown: z3.string(),
|
|
300
|
+
contentRange: z3.object({ start: z3.number(), end: z3.number(), totalLength: z3.number(), sectionEnd: z3.number(), hasMore: z3.boolean(), nextOffset: z3.number().nullable() }).nullable(),
|
|
301
|
+
candidates: z3.array(z3.object({ heading: z3.string(), level: z3.number(), start: z3.number(), end: z3.number() }))
|
|
302
|
+
});
|
|
303
|
+
var createMcpReadNoteResult = (raw, rawBackReferences) => {
|
|
304
|
+
const result = readResultSchema.parse(raw);
|
|
305
|
+
const backReferences = z3.array(z3.object({ id: z3.string(), title: z3.string() })).parse(rawBackReferences);
|
|
306
|
+
const structuredContent = { ...result, backReferences };
|
|
307
|
+
if (result.status !== "read") return createMcpJsonToolResult(structuredContent);
|
|
308
|
+
const range = result.contentRange;
|
|
309
|
+
const rangeText = range ? `
|
|
310
|
+
Content range: [${range.start}, ${range.end}) of ${range.totalLength} UTF-16 units; sectionEnd: ${range.sectionEnd}; nextOffset: ${range.nextOffset ?? "none"}` : "";
|
|
311
|
+
return {
|
|
312
|
+
...createMcpTextToolResult(formatMcpReadNoteOutput({ note: { ...result.note, contentAsMarkdown: result.markdown }, backReferences, maxLength: 0 }) + rangeText),
|
|
313
|
+
structuredContent
|
|
314
|
+
};
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// src/mcp-query-tools.ts
|
|
318
|
+
import { z as z4 } from "zod";
|
|
319
|
+
var propertyFilterSchema = z4.object({
|
|
320
|
+
key: z4.string().min(1),
|
|
321
|
+
valueType: z4.enum(["text", "url", "number", "date", "boolean", "select"]),
|
|
322
|
+
operator: z4.enum(["equals", "notEquals", "contains", "notContains", "before", "after", "exists", "notExists"]),
|
|
323
|
+
value: z4.string().nullable().optional().describe(
|
|
324
|
+
"Required except exists/notExists. select=option.value, date=YYYY-MM-DD, boolean=true/false. contains/notContains: text/url; before/after: date/number."
|
|
325
|
+
)
|
|
326
|
+
});
|
|
327
|
+
var queryConditionsSchema = z4.object({
|
|
328
|
+
tagNames: z4.array(z4.string()),
|
|
329
|
+
mode: z4.enum(["and", "or"]),
|
|
330
|
+
propertyFilters: z4.array(propertyFilterSchema.extend({ name: z4.string().optional() })),
|
|
331
|
+
sortBy: z4.enum(["updatedAt", "createdAt", "title"]),
|
|
332
|
+
sortOrder: z4.enum(["asc", "desc"])
|
|
333
|
+
});
|
|
334
|
+
var optionSchema = z4.object({
|
|
335
|
+
id: z4.string(),
|
|
336
|
+
label: z4.string(),
|
|
337
|
+
value: z4.string(),
|
|
338
|
+
color: z4.string().nullable(),
|
|
339
|
+
order: z4.number()
|
|
340
|
+
});
|
|
341
|
+
var noteSummarySchema = z4.object({
|
|
342
|
+
id: z4.string(),
|
|
343
|
+
title: z4.string(),
|
|
344
|
+
createdAt: z4.string(),
|
|
345
|
+
updatedAt: z4.string(),
|
|
346
|
+
tags: z4.array(z4.object({ id: z4.string(), name: z4.string() })),
|
|
347
|
+
properties: z4.array(
|
|
348
|
+
z4.object({
|
|
349
|
+
key: z4.string(),
|
|
350
|
+
name: z4.string(),
|
|
351
|
+
value: z4.string(),
|
|
352
|
+
valueType: z4.string(),
|
|
353
|
+
option: optionSchema.nullable()
|
|
354
|
+
})
|
|
355
|
+
).optional()
|
|
356
|
+
});
|
|
357
|
+
var pageInfoSchema = z4.object({
|
|
358
|
+
limit: z4.number(),
|
|
359
|
+
offset: z4.number(),
|
|
360
|
+
hasMore: z4.boolean(),
|
|
361
|
+
nextOffset: z4.number().nullable()
|
|
362
|
+
});
|
|
363
|
+
var propertyKeysSchema = z4.array(z4.string().trim().min(1)).max(50).transform((keys) => [...new Set(keys.map((key) => key.toLowerCase().replace(/\s+/g, "-")))]);
|
|
364
|
+
var NOTE_SUMMARY_FIELDS = "id title createdAt updatedAt tags { id name } properties(keys: $propertyKeys) { key name value valueType option { id label value color order } }";
|
|
365
|
+
var QUERY_CONDITION_FIELDS = "tagNames mode propertyFilters { key name valueType operator value } sortBy sortOrder";
|
|
366
|
+
var registerQueryTools = (server, request) => {
|
|
367
|
+
server.registerTool(
|
|
368
|
+
"ocean_brain_query_notes",
|
|
369
|
+
{
|
|
370
|
+
description: "Query notes using tags and property conditions; no filters returns recent notes. mode combines tags only; property filters always use AND. Use list_properties for valid keys/types/options. Only requested propertyKeys are returned. No note bodies.",
|
|
371
|
+
inputSchema: {
|
|
372
|
+
tagNames: z4.array(z4.string().trim().min(1)).max(100).default([]),
|
|
373
|
+
mode: z4.enum(["and", "or"]).default("and"),
|
|
374
|
+
propertyFilters: z4.array(propertyFilterSchema).max(10).default([]),
|
|
375
|
+
sortBy: z4.enum(["updatedAt", "createdAt", "title"]).default("updatedAt"),
|
|
376
|
+
sortOrder: z4.enum(["asc", "desc"]).default("desc"),
|
|
377
|
+
propertyKeys: propertyKeysSchema.default([]),
|
|
378
|
+
limit: pageLimitSchema(20, 50),
|
|
379
|
+
offset: pageOffsetSchema
|
|
380
|
+
},
|
|
381
|
+
outputSchema: {
|
|
382
|
+
query: queryConditionsSchema,
|
|
383
|
+
totalCount: z4.number(),
|
|
384
|
+
notes: z4.array(noteSummarySchema),
|
|
385
|
+
missingTags: z4.array(z4.string()),
|
|
386
|
+
page: pageInfoSchema
|
|
387
|
+
},
|
|
388
|
+
annotations: { readOnlyHint: true }
|
|
389
|
+
},
|
|
390
|
+
async ({ limit, offset, propertyKeys, ...input }) => {
|
|
391
|
+
const data = await request(
|
|
392
|
+
`query ($input: NotesByPropertiesInput!, $names: [String!]!, $pagination: PaginationInput!, $propertyKeys: [String!]) {
|
|
393
|
+
notesByQuery(input: $input, pagination: $pagination) { query { ${QUERY_CONDITION_FIELDS} } totalCount notes { ${NOTE_SUMMARY_FIELDS} } }
|
|
394
|
+
tagsByNames(names: $names) { name }
|
|
395
|
+
}`,
|
|
396
|
+
{
|
|
397
|
+
input,
|
|
398
|
+
names: input.tagNames,
|
|
399
|
+
propertyKeys,
|
|
400
|
+
pagination: { limit, offset }
|
|
401
|
+
}
|
|
402
|
+
);
|
|
403
|
+
const result = z4.object({
|
|
404
|
+
query: queryConditionsSchema,
|
|
405
|
+
totalCount: z4.number(),
|
|
406
|
+
notes: z4.array(noteSummarySchema)
|
|
407
|
+
}).parse(data?.notesByQuery);
|
|
408
|
+
const tags = z4.array(z4.object({ name: z4.string() })).parse(data?.tagsByNames);
|
|
409
|
+
return createMcpJsonToolResult({
|
|
410
|
+
...result,
|
|
411
|
+
missingTags: result.query.tagNames.filter((name) => !tags.some((tag) => tag.name === name)),
|
|
412
|
+
page: createPageInfo(result.totalCount, limit, offset)
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
);
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
// src/mcp-view-tools.ts
|
|
419
|
+
import { z as z5 } from "zod";
|
|
420
|
+
var sectionSchema = queryConditionsSchema.extend({
|
|
421
|
+
id: z5.string(),
|
|
422
|
+
tabId: z5.string(),
|
|
423
|
+
title: z5.string(),
|
|
424
|
+
displayType: z5.enum(["list", "table", "board", "calendar"]),
|
|
425
|
+
limit: z5.number(),
|
|
426
|
+
order: z5.number(),
|
|
427
|
+
displayOptions: z5.object({
|
|
428
|
+
tableColumns: z5.array(z5.string()),
|
|
429
|
+
tablePropertyKeys: z5.array(z5.string()),
|
|
430
|
+
boardGroupByPropertyKey: z5.string().nullable(),
|
|
431
|
+
calendarDateField: z5.enum(["createdAt", "updatedAt", "property"]),
|
|
432
|
+
calendarDatePropertyKey: z5.string().nullable()
|
|
433
|
+
})
|
|
307
434
|
});
|
|
435
|
+
var SECTION_FIELDS = `id tabId title displayType limit order ${QUERY_CONDITION_FIELDS} displayOptions { tableColumns tablePropertyKeys boardGroupByPropertyKey calendarDateField calendarDatePropertyKey }`;
|
|
436
|
+
var viewReadSchema = z5.object({
|
|
437
|
+
section: sectionSchema,
|
|
438
|
+
totalCount: z5.number(),
|
|
439
|
+
rows: z5.array(
|
|
440
|
+
z5.object({
|
|
441
|
+
note: noteSummarySchema,
|
|
442
|
+
groupValue: z5.string().nullable(),
|
|
443
|
+
calendarDate: z5.string().nullable()
|
|
444
|
+
})
|
|
445
|
+
),
|
|
446
|
+
groupProperty: z5.object({
|
|
447
|
+
key: z5.string(),
|
|
448
|
+
name: z5.string(),
|
|
449
|
+
options: z5.array(optionSchema)
|
|
450
|
+
}).nullable()
|
|
451
|
+
});
|
|
452
|
+
var registerViewTools = (server, request) => {
|
|
453
|
+
server.registerTool(
|
|
454
|
+
"ocean_brain_list_views",
|
|
455
|
+
{
|
|
456
|
+
description: "Find saved view sections by section or tab title. Returns IDs, saved conditions and display settings without loading notes or switching the active tab.",
|
|
457
|
+
inputSchema: {
|
|
458
|
+
query: z5.string().default(""),
|
|
459
|
+
limit: pageLimitSchema(20, 50),
|
|
460
|
+
offset: pageOffsetSchema
|
|
461
|
+
},
|
|
462
|
+
outputSchema: {
|
|
463
|
+
totalCount: z5.number(),
|
|
464
|
+
views: z5.array(
|
|
465
|
+
z5.object({
|
|
466
|
+
sectionId: z5.string(),
|
|
467
|
+
tabTitle: z5.string(),
|
|
468
|
+
section: sectionSchema
|
|
469
|
+
})
|
|
470
|
+
),
|
|
471
|
+
page: pageInfoSchema
|
|
472
|
+
},
|
|
473
|
+
annotations: { readOnlyHint: true }
|
|
474
|
+
},
|
|
475
|
+
async ({ query, limit, offset }) => {
|
|
476
|
+
const data = await request(
|
|
477
|
+
`query ($query: String, $pagination: PaginationInput) { viewSections(query: $query, pagination: $pagination) { totalCount sections { tabTitle section { ${SECTION_FIELDS} } } } }`,
|
|
478
|
+
{ query, pagination: { limit, offset } }
|
|
479
|
+
);
|
|
480
|
+
const result = z5.object({
|
|
481
|
+
totalCount: z5.number(),
|
|
482
|
+
sections: z5.array(z5.object({ tabTitle: z5.string(), section: sectionSchema }))
|
|
483
|
+
}).parse(data?.viewSections);
|
|
484
|
+
return createMcpJsonToolResult({
|
|
485
|
+
totalCount: result.totalCount,
|
|
486
|
+
views: result.sections.map((view) => ({
|
|
487
|
+
...view,
|
|
488
|
+
sectionId: view.section.id
|
|
489
|
+
})),
|
|
490
|
+
page: createPageInfo(result.totalCount, limit, offset)
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
);
|
|
494
|
+
server.registerTool(
|
|
495
|
+
"ocean_brain_read_view",
|
|
496
|
+
{
|
|
497
|
+
description: "Read a saved view with its conditions, display settings and paged note rows. Board: omit groupValue for all columns; null means unclassified. Calendar: dateRange is required ([start,end), at most 32 days); saved date field controls order. propertyKeys overrides table columns; omit to use saved table property columns. section.limit is the UI display limit, separate from page.limit.",
|
|
498
|
+
inputSchema: {
|
|
499
|
+
sectionId: z5.string().min(1),
|
|
500
|
+
limit: pageLimitSchema(20, 50),
|
|
501
|
+
offset: pageOffsetSchema,
|
|
502
|
+
propertyKeys: propertyKeysSchema.optional(),
|
|
503
|
+
groupValue: z5.string().nullable().optional(),
|
|
504
|
+
dateRange: z5.object({ start: z5.string(), end: z5.string() }).optional()
|
|
505
|
+
},
|
|
506
|
+
outputSchema: {
|
|
507
|
+
...viewReadSchema.shape,
|
|
508
|
+
selection: z5.object({
|
|
509
|
+
propertyKeys: z5.array(z5.string()),
|
|
510
|
+
groupValue: z5.string().nullable().optional(),
|
|
511
|
+
dateRange: z5.object({ start: z5.string(), end: z5.string() }).optional()
|
|
512
|
+
}),
|
|
513
|
+
page: pageInfoSchema
|
|
514
|
+
},
|
|
515
|
+
annotations: { readOnlyHint: true }
|
|
516
|
+
},
|
|
517
|
+
async ({ sectionId, limit, offset, propertyKeys, groupValue, dateRange }) => {
|
|
518
|
+
const data = await request(
|
|
519
|
+
`query ($id: ID!, $pagination: PaginationInput, $propertyKeys: [String!], $groupValue: String, $dateRange: DateRangeInput) {
|
|
520
|
+
readViewSection(id: $id, pagination: $pagination, propertyKeys: $propertyKeys, groupValue: $groupValue, dateRange: $dateRange) {
|
|
521
|
+
section { ${SECTION_FIELDS} } totalCount groupProperty { key name options { id label value color order } }
|
|
522
|
+
rows { groupValue calendarDate note { ${NOTE_SUMMARY_FIELDS} } }
|
|
523
|
+
}
|
|
524
|
+
}`,
|
|
525
|
+
{
|
|
526
|
+
id: sectionId,
|
|
527
|
+
pagination: { limit, offset },
|
|
528
|
+
propertyKeys,
|
|
529
|
+
groupValue,
|
|
530
|
+
dateRange
|
|
531
|
+
}
|
|
532
|
+
);
|
|
533
|
+
const result = viewReadSchema.parse(data?.readViewSection);
|
|
534
|
+
const keys = propertyKeys ?? (result.section.displayType === "table" ? result.section.displayOptions.tablePropertyKeys : []);
|
|
535
|
+
const selection = {
|
|
536
|
+
propertyKeys: keys,
|
|
537
|
+
...groupValue !== void 0 ? {
|
|
538
|
+
groupValue: groupValue === null ? null : groupValue.trim().toLowerCase().replace(/\s+/g, "-")
|
|
539
|
+
} : {},
|
|
540
|
+
...dateRange ? { dateRange } : {}
|
|
541
|
+
};
|
|
542
|
+
return createMcpJsonToolResult({
|
|
543
|
+
...result,
|
|
544
|
+
selection,
|
|
545
|
+
rows: result.rows.map((row) => ({
|
|
546
|
+
...row,
|
|
547
|
+
note: {
|
|
548
|
+
...row.note,
|
|
549
|
+
properties: row.note.properties?.filter((property) => keys.includes(property.key))
|
|
550
|
+
}
|
|
551
|
+
})),
|
|
552
|
+
page: createPageInfo(result.totalCount, limit, offset)
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
);
|
|
556
|
+
};
|
|
308
557
|
|
|
309
558
|
// src/mcp.ts
|
|
310
559
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
311
|
-
var pkg = JSON.parse(
|
|
312
|
-
fs.readFileSync(path.resolve(__dirname, "..", "package.json"), "utf-8")
|
|
313
|
-
);
|
|
560
|
+
var pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, "..", "package.json"), "utf-8"));
|
|
314
561
|
if (!pkg.oceanBrain?.mcpCompatibilityVersion) {
|
|
315
562
|
throw new Error("Ocean Brain MCP compatibility version is required in package metadata.");
|
|
316
563
|
}
|
|
@@ -326,6 +573,9 @@ var createMcpRequestHeaders = (token) => ({
|
|
|
326
573
|
...token ? { Authorization: `Bearer ${token}` } : {}
|
|
327
574
|
});
|
|
328
575
|
var OCEAN_BRAIN_MCP_TOOLS = {
|
|
576
|
+
queryNotes: "ocean_brain_query_notes",
|
|
577
|
+
listViews: "ocean_brain_list_views",
|
|
578
|
+
readView: "ocean_brain_read_view",
|
|
329
579
|
searchNotes: "ocean_brain_search_notes",
|
|
330
580
|
readNote: "ocean_brain_read_note",
|
|
331
581
|
createNote: "ocean_brain_create_note",
|
|
@@ -335,20 +585,9 @@ var OCEAN_BRAIN_MCP_TOOLS = {
|
|
|
335
585
|
replaceNoteMarkdown: "ocean_brain_replace_note_markdown",
|
|
336
586
|
listTags: "ocean_brain_list_tags",
|
|
337
587
|
listProperties: "ocean_brain_list_properties",
|
|
338
|
-
queryNotesByProperties: "ocean_brain_query_notes_by_properties",
|
|
339
|
-
listNotesByTag: "ocean_brain_list_notes_by_tag",
|
|
340
|
-
listNotesByTags: "ocean_brain_list_notes_by_tags",
|
|
341
|
-
listRecentNotes: "ocean_brain_list_recent_notes",
|
|
342
|
-
findNoteCleanupCandidates: "ocean_brain_find_note_cleanup_candidates",
|
|
343
|
-
createTag: "ocean_brain_create_tag",
|
|
344
588
|
deleteNote: "ocean_brain_delete_note"
|
|
345
589
|
};
|
|
346
|
-
var
|
|
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"]);
|
|
351
|
-
var searchModeSchema = z3.enum(["hybrid", "lexical", "semantic"]);
|
|
590
|
+
var searchModeSchema = z6.enum(["hybrid", "lexical", "semantic"]);
|
|
352
591
|
var graphqlSearchModes = {
|
|
353
592
|
hybrid: "HYBRID",
|
|
354
593
|
lexical: "LEXICAL",
|
|
@@ -367,51 +606,8 @@ var formatMcpGraphqlError = (error) => {
|
|
|
367
606
|
}
|
|
368
607
|
return `GraphQL error: ${error.message} (${suffix.join(", ")})`;
|
|
369
608
|
};
|
|
370
|
-
var propertyFilterSchema = z3.object({
|
|
371
|
-
key: z3.string().describe("Property key from ocean_brain_list_properties, e.g. state"),
|
|
372
|
-
valueType: propertyValueTypeSchema.describe("Property value type from the property definition"),
|
|
373
|
-
operator: propertyFilterOperatorSchema.describe("Filter operator. before/after are only valid for date or number properties."),
|
|
374
|
-
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).")
|
|
375
|
-
});
|
|
376
|
-
var normalizeOceanBrainTagName = (name) => {
|
|
377
|
-
const trimmedName = name.trim();
|
|
378
|
-
if (!trimmedName) {
|
|
379
|
-
throw new Error("Tag name is required.");
|
|
380
|
-
}
|
|
381
|
-
if (trimmedName.startsWith("#")) {
|
|
382
|
-
throw new Error("Ocean Brain tags use @, not #. Example: @project");
|
|
383
|
-
}
|
|
384
|
-
const normalizedName = trimmedName.startsWith("@") ? trimmedName : `@${trimmedName}`;
|
|
385
|
-
if (normalizedName === "@" || /\s/.test(normalizedName.slice(1))) {
|
|
386
|
-
throw new Error("Ocean Brain tag names must be a single token. Example: @project");
|
|
387
|
-
}
|
|
388
|
-
return normalizedName;
|
|
389
|
-
};
|
|
390
|
-
var normalizeOceanBrainTagNames = (names) => {
|
|
391
|
-
return Array.from(
|
|
392
|
-
new Set(names.map(normalizeOceanBrainTagName))
|
|
393
|
-
);
|
|
394
|
-
};
|
|
395
|
-
var fetchOceanBrainTags = async (serverUrl, token, query, graphqlRequest = graphql) => {
|
|
396
|
-
const data = await graphqlRequest(serverUrl, token, `
|
|
397
|
-
query ($searchFilter: SearchFilterInput, $pagination: PaginationInput) {
|
|
398
|
-
allTags(searchFilter: $searchFilter, pagination: $pagination) {
|
|
399
|
-
totalCount
|
|
400
|
-
tags {
|
|
401
|
-
id
|
|
402
|
-
name
|
|
403
|
-
referenceCount
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
`, {
|
|
408
|
-
searchFilter: { query },
|
|
409
|
-
pagination: { limit: 100, offset: 0 }
|
|
410
|
-
});
|
|
411
|
-
return data?.allTags;
|
|
412
|
-
};
|
|
413
609
|
async function graphql(serverUrl, token, query, variables) {
|
|
414
|
-
const response = await fetch(`${serverUrl}/graphql
|
|
610
|
+
const response = await fetch(`${serverUrl}/api/integrations/v1/graphql`, {
|
|
415
611
|
method: "POST",
|
|
416
612
|
headers: createMcpRequestHeaders(token),
|
|
417
613
|
body: JSON.stringify({ query, variables })
|
|
@@ -455,17 +651,25 @@ var formatMcpSearchNotes = (notes) => notes.map((note) => ({
|
|
|
455
651
|
}));
|
|
456
652
|
var registerMcpTools = (server, serverUrl, token, options = {}) => {
|
|
457
653
|
const graphqlRequest = options.graphqlRequest ?? graphql;
|
|
654
|
+
const queryRequest = (query, variables) => graphqlRequest(serverUrl, token, query, variables);
|
|
655
|
+
registerQueryTools(server, queryRequest);
|
|
656
|
+
registerViewTools(server, queryRequest);
|
|
458
657
|
server.tool(
|
|
459
658
|
OCEAN_BRAIN_MCP_TOOLS.searchNotes,
|
|
460
659
|
"Search Ocean Brain notes by keyword or meaning. Lexical uses words only, hybrid combines words and meaning, and semantic uses meaning only. Results use one ranked result shape with match and semantic-search status. Use ocean_brain_read_note to get full content for a specific note.",
|
|
461
660
|
{
|
|
462
|
-
query:
|
|
463
|
-
limit:
|
|
464
|
-
|
|
661
|
+
query: z6.string().describe("Search words or a natural-language description"),
|
|
662
|
+
limit: pageLimitSchema(10, 50).describe(
|
|
663
|
+
"Results per page. Search returns at most 50 per page. (default: 10)"
|
|
664
|
+
),
|
|
665
|
+
offset: z6.number().int().min(0).optional().default(0).describe("Results to skip for pagination (default: 0)"),
|
|
465
666
|
mode: searchModeSchema.default("hybrid").describe("Search mode. Defaults to hybrid.")
|
|
466
667
|
},
|
|
467
668
|
async ({ query, limit, offset, mode }) => {
|
|
468
|
-
const data = await graphqlRequest(
|
|
669
|
+
const data = await graphqlRequest(
|
|
670
|
+
serverUrl,
|
|
671
|
+
token,
|
|
672
|
+
`
|
|
469
673
|
query ($query: String!, $mode: SearchMode!, $pagination: PaginationInput!) {
|
|
470
674
|
searchNotes(query: $query, mode: $mode, pagination: $pagination) {
|
|
471
675
|
totalCount
|
|
@@ -476,6 +680,7 @@ var registerMcpTools = (server, serverUrl, token, options = {}) => {
|
|
|
476
680
|
noteId
|
|
477
681
|
lexical
|
|
478
682
|
semantic
|
|
683
|
+
excerpt { text source start end }
|
|
479
684
|
}
|
|
480
685
|
notes {
|
|
481
686
|
id
|
|
@@ -486,11 +691,13 @@ var registerMcpTools = (server, serverUrl, token, options = {}) => {
|
|
|
486
691
|
}
|
|
487
692
|
}
|
|
488
693
|
}
|
|
489
|
-
`,
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
694
|
+
`,
|
|
695
|
+
{
|
|
696
|
+
query,
|
|
697
|
+
mode: graphqlSearchModes[mode],
|
|
698
|
+
pagination: { limit, offset }
|
|
699
|
+
}
|
|
700
|
+
);
|
|
494
701
|
const result = data?.searchNotes;
|
|
495
702
|
return createMcpJsonToolResult({
|
|
496
703
|
totalCount: result.totalCount,
|
|
@@ -498,59 +705,68 @@ var registerMcpTools = (server, serverUrl, token, options = {}) => {
|
|
|
498
705
|
semanticUsed: result.semanticUsed,
|
|
499
706
|
semanticError: result.semanticError,
|
|
500
707
|
matches: result.matches,
|
|
708
|
+
page: createPageInfo(result.totalCount, limit, offset),
|
|
501
709
|
notes: formatMcpSearchNotes(result.notes)
|
|
502
710
|
});
|
|
503
711
|
}
|
|
504
712
|
);
|
|
505
713
|
server.tool(
|
|
506
714
|
OCEAN_BRAIN_MCP_TOOLS.readNote,
|
|
507
|
-
"Read
|
|
715
|
+
"Read note metadata, markdown and back references. Default 1000 UTF-16 units; maxLength: 0 reads the remaining body or whole selected section. Use offset or exact heading, never both. For continuation pass contentRange.nextOffset and expectedUpdatedAt from this read; sectionEnd bounds a selected section. Ambiguous headings return positions. Prefer structuredContent for code consumers.",
|
|
508
716
|
{
|
|
509
|
-
id:
|
|
510
|
-
maxLength:
|
|
717
|
+
id: z6.string().min(1),
|
|
718
|
+
maxLength: z6.number().int().nonnegative().default(1e3),
|
|
719
|
+
offset: z6.number().int().nonnegative().optional(),
|
|
720
|
+
heading: z6.string().min(1).optional(),
|
|
721
|
+
expectedUpdatedAt: z6.string().optional()
|
|
511
722
|
},
|
|
512
|
-
async (
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
id
|
|
526
|
-
title
|
|
723
|
+
async (input) => {
|
|
724
|
+
if (input.heading !== void 0 && input.offset !== void 0)
|
|
725
|
+
throw new Error("Use either heading or offset, not both.");
|
|
726
|
+
const data = await graphqlRequest(
|
|
727
|
+
serverUrl,
|
|
728
|
+
token,
|
|
729
|
+
`
|
|
730
|
+
query ($id: ID!, $maxLength: Int, $offset: Int, $heading: String, $expectedUpdatedAt: String) {
|
|
731
|
+
noteRead(id: $id, maxLength: $maxLength, offset: $offset, heading: $heading, expectedUpdatedAt: $expectedUpdatedAt) {
|
|
732
|
+
status reason message markdown
|
|
733
|
+
candidates { heading level start end }
|
|
734
|
+
contentRange { start end totalLength sectionEnd hasMore nextOffset }
|
|
735
|
+
note { id title createdAt updatedAt tags { id name } properties { key name value valueType option { label value } } }
|
|
527
736
|
}
|
|
737
|
+
backReferences(id: $id) { id title }
|
|
528
738
|
}
|
|
529
|
-
`,
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
note,
|
|
534
|
-
backReferences,
|
|
535
|
-
maxLength
|
|
536
|
-
});
|
|
537
|
-
return createMcpTextToolResult(output);
|
|
739
|
+
`,
|
|
740
|
+
input
|
|
741
|
+
);
|
|
742
|
+
return createMcpReadNoteResult(data?.noteRead, data?.backReferences);
|
|
538
743
|
}
|
|
539
744
|
);
|
|
540
745
|
server.tool(
|
|
541
746
|
OCEAN_BRAIN_MCP_TOOLS.createNote,
|
|
542
747
|
"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.",
|
|
543
748
|
{
|
|
544
|
-
title:
|
|
545
|
-
markdown:
|
|
546
|
-
|
|
749
|
+
title: z6.string().describe("Note title"),
|
|
750
|
+
markdown: z6.string().optional().default("").describe(
|
|
751
|
+
"Markdown body for the new note. In MCP markdown, tags must use [@tag]. Defaults to an empty note body."
|
|
752
|
+
),
|
|
753
|
+
layout: noteLayoutSchema.optional().describe(
|
|
754
|
+
"Optional note layout: narrow, wide, or full. Prefer wide for most notes unless the user explicitly wants narrow or full."
|
|
755
|
+
),
|
|
756
|
+
properties: z6.object({
|
|
757
|
+
set: z6.array(z6.object({ key: z6.string().min(1), value: z6.string() })).max(50)
|
|
758
|
+
}).strict().optional().describe(
|
|
759
|
+
"Set existing properties atomically with creation. Same keys/values as update_note_metadata; definitions and select options must already exist."
|
|
760
|
+
)
|
|
547
761
|
},
|
|
548
|
-
async ({ title, markdown, layout }) => {
|
|
762
|
+
async ({ title, markdown, layout, properties }) => {
|
|
763
|
+
if (properties) metadataPropertyPatchSchema.parse(properties);
|
|
549
764
|
const writeToken = requireWriteToken(token, OCEAN_BRAIN_MCP_TOOLS.createNote);
|
|
550
|
-
const result = await jsonRequest(serverUrl, writeToken, "/api/
|
|
765
|
+
const result = await jsonRequest(serverUrl, writeToken, "/api/integrations/v1/notes/create", {
|
|
551
766
|
title,
|
|
552
767
|
markdown,
|
|
553
|
-
...layout ? { layout } : {}
|
|
768
|
+
...layout ? { layout } : {},
|
|
769
|
+
...properties ? { properties } : {}
|
|
554
770
|
});
|
|
555
771
|
return createMcpJsonToolResult(result);
|
|
556
772
|
}
|
|
@@ -564,10 +780,17 @@ var registerMcpTools = (server, serverUrl, token, options = {}) => {
|
|
|
564
780
|
});
|
|
565
781
|
server.tool(
|
|
566
782
|
OCEAN_BRAIN_MCP_TOOLS.listTags,
|
|
567
|
-
"
|
|
568
|
-
{
|
|
569
|
-
|
|
570
|
-
|
|
783
|
+
"Search and page through Ocean Brain tags that are used by notes, with note counts.",
|
|
784
|
+
{
|
|
785
|
+
query: z6.string().default("").describe("Tag name search"),
|
|
786
|
+
limit: pageLimitSchema(100),
|
|
787
|
+
offset: pageOffsetSchema
|
|
788
|
+
},
|
|
789
|
+
async ({ query, limit, offset }) => {
|
|
790
|
+
const data = await graphqlRequest(
|
|
791
|
+
serverUrl,
|
|
792
|
+
token,
|
|
793
|
+
`
|
|
571
794
|
query ($searchFilter: SearchFilterInput, $pagination: PaginationInput) {
|
|
572
795
|
allTags(searchFilter: $searchFilter, pagination: $pagination) {
|
|
573
796
|
totalCount
|
|
@@ -578,24 +801,32 @@ var registerMcpTools = (server, serverUrl, token, options = {}) => {
|
|
|
578
801
|
}
|
|
579
802
|
}
|
|
580
803
|
}
|
|
581
|
-
`,
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
804
|
+
`,
|
|
805
|
+
{
|
|
806
|
+
searchFilter: { query },
|
|
807
|
+
pagination: { limit, offset }
|
|
808
|
+
}
|
|
809
|
+
);
|
|
585
810
|
const result = data?.allTags;
|
|
586
|
-
return createMcpJsonToolResult(
|
|
811
|
+
return createMcpJsonToolResult({
|
|
812
|
+
...result,
|
|
813
|
+
page: createPageInfo(result.totalCount, limit, offset)
|
|
814
|
+
});
|
|
587
815
|
}
|
|
588
816
|
);
|
|
589
817
|
server.tool(
|
|
590
818
|
OCEAN_BRAIN_MCP_TOOLS.listProperties,
|
|
591
819
|
"List shared Ocean Brain property definitions. Use this before property queries so keys, value types, and select option values are valid.",
|
|
592
820
|
{
|
|
593
|
-
query:
|
|
594
|
-
limit:
|
|
595
|
-
offset:
|
|
821
|
+
query: z6.string().optional().default("").describe("Optional property key/name search query"),
|
|
822
|
+
limit: pageLimitSchema(50, 100).describe("Max results (default: 50, server max: 100)"),
|
|
823
|
+
offset: pageOffsetSchema.describe("Pagination offset (default: 0)")
|
|
596
824
|
},
|
|
597
825
|
async ({ query, limit, offset }) => {
|
|
598
|
-
const data = await graphqlRequest(
|
|
826
|
+
const data = await graphqlRequest(
|
|
827
|
+
serverUrl,
|
|
828
|
+
token,
|
|
829
|
+
`
|
|
599
830
|
query ($query: String, $pagination: PaginationInput) {
|
|
600
831
|
notePropertyKeys(query: $query, pagination: $pagination) {
|
|
601
832
|
totalCount
|
|
@@ -609,301 +840,30 @@ var registerMcpTools = (server, serverUrl, token, options = {}) => {
|
|
|
609
840
|
}
|
|
610
841
|
}
|
|
611
842
|
}
|
|
612
|
-
`,
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
843
|
+
`,
|
|
844
|
+
{
|
|
845
|
+
query,
|
|
846
|
+
pagination: { limit, offset }
|
|
847
|
+
}
|
|
848
|
+
);
|
|
616
849
|
const result = data?.notePropertyKeys;
|
|
617
850
|
return createMcpJsonToolResult({
|
|
618
851
|
totalCount: result.totalCount,
|
|
619
|
-
propertyKeys: result.keys
|
|
620
|
-
|
|
621
|
-
}
|
|
622
|
-
);
|
|
623
|
-
server.tool(
|
|
624
|
-
OCEAN_BRAIN_MCP_TOOLS.listNotesByTag,
|
|
625
|
-
"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.",
|
|
626
|
-
{
|
|
627
|
-
tag: z3.string().describe("Tag name to inspect. You can pass @project or project."),
|
|
628
|
-
limit: z3.number().optional().default(20).describe("Max results (default: 20)"),
|
|
629
|
-
offset: z3.number().optional().default(0).describe("Pagination offset (default: 0)")
|
|
630
|
-
},
|
|
631
|
-
async ({ tag, limit, offset }) => {
|
|
632
|
-
const normalizedTag = normalizeOceanBrainTagName(tag);
|
|
633
|
-
const tagResult = await fetchOceanBrainTags(serverUrl, token, normalizedTag, graphqlRequest);
|
|
634
|
-
const exactMatches = tagResult.tags.filter((item) => item.name === normalizedTag);
|
|
635
|
-
if (exactMatches.length === 0) {
|
|
636
|
-
return createMcpJsonToolResult({
|
|
637
|
-
requestedTag: tag,
|
|
638
|
-
normalizedTag,
|
|
639
|
-
tagFound: false,
|
|
640
|
-
noteCount: 0,
|
|
641
|
-
notes: []
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
const selectedTag = exactMatches[0];
|
|
645
|
-
const notesData = await graphqlRequest(serverUrl, token, `
|
|
646
|
-
query ($searchFilter: SearchFilterInput, $pagination: PaginationInput) {
|
|
647
|
-
tagNotes(searchFilter: $searchFilter, pagination: $pagination) {
|
|
648
|
-
totalCount
|
|
649
|
-
notes {
|
|
650
|
-
id
|
|
651
|
-
title
|
|
652
|
-
updatedAt
|
|
653
|
-
tags { id name }
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
`, {
|
|
658
|
-
searchFilter: { query: selectedTag.id },
|
|
659
|
-
pagination: { limit, offset }
|
|
660
|
-
});
|
|
661
|
-
const noteResult = notesData?.tagNotes;
|
|
662
|
-
return createMcpJsonToolResult({
|
|
663
|
-
requestedTag: tag,
|
|
664
|
-
normalizedTag,
|
|
665
|
-
tagFound: true,
|
|
666
|
-
duplicateExactMatchCount: exactMatches.length,
|
|
667
|
-
tag: selectedTag,
|
|
668
|
-
totalCount: noteResult.totalCount,
|
|
669
|
-
notes: noteResult.notes.map((note) => ({
|
|
670
|
-
id: note.id,
|
|
671
|
-
title: note.title,
|
|
672
|
-
updatedAt: note.updatedAt,
|
|
673
|
-
tags: note.tags.map((item) => item.name)
|
|
674
|
-
}))
|
|
675
|
-
});
|
|
676
|
-
}
|
|
677
|
-
);
|
|
678
|
-
server.tool(
|
|
679
|
-
OCEAN_BRAIN_MCP_TOOLS.listNotesByTags,
|
|
680
|
-
"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.",
|
|
681
|
-
{
|
|
682
|
-
tags: z3.array(z3.string()).min(1).describe("Tag names to inspect. You can pass @project or project values."),
|
|
683
|
-
mode: tagMatchModeSchema.optional().default("and").describe("Tag match mode. Use and for intersection, or for union. Defaults to and."),
|
|
684
|
-
limit: z3.number().optional().default(20).describe("Max results (default: 20)"),
|
|
685
|
-
offset: z3.number().optional().default(0).describe("Pagination offset (default: 0)")
|
|
686
|
-
},
|
|
687
|
-
async ({ tags, mode, limit, offset }) => {
|
|
688
|
-
const normalizedTags = normalizeOceanBrainTagNames(tags);
|
|
689
|
-
const tagResults = await Promise.all(
|
|
690
|
-
normalizedTags.map(async (normalizedTag) => {
|
|
691
|
-
const result = await fetchOceanBrainTags(serverUrl, token, normalizedTag, graphqlRequest);
|
|
692
|
-
const exactMatches = result.tags.filter((item) => item.name === normalizedTag);
|
|
693
|
-
return {
|
|
694
|
-
normalizedTag,
|
|
695
|
-
exactMatches
|
|
696
|
-
};
|
|
697
|
-
})
|
|
698
|
-
);
|
|
699
|
-
const matchedTags = tagResults.filter((tagResult) => tagResult.exactMatches.length > 0).map((tagResult) => {
|
|
700
|
-
const selectedTag = tagResult.exactMatches[0];
|
|
701
|
-
return {
|
|
702
|
-
id: selectedTag.id,
|
|
703
|
-
name: selectedTag.name,
|
|
704
|
-
referenceCount: selectedTag.referenceCount,
|
|
705
|
-
duplicateExactMatchCount: tagResult.exactMatches.length
|
|
706
|
-
};
|
|
707
|
-
});
|
|
708
|
-
const missingTags = tagResults.filter((tagResult) => tagResult.exactMatches.length === 0).map((tagResult) => tagResult.normalizedTag);
|
|
709
|
-
let noteResult = {
|
|
710
|
-
totalCount: 0,
|
|
711
|
-
notes: []
|
|
712
|
-
};
|
|
713
|
-
if (mode === "and" && missingTags.length === 0 || mode === "or" && matchedTags.length > 0) {
|
|
714
|
-
const notesData = await graphqlRequest(serverUrl, token, `
|
|
715
|
-
query ($tagNames: [String!]!, $mode: TagMatchMode!, $pagination: PaginationInput) {
|
|
716
|
-
notesByTagNames(tagNames: $tagNames, mode: $mode, pagination: $pagination) {
|
|
717
|
-
totalCount
|
|
718
|
-
notes {
|
|
719
|
-
id
|
|
720
|
-
title
|
|
721
|
-
updatedAt
|
|
722
|
-
tags { id name }
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
`, {
|
|
727
|
-
tagNames: normalizedTags,
|
|
728
|
-
mode,
|
|
729
|
-
pagination: { limit, offset }
|
|
730
|
-
});
|
|
731
|
-
noteResult = notesData?.notesByTagNames;
|
|
732
|
-
}
|
|
733
|
-
return createMcpJsonToolResult({
|
|
734
|
-
requestedTags: tags,
|
|
735
|
-
normalizedTags,
|
|
736
|
-
mode,
|
|
737
|
-
allTagsFound: missingTags.length === 0,
|
|
738
|
-
missingTags,
|
|
739
|
-
tags: matchedTags,
|
|
740
|
-
totalCount: noteResult.totalCount,
|
|
741
|
-
notes: noteResult.notes.map((note) => ({
|
|
742
|
-
id: note.id,
|
|
743
|
-
title: note.title,
|
|
744
|
-
updatedAt: note.updatedAt,
|
|
745
|
-
tags: note.tags.map((item) => item.name)
|
|
746
|
-
}))
|
|
747
|
-
});
|
|
748
|
-
}
|
|
749
|
-
);
|
|
750
|
-
server.tool(
|
|
751
|
-
OCEAN_BRAIN_MCP_TOOLS.queryNotesByProperties,
|
|
752
|
-
"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.",
|
|
753
|
-
{
|
|
754
|
-
propertyFilters: z3.array(propertyFilterSchema).min(1).max(10).describe("Required property filters. Multiple property filters are combined with AND."),
|
|
755
|
-
tagNames: z3.array(z3.string()).optional().default([]).describe("Optional tag filters. You can pass @project or project."),
|
|
756
|
-
mode: tagMatchModeSchema.optional().default("and").describe("Tag match mode for tagNames only. Property filters are always combined with AND."),
|
|
757
|
-
sortBy: viewSortBySchema.optional().default("updatedAt").describe("Sort field (default: updatedAt)"),
|
|
758
|
-
sortOrder: viewSortOrderSchema.optional().default("desc").describe("Sort order (default: desc)"),
|
|
759
|
-
includeProperties: z3.boolean().optional().default(false).describe("Include returned note properties. Defaults to false to reduce tokens."),
|
|
760
|
-
propertyKeys: z3.array(z3.string()).optional().default([]).describe("Property keys to include in output; automatically includes properties."),
|
|
761
|
-
limit: z3.number().optional().default(20).describe("Max results (default: 20, server max: 50)"),
|
|
762
|
-
offset: z3.number().optional().default(0).describe("Pagination offset (default: 0)")
|
|
763
|
-
},
|
|
764
|
-
async ({ propertyFilters, tagNames, mode, sortBy, sortOrder, includeProperties, propertyKeys, limit, offset }) => {
|
|
765
|
-
const shouldIncludeProperties = includeProperties || propertyKeys.length > 0;
|
|
766
|
-
const normalizedTagNames = normalizeOceanBrainTagNames(tagNames);
|
|
767
|
-
const data = await graphqlRequest(serverUrl, token, `
|
|
768
|
-
query ($input: NotesByPropertiesInput!, $pagination: PaginationInput, $includeProperties: Boolean!) {
|
|
769
|
-
notesByProperties(input: $input, pagination: $pagination) {
|
|
770
|
-
totalCount
|
|
771
|
-
notes {
|
|
772
|
-
id
|
|
773
|
-
title
|
|
774
|
-
createdAt
|
|
775
|
-
updatedAt
|
|
776
|
-
tags { id name }
|
|
777
|
-
properties @include(if: $includeProperties) { key name value valueType option { id label value color order } }
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
`, {
|
|
782
|
-
input: {
|
|
783
|
-
tagNames: normalizedTagNames,
|
|
784
|
-
mode,
|
|
785
|
-
propertyFilters,
|
|
786
|
-
sortBy,
|
|
787
|
-
sortOrder
|
|
788
|
-
},
|
|
789
|
-
pagination: { limit, offset },
|
|
790
|
-
includeProperties: shouldIncludeProperties
|
|
791
|
-
});
|
|
792
|
-
const result = data?.notesByProperties;
|
|
793
|
-
return createMcpJsonToolResult(formatPropertyQueryResponse({
|
|
794
|
-
result,
|
|
795
|
-
query: {
|
|
796
|
-
propertyFilters,
|
|
797
|
-
tagNames: normalizedTagNames,
|
|
798
|
-
mode,
|
|
799
|
-
sortBy,
|
|
800
|
-
sortOrder,
|
|
801
|
-
limit,
|
|
802
|
-
offset
|
|
803
|
-
},
|
|
804
|
-
includeProperties: shouldIncludeProperties,
|
|
805
|
-
propertyKeys
|
|
806
|
-
}));
|
|
807
|
-
}
|
|
808
|
-
);
|
|
809
|
-
server.tool(
|
|
810
|
-
OCEAN_BRAIN_MCP_TOOLS.listRecentNotes,
|
|
811
|
-
"List recently updated Ocean Brain notes. Returns titles and tags only. Use ocean_brain_read_note to get full content for a specific note.",
|
|
812
|
-
{
|
|
813
|
-
limit: z3.number().optional().default(10).describe("Max results (default: 10)")
|
|
814
|
-
},
|
|
815
|
-
async ({ limit }) => {
|
|
816
|
-
const data = await graphqlRequest(serverUrl, token, `
|
|
817
|
-
query ($searchFilter: SearchFilterInput, $pagination: PaginationInput) {
|
|
818
|
-
allNotes(searchFilter: $searchFilter, pagination: $pagination) {
|
|
819
|
-
totalCount
|
|
820
|
-
notes {
|
|
821
|
-
id
|
|
822
|
-
title
|
|
823
|
-
updatedAt
|
|
824
|
-
tags { id name }
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
`, {
|
|
829
|
-
searchFilter: { query: "", sortBy: "updatedAt", sortOrder: "desc" },
|
|
830
|
-
pagination: { limit, offset: 0 }
|
|
852
|
+
propertyKeys: result.keys,
|
|
853
|
+
page: createPageInfo(result.totalCount, limit, offset)
|
|
831
854
|
});
|
|
832
|
-
const result = data?.allNotes;
|
|
833
|
-
const notes = result.notes.map((note) => ({
|
|
834
|
-
id: note.id,
|
|
835
|
-
title: note.title,
|
|
836
|
-
updatedAt: note.updatedAt,
|
|
837
|
-
tags: note.tags.map((t) => t.name)
|
|
838
|
-
}));
|
|
839
|
-
return createMcpJsonToolResult({ totalCount: result.totalCount, notes });
|
|
840
|
-
}
|
|
841
|
-
);
|
|
842
|
-
server.tool(
|
|
843
|
-
OCEAN_BRAIN_MCP_TOOLS.findNoteCleanupCandidates,
|
|
844
|
-
"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.",
|
|
845
|
-
{
|
|
846
|
-
keywords: z3.string().optional().default("temp tmp draft test wip").describe("Keywords that mark a note as a cleanup candidate. Comma or space separated."),
|
|
847
|
-
limit: z3.number().optional().default(20).describe("Max results (default: 20)"),
|
|
848
|
-
offset: z3.number().optional().default(0).describe("Pagination offset (default: 0)")
|
|
849
|
-
},
|
|
850
|
-
async ({ keywords, limit, offset }) => {
|
|
851
|
-
const normalizedKeywords = keywords.split(/[,\s]+/).map((keyword) => keyword.trim()).filter(Boolean);
|
|
852
|
-
const data = await graphqlRequest(serverUrl, token, `
|
|
853
|
-
query ($query: String, $pagination: PaginationInput) {
|
|
854
|
-
noteCleanupCandidates(query: $query, pagination: $pagination) {
|
|
855
|
-
id
|
|
856
|
-
title
|
|
857
|
-
updatedAt
|
|
858
|
-
pinned
|
|
859
|
-
tagNames
|
|
860
|
-
reminderCount
|
|
861
|
-
backReferenceCount
|
|
862
|
-
matchedTerms
|
|
863
|
-
reasons
|
|
864
|
-
requiresForce
|
|
865
|
-
forceReasons
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
`, {
|
|
869
|
-
query: keywords,
|
|
870
|
-
pagination: { limit, offset }
|
|
871
|
-
});
|
|
872
|
-
const result = data?.noteCleanupCandidates;
|
|
873
|
-
return createMcpJsonToolResult({
|
|
874
|
-
keywords: normalizedKeywords,
|
|
875
|
-
limit,
|
|
876
|
-
offset,
|
|
877
|
-
candidateCount: result.length,
|
|
878
|
-
notes: result
|
|
879
|
-
});
|
|
880
|
-
}
|
|
881
|
-
);
|
|
882
|
-
server.tool(
|
|
883
|
-
OCEAN_BRAIN_MCP_TOOLS.createTag,
|
|
884
|
-
"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.",
|
|
885
|
-
{
|
|
886
|
-
name: z3.string().describe("Tag name to create. You can pass @project or project.")
|
|
887
|
-
},
|
|
888
|
-
async ({ name }) => {
|
|
889
|
-
const writeToken = requireWriteToken(token, OCEAN_BRAIN_MCP_TOOLS.createTag);
|
|
890
|
-
const normalizedName = normalizeOceanBrainTagName(name);
|
|
891
|
-
const result = await jsonRequest(serverUrl, writeToken, "/api/mcp/tags/create", {
|
|
892
|
-
name: normalizedName
|
|
893
|
-
});
|
|
894
|
-
return createMcpJsonToolResult(result);
|
|
895
855
|
}
|
|
896
856
|
);
|
|
897
857
|
server.tool(
|
|
898
858
|
OCEAN_BRAIN_MCP_TOOLS.deleteNote,
|
|
899
859
|
"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.",
|
|
900
860
|
{
|
|
901
|
-
id:
|
|
861
|
+
id: z6.string().describe("Note ID to move to trash")
|
|
902
862
|
},
|
|
903
863
|
async ({ id }) => {
|
|
904
864
|
const writeToken = requireWriteToken(token, OCEAN_BRAIN_MCP_TOOLS.deleteNote);
|
|
905
865
|
try {
|
|
906
|
-
const result = await jsonRequest(serverUrl, writeToken, "/api/
|
|
866
|
+
const result = await jsonRequest(serverUrl, writeToken, "/api/integrations/v1/notes/delete", { id });
|
|
907
867
|
return createMcpJsonToolResult(result);
|
|
908
868
|
} catch (error) {
|
|
909
869
|
const message = error instanceof Error ? error.message : "Unknown MCP note delete error";
|
|
@@ -928,7 +888,6 @@ export {
|
|
|
928
888
|
OCEAN_BRAIN_MCP_TOOLS,
|
|
929
889
|
OCEAN_BRAIN_MCP_VERSION_HEADER,
|
|
930
890
|
createMcpRequestHeaders,
|
|
931
|
-
normalizeOceanBrainTagName,
|
|
932
891
|
registerMcpTools,
|
|
933
892
|
startMcpServer
|
|
934
893
|
};
|