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.
Files changed (158) hide show
  1. package/README.md +66 -2
  2. package/dist/mcp.js +439 -480
  3. package/package.json +2 -2
  4. package/server/client/dist/assets/{ArrowRight.es-BlNsZQFs.js → ArrowRight.es-nBJws3ws.js} +1 -1
  5. package/server/client/dist/assets/{AuxiliaryPanelHeader-HAttwVby.js → AuxiliaryPanelHeader-BN_2axQu.js} +1 -1
  6. package/server/client/dist/assets/{Calendar-DwQBllfw.js → Calendar-vA4Xa4Qr.js} +1 -1
  7. package/server/client/dist/assets/{Callout-CcYyvHQP.js → Callout-Bo2P960P.js} +1 -1
  8. package/server/client/dist/assets/{CaretUp.es-C8iY5OlB.js → CaretUp.es-DDbqKn78.js} +1 -1
  9. package/server/client/dist/assets/{CheckCircle.es-Dqr3WImn.js → CheckCircle.es-CyY9aLBF.js} +1 -1
  10. package/server/client/dist/assets/{Copy.es-fMv0ETir.js → Copy.es-BwX4tEoU.js} +1 -1
  11. package/server/client/dist/assets/{File.es-CHtXXafR.js → File.es-DeTgvUnI.js} +1 -1
  12. package/server/client/dist/assets/{FileText.es-DPIwDCEe.js → FileText.es-C-bpw8dQ.js} +1 -1
  13. package/server/client/dist/assets/{FlowArrow.es-Dxee98ir.js → FlowArrow.es-CKcMBOmX.js} +1 -1
  14. package/server/client/dist/assets/{Graph-hVj6vAYJ.js → Graph-BNoSRJP3.js} +1 -1
  15. package/server/client/dist/assets/{Graph-CdEu8eAn.js → Graph-DYYxNAWn.js} +1 -1
  16. package/server/client/dist/assets/{Image.es-CjXY1JVf.js → Image.es-d4jWnijM.js} +1 -1
  17. package/server/client/dist/assets/{Info.es-CYxg-Vku.js → Info.es-DVzN2joA.js} +1 -1
  18. package/server/client/dist/assets/Integration-gDDtIJYJ.js +1 -0
  19. package/server/client/dist/assets/{LinkSimple.es-B-P-7Iem.js → LinkSimple.es-BD4aGTc3.js} +1 -1
  20. package/server/client/dist/assets/Note-FL31jb2U.js +19 -0
  21. package/server/client/dist/assets/{NotePropertiesPanel-6P7UfRBR.js → NotePropertiesPanel-os22Ppnu.js} +1 -1
  22. package/server/client/dist/assets/{Plus.es-Dy6onD-Z.js → Plus.es-Cl8zOOZa.js} +1 -1
  23. package/server/client/dist/assets/Reminders-Dt2JuGci.js +1 -0
  24. package/server/client/dist/assets/{Search-AaD1Mt0d.js → Search-CPPTW2PO.js} +2 -2
  25. package/server/client/dist/assets/{SurfaceCard-D5QxOte_.js → SurfaceCard-jL2qzrzF.js} +1 -1
  26. package/server/client/dist/assets/{Tag-B2Qiq1Hv.js → Tag-3DbGmuxa.js} +1 -1
  27. package/server/client/dist/assets/{TagNotes-B27G1ZVg.js → TagNotes-DLwRF8pT.js} +1 -1
  28. package/server/client/dist/assets/Trash.es-fxukjAO6.js +1 -0
  29. package/server/client/dist/assets/{ViewChip-BVkZAXyx.js → ViewChip-CVe_HNyd.js} +1 -1
  30. package/server/client/dist/assets/Views-8ghLRax7.js +1 -0
  31. package/server/client/dist/assets/{abnfDiagram-N423BO3Z-D44O4T7q.js → abnfDiagram-N423BO3Z-CPgGmCbn.js} +1 -1
  32. package/server/client/dist/assets/app-Bcl0U7r9.css +1 -0
  33. package/server/client/dist/assets/app-DzChh-V0.js +151 -0
  34. package/server/client/dist/assets/{arc-Cil9fXuW.js → arc-DqTgRpuF.js} +1 -1
  35. package/server/client/dist/assets/{architectureDiagram-T3A2C74G-Cjq6ayF2.js → architectureDiagram-T3A2C74G-rTYMAQ-J.js} +1 -1
  36. package/server/client/dist/assets/{blockDiagram-VBNYF7ZC-B2Xm5N0i.js → blockDiagram-VBNYF7ZC-BOluytVz.js} +1 -1
  37. package/server/client/dist/assets/{c4Diagram-5PPSVZJV-Dp0y-lCR.js → c4Diagram-5PPSVZJV-C8gbPpcb.js} +1 -1
  38. package/server/client/dist/assets/{calendar-data-wB0ifXdo.js → calendar-data-BQ24n348.js} +1 -1
  39. package/server/client/dist/assets/channel--aI9fJWX.js +1 -0
  40. package/server/client/dist/assets/{chunk-2GRJ4B5K-CWaaNkFx.js → chunk-2GRJ4B5K-CRER5Ka0.js} +1 -1
  41. package/server/client/dist/assets/{chunk-2Q5K7J3B-Ces-xz2S.js → chunk-2Q5K7J3B-hxmJWGu_.js} +1 -1
  42. package/server/client/dist/assets/{chunk-5RXB4S5H-B-4Chlfc.js → chunk-5RXB4S5H-Bpr5Us8Y.js} +1 -1
  43. package/server/client/dist/assets/{chunk-5VM5RSS4-C8_HVwuz.js → chunk-5VM5RSS4-CKUz9fAb.js} +1 -1
  44. package/server/client/dist/assets/{chunk-6Q2QTUOP-xZ8ofRqk.js → chunk-6Q2QTUOP-B8XEOHPN.js} +1 -1
  45. package/server/client/dist/assets/{chunk-GF5L2VYU-Bpa43a7s.js → chunk-GF5L2VYU-BBUD-HJi.js} +1 -1
  46. package/server/client/dist/assets/{chunk-JWPE2WC7-DsyTXbuq.js → chunk-JWPE2WC7-Dq6_B8-d.js} +1 -1
  47. package/server/client/dist/assets/{chunk-KBJHAD2P-BDhjm9-8.js → chunk-KBJHAD2P--2VZEwNJ.js} +1 -1
  48. package/server/client/dist/assets/{chunk-RYQCIY6F-Cn-jdp6I.js → chunk-RYQCIY6F-CKCwnKiC.js} +1 -1
  49. package/server/client/dist/assets/{chunk-XXDRQBXY-BKsc_0wh.js → chunk-XXDRQBXY-VDufR1Oi.js} +1 -1
  50. package/server/client/dist/assets/classDiagram-JCYQIIEL-BsbwE38V.js +1 -0
  51. package/server/client/dist/assets/classDiagram-v2-OCEON4UE-BsbwE38V.js +1 -0
  52. package/server/client/dist/assets/{cose-bilkent-JH36ORCC-jmn3Q4zj.js → cose-bilkent-JH36ORCC-D4nqOx8S.js} +1 -1
  53. package/server/client/dist/assets/{cynefinDiagram-MW4NZA55-BpLstuBJ.js → cynefinDiagram-MW4NZA55-CUuLMs-6.js} +1 -1
  54. package/server/client/dist/assets/{dagre-VZM6K2ZE-B5ft5Yzm.js → dagre-VZM6K2ZE-CUSkhZgS.js} +1 -1
  55. package/server/client/dist/assets/{diagram-7IWD3JNH-B_W9IsWZ.js → diagram-7IWD3JNH-D95eVcKx.js} +4 -4
  56. package/server/client/dist/assets/{diagram-B4RE2ZJO-32chS3GX.js → diagram-B4RE2ZJO-7OH_fb0K.js} +1 -1
  57. package/server/client/dist/assets/{diagram-LBJQPF4R-Bqupdb3-.js → diagram-LBJQPF4R-D9UwEIMd.js} +1 -1
  58. package/server/client/dist/assets/{diagram-Q27KOJAE-BD8jKBsv.js → diagram-Q27KOJAE-Dx7WFDuX.js} +1 -1
  59. package/server/client/dist/assets/{diagram-UB23O5K3-C40jAU8D.js → diagram-UB23O5K3-D2BYwAKT.js} +1 -1
  60. package/server/client/dist/assets/{ebnfDiagram-BXEA7PRR-BQfW9mFA.js → ebnfDiagram-BXEA7PRR-DbeA-fk4.js} +1 -1
  61. package/server/client/dist/assets/{erDiagram-JOGREHBK-ILZnLzz2.js → erDiagram-JOGREHBK-jWW-ICGO.js} +1 -1
  62. package/server/client/dist/assets/{flowDiagram-UKHOOZJN-CDmwr2dI.js → flowDiagram-UKHOOZJN-68iZdxxw.js} +1 -1
  63. package/server/client/dist/assets/{ganttDiagram-PKOTCBZU-DBcrUc49.js → ganttDiagram-PKOTCBZU-CxwY3VXg.js} +1 -1
  64. package/server/client/dist/assets/{gitGraphDiagram-DS77QQ5N-_xO6__FK.js → gitGraphDiagram-DS77QQ5N-Ci6o5MfZ.js} +1 -1
  65. package/server/client/dist/assets/{image.api-ChvlfuZg.js → image.api-CyIIEGkG.js} +1 -1
  66. package/server/client/dist/assets/index-Iy_BgP1R.js +1 -0
  67. package/server/client/dist/assets/{infoDiagram-6WML65LV-CuIIcu-L.js → infoDiagram-6WML65LV-DY7aMdjV.js} +1 -1
  68. package/server/client/dist/assets/integrations-LOdTs1T6.js +1 -0
  69. package/server/client/dist/assets/{ishikawaDiagram-WSZJBQD7-BVtRGTOj.js → ishikawaDiagram-WSZJBQD7-Bh5qKh1d.js} +1 -1
  70. package/server/client/dist/assets/{journeyDiagram-NVQOT4AX-D5-Nzb2N.js → journeyDiagram-NVQOT4AX-B_svXTTT.js} +1 -1
  71. package/server/client/dist/assets/{kanban-definition-27J2QSJJ-DmOhgC8H.js → kanban-definition-27J2QSJJ-CaJslHGg.js} +1 -1
  72. package/server/client/dist/assets/manage-image-DXedfr7U.js +1 -0
  73. package/server/client/dist/assets/{manage-image-detail-Dw3tAW3h.js → manage-image-detail-DmifOPNU.js} +1 -1
  74. package/server/client/dist/assets/{mcp-admin-adapter-D0R8p9az.js → mcp-admin-adapter-CmKsg1vd.js} +1 -1
  75. package/server/client/dist/assets/mcp-d8IzsoRz.js +5 -0
  76. package/server/client/dist/assets/{mermaid.core-C2tL9tq7.js → mermaid.core-BfUQM5vF.js} +4 -4
  77. package/server/client/dist/assets/{mindmap-definition-FAOFIHXS-DuvsPqrh.js → mindmap-definition-FAOFIHXS-DGbkJda2.js} +1 -1
  78. package/server/client/dist/assets/{note-core-YhYEmI_R.js → note-core-DRBsBf9H.js} +9 -9
  79. package/server/client/dist/assets/{note-runtime-B3ats-dC.js → note-runtime-ztZvXRIu.js} +16 -16
  80. package/server/client/dist/assets/{note-ui-BtzPk8gA.js → note-ui-DRnAnwKv.js} +1 -1
  81. package/server/client/dist/assets/{pegDiagram-VL7TDLO6-D64crEZV.js → pegDiagram-VL7TDLO6-Bip6zQct.js} +1 -1
  82. package/server/client/dist/assets/{pieDiagram-7S7Q4E2Y--kl7vopC.js → pieDiagram-7S7Q4E2Y-CjfjGY8Y.js} +1 -1
  83. package/server/client/dist/assets/{placeholder-B5uPsE7-.js → placeholder-G6iEBbDy.js} +1 -1
  84. package/server/client/dist/assets/{properties-B7Fv95qY.js → properties-BPobx_Qk.js} +1 -1
  85. package/server/client/dist/assets/{quadrantDiagram-CIZ2JOQS-DuvJ4M2y.js → quadrantDiagram-CIZ2JOQS-B6F1yUT-.js} +1 -1
  86. package/server/client/dist/assets/{railroadDiagram-AXF67PYL-BMKmCOY7.js → railroadDiagram-AXF67PYL-D-0fLe0F.js} +1 -1
  87. package/server/client/dist/assets/{requirementDiagram-LRYGKXZP-DY1eJcy0.js → requirementDiagram-LRYGKXZP-tm4mnCyJ.js} +1 -1
  88. package/server/client/dist/assets/route-preload.js +2 -2
  89. package/server/client/dist/assets/{sankeyDiagram-W5VNT64P-C3Ka4GKM.js → sankeyDiagram-W5VNT64P-DSBWFurb.js} +1 -1
  90. package/server/client/dist/assets/{search-ddYl3qH-.js → search-DanMKCC8.js} +1 -1
  91. package/server/client/dist/assets/{search-admin-adapter-Daf7e2YQ.js → search-admin-adapter-7sTcWTmv.js} +1 -1
  92. package/server/client/dist/assets/{sequenceDiagram-SI44F4Z6-Bhpwtk2s.js → sequenceDiagram-SI44F4Z6-KrwjjJIV.js} +1 -1
  93. package/server/client/dist/assets/{sizeCapture-X5ZJPWSS-BFta71TW.js → sizeCapture-X5ZJPWSS-DL2cn_Q6.js} +1 -1
  94. package/server/client/dist/assets/{stateDiagram-OKZ733FA-D6GCn1io.js → stateDiagram-OKZ733FA-CKuNDH85.js} +1 -1
  95. package/server/client/dist/assets/{stateDiagram-v2-UEYNNEHI-g4yinGDn.js → stateDiagram-v2-UEYNNEHI-BFaj2wbM.js} +1 -1
  96. package/server/client/dist/assets/{swimlanes-SLNWSIFB-DLFvOjUl.js → swimlanes-SLNWSIFB-5S17nqhK.js} +2 -2
  97. package/server/client/dist/assets/swimlanesDiagram-ULZ7WXOC-dexsHJq2.js +8 -0
  98. package/server/client/dist/assets/{tag.api-DEUROVKQ.js → tag.api-kKZl04yY.js} +1 -1
  99. package/server/client/dist/assets/{time-PxWila4-.js → time-BwwQGbK-.js} +38 -38
  100. package/server/client/dist/assets/{timeline-definition-Z64GVDOM-DTSIekSM.js → timeline-definition-Z64GVDOM-CU1hp_7d.js} +3 -3
  101. package/server/client/dist/assets/{trash-BHida7BG.js → trash-Cepvi7G8.js} +1 -1
  102. package/server/client/dist/assets/url-Bp8sXMb4.js +1 -0
  103. package/server/client/dist/assets/{useNoteMutate-BjvZUmtX.js → useNoteMutate-D-mZKXjl.js} +3 -3
  104. package/server/client/dist/assets/{useReminderMutate-C5n-5LM-.js → useReminderMutate-Cexk1ydX.js} +1 -1
  105. package/server/client/dist/assets/{vennDiagram-T6HMQDX7-CMNqSI2X.js → vennDiagram-T6HMQDX7-MoiVLnil.js} +1 -1
  106. package/server/client/dist/assets/{wardleyDiagram-T6FBY63Y-DV-JOYqB.js → wardleyDiagram-T6FBY63Y-cZkfdI6C.js} +1 -1
  107. package/server/client/dist/assets/{xychartDiagram-ELKLHX3M-BUIniF4m.js → xychartDiagram-ELKLHX3M-B63hxD-r.js} +1 -1
  108. package/server/client/dist/index.html +6 -6
  109. package/server/dist/app.js +2 -0
  110. package/server/dist/features/integration/auth.js +21 -0
  111. package/server/dist/features/integration/manifest.js +87 -0
  112. package/server/dist/features/integration/schema.js +54 -0
  113. package/server/dist/features/integration/service.js +173 -0
  114. package/server/dist/features/mcp-admin/service.js +19 -83
  115. package/server/dist/features/note/graphql/note.field.resolver.js +1 -1
  116. package/server/dist/features/note/graphql/note.query.resolver.js +6 -0
  117. package/server/dist/features/note/graphql/note.type-defs.js +13 -1
  118. package/server/dist/features/note/http/authoring.js +493 -0
  119. package/server/dist/features/note/http/mcp.js +15 -463
  120. package/server/dist/features/note/services/authoring.js +17 -1
  121. package/server/dist/features/note/services/markdown-intent-write.js +1 -0
  122. package/server/dist/features/note/services/markdown-read.js +85 -0
  123. package/server/dist/features/note/services/properties.js +36 -30
  124. package/server/dist/features/note/services/search.js +17 -0
  125. package/server/dist/features/search/graphql/resolver.js +10 -1
  126. package/server/dist/features/search/graphql/type-defs.js +3 -0
  127. package/server/dist/features/tag/graphql/tag.query.resolver.js +10 -0
  128. package/server/dist/features/tag/graphql/tag.type-defs.js +1 -0
  129. package/server/dist/features/view/graphql/mcp.query.resolver.js +22 -0
  130. package/server/dist/features/view/graphql/view.query.resolver.js +2 -0
  131. package/server/dist/features/view/graphql/view.type-defs.js +27 -0
  132. package/server/dist/features/view/services/mcp-read.js +142 -0
  133. package/server/dist/features/view/services/workspace.js +12 -4
  134. package/server/dist/modules/auth-guard.js +24 -1
  135. package/server/dist/modules/integration-token.js +27 -0
  136. package/server/dist/modules/mcp-auth.js +37 -23
  137. package/server/dist/modules/mcp-token.js +6 -25
  138. package/server/dist/routes/api.js +2 -0
  139. package/server/dist/routes/graphql.js +2 -1
  140. package/server/dist/routes/integrations.js +117 -0
  141. package/server/dist/routes/mcp.js +29 -9
  142. package/server/prisma/migrations/20260917120000_0020_plugin_installations/migration.sql +39 -0
  143. package/server/prisma/migrations/20260917150000_0021_integration_connections/migration.sql +29 -0
  144. package/server/prisma/schema.prisma +34 -10
  145. package/server/client/dist/assets/Note-CpzLtGVM.js +0 -19
  146. package/server/client/dist/assets/Reminders-DnuALBL6.js +0 -1
  147. package/server/client/dist/assets/Trash.es-XEcWG_aq.js +0 -1
  148. package/server/client/dist/assets/Views-B5oh8GVM.js +0 -1
  149. package/server/client/dist/assets/app-EwvT7bMw.css +0 -1
  150. package/server/client/dist/assets/app-NgiBDISG.js +0 -151
  151. package/server/client/dist/assets/channel-DZFalh-h.js +0 -1
  152. package/server/client/dist/assets/classDiagram-JCYQIIEL-CkEhFvVV.js +0 -1
  153. package/server/client/dist/assets/classDiagram-v2-OCEON4UE-CkEhFvVV.js +0 -1
  154. package/server/client/dist/assets/index-CIEap4bu.js +0 -1
  155. package/server/client/dist/assets/manage-image-CSJ3Flgc.js +0 -1
  156. package/server/client/dist/assets/mcp-D8footel.js +0 -5
  157. package/server/client/dist/assets/swimlanesDiagram-ULZ7WXOC-Ci4mcvmo.js +0 -8
  158. package/server/client/dist/assets/url-DKUMNVCI.js +0 -1
@@ -0,0 +1,173 @@
1
+ import { createHash } from "node:crypto";
2
+ import models from "../../models.js";
3
+ import { createAppError } from "../../modules/error-handler.js";
4
+ import { issueIntegrationToken } from "../../modules/integration-token.js";
5
+ import {
6
+ MCP_CONNECTION_ID,
7
+ NATIVE_INTEGRATIONS,
8
+ parseManifest,
9
+ parsePermissions,
10
+ validateGrants
11
+ } from "./manifest.js";
12
+ const resolveManifest = (row) => {
13
+ if (row.native) {
14
+ const definition = NATIVE_INTEGRATIONS.find((integration) => integration.connectionId === row.id);
15
+ if (definition) return definition.manifest;
16
+ }
17
+ return parseManifest(JSON.parse(row.manifest));
18
+ };
19
+ const toConnection = (row) => ({
20
+ id: row.id,
21
+ native: row.native,
22
+ manifest: resolveManifest(row),
23
+ grantedPermissions: parsePermissions(JSON.parse(row.grantedPermissions)),
24
+ enabled: row.enabled,
25
+ pinned: row.pinned,
26
+ createdAt: row.createdAt.toISOString(),
27
+ updatedAt: row.updatedAt.toISOString(),
28
+ token: row.credential ? {
29
+ id: row.credential.id,
30
+ createdAt: row.credential.createdAt.toISOString(),
31
+ lastUsedAt: row.credential.lastUsedAt?.toISOString() ?? null
32
+ } : null
33
+ });
34
+ const credentialSummary = { select: { id: true, createdAt: true, lastUsedAt: true } };
35
+ const createIntegrationService = (db = models) => {
36
+ const get = async (id) => {
37
+ const row = await db.integrationConnection.findUnique({
38
+ where: { id },
39
+ include: { credential: credentialSummary }
40
+ });
41
+ if (!row) throw createAppError(404, "INTEGRATION_NOT_FOUND", "The integration connection was not found.");
42
+ return toConnection(row);
43
+ };
44
+ return {
45
+ get,
46
+ async ensureNativeConnections() {
47
+ for (const integration of NATIVE_INTEGRATIONS) {
48
+ await db.integrationConnection.upsert({
49
+ where: { id: integration.connectionId },
50
+ create: {
51
+ id: integration.connectionId,
52
+ integrationId: integration.manifest.id,
53
+ native: true,
54
+ manifest: JSON.stringify(integration.manifest),
55
+ grantedPermissions: JSON.stringify(integration.manifest.permissions)
56
+ },
57
+ // Keep the owner's grants, enabled state and credentials on upgrade.
58
+ update: {}
59
+ });
60
+ }
61
+ },
62
+ async list() {
63
+ const rows = await db.integrationConnection.findMany({
64
+ orderBy: [{ native: "desc" }, { createdAt: "asc" }],
65
+ include: { credential: credentialSummary }
66
+ });
67
+ return rows.map(toConnection);
68
+ },
69
+ async connect(input) {
70
+ const manifest = parseManifest(input.manifest);
71
+ if (manifest.id.startsWith("ocean-brain."))
72
+ throw createAppError(
73
+ 400,
74
+ "RESERVED_INTEGRATION_ID",
75
+ "The ocean-brain namespace is reserved for native integrations."
76
+ );
77
+ const grantedPermissions = validateGrants(input.grantedPermissions ?? [], manifest);
78
+ const row = await db.integrationConnection.create({
79
+ data: {
80
+ integrationId: manifest.id,
81
+ manifest: JSON.stringify(manifest),
82
+ grantedPermissions: JSON.stringify(grantedPermissions)
83
+ },
84
+ include: { credential: credentialSummary }
85
+ });
86
+ return toConnection(row);
87
+ },
88
+ async update(id, input) {
89
+ return db.$transaction(async (tx) => {
90
+ const current = await tx.integrationConnection.findUnique({ where: { id } });
91
+ if (!current)
92
+ throw createAppError(404, "INTEGRATION_NOT_FOUND", "The integration connection was not found.");
93
+ if (input.manifest !== void 0 && current.native)
94
+ throw createAppError(
95
+ 400,
96
+ "NATIVE_INTEGRATION_MANIFEST",
97
+ "Native integration manifests are managed by Ocean Brain."
98
+ );
99
+ const manifest = input.manifest === void 0 ? resolveManifest(current) : parseManifest(input.manifest);
100
+ if (manifest.id !== current.integrationId)
101
+ throw createAppError(400, "INTEGRATION_ID_CHANGED", "An upgrade cannot change the integration id.");
102
+ const previousGrants = parsePermissions(JSON.parse(current.grantedPermissions)).filter(
103
+ (permission) => manifest.permissions.includes(permission)
104
+ );
105
+ const grantedPermissions = validateGrants(input.grantedPermissions ?? previousGrants, manifest);
106
+ for (const key of ["enabled", "pinned"]) {
107
+ if (input[key] !== void 0 && typeof input[key] !== "boolean")
108
+ throw createAppError(400, "INVALID_INTEGRATION_SETTING", `${key} must be a boolean.`);
109
+ }
110
+ const row = await tx.integrationConnection.update({
111
+ where: { id },
112
+ data: {
113
+ manifest: JSON.stringify(manifest),
114
+ grantedPermissions: JSON.stringify(grantedPermissions),
115
+ ...typeof input.enabled === "boolean" ? { enabled: input.enabled } : {},
116
+ ...typeof input.pinned === "boolean" ? { pinned: input.pinned } : {}
117
+ },
118
+ include: { credential: credentialSummary }
119
+ });
120
+ return toConnection(row);
121
+ });
122
+ },
123
+ async disconnect(id) {
124
+ const connection = await get(id);
125
+ if (connection.native || id === MCP_CONNECTION_ID)
126
+ throw createAppError(
127
+ 400,
128
+ "NATIVE_INTEGRATION_REQUIRED",
129
+ "Native integrations can be disabled, but cannot be disconnected."
130
+ );
131
+ await db.integrationConnection.delete({ where: { id } });
132
+ },
133
+ async rotateToken(id) {
134
+ await get(id);
135
+ const token = issueIntegrationToken();
136
+ await db.integrationCredential.upsert({
137
+ where: { connectionId: id },
138
+ create: { connectionId: id, tokenHash: token.hash },
139
+ update: { tokenHash: token.hash, createdAt: /* @__PURE__ */ new Date(), lastUsedAt: null }
140
+ });
141
+ return { token: token.plaintext };
142
+ },
143
+ async revokeToken(id) {
144
+ await get(id);
145
+ await db.integrationCredential.deleteMany({ where: { connectionId: id } });
146
+ },
147
+ async authenticate(token) {
148
+ const tokenHash = createHash("sha256").update(token, "utf8").digest("hex");
149
+ const credential = await db.integrationCredential.findUnique({
150
+ where: { tokenHash },
151
+ include: { connection: true }
152
+ });
153
+ if (!credential?.connection.enabled) return null;
154
+ const connection = credential.connection;
155
+ const touched = await db.integrationCredential.updateMany({
156
+ where: { connectionId: connection.id, tokenHash },
157
+ data: { lastUsedAt: /* @__PURE__ */ new Date() }
158
+ });
159
+ if (!touched.count) return null;
160
+ return {
161
+ connectionId: connection.id,
162
+ integrationId: connection.integrationId,
163
+ native: connection.native,
164
+ permissions: parsePermissions(JSON.parse(connection.grantedPermissions)).filter(
165
+ (permission) => resolveManifest(connection).permissions.includes(permission)
166
+ )
167
+ };
168
+ }
169
+ };
170
+ };
171
+ export {
172
+ createIntegrationService
173
+ };
@@ -1,87 +1,23 @@
1
- import models from "../../models.js";
2
- import { issueMcpToken, verifyMcpToken } from "../../modules/mcp-token.js";
3
- const MCP_ENABLED_CACHE_KEY = "MCP_ENABLED";
4
- const toStatus = (enabled, activeToken) => {
5
- if (!activeToken) {
6
- return {
7
- enabled,
8
- hasActiveToken: false,
9
- token: null
10
- };
1
+ import { MCP_CONNECTION_ID } from "../integration/manifest.js";
2
+ import { createIntegrationService } from "../integration/service.js";
3
+ const createMcpAdminService = (integrations = createIntegrationService()) => ({
4
+ async getStatus() {
5
+ const integration = await integrations.get(MCP_CONNECTION_ID);
6
+ return { enabled: integration.enabled, hasActiveToken: integration.token !== null, token: integration.token };
7
+ },
8
+ async setEnabled(enabled) {
9
+ await integrations.update(MCP_CONNECTION_ID, { enabled });
10
+ },
11
+ rotateToken: () => integrations.rotateToken(MCP_CONNECTION_ID),
12
+ revokeActiveToken: () => integrations.revokeToken(MCP_CONNECTION_ID),
13
+ async validatePresentedToken(token) {
14
+ const integration = await integrations.get(MCP_CONNECTION_ID);
15
+ if (!integration.token) return { ok: false, reason: "not_configured" };
16
+ const principal = await integrations.authenticate(token);
17
+ if (!principal || principal.connectionId !== MCP_CONNECTION_ID) return { ok: false, reason: "forbidden" };
18
+ return { ok: true, permissions: principal.permissions };
11
19
  }
12
- return {
13
- enabled,
14
- hasActiveToken: true,
15
- token: {
16
- id: String(activeToken.id),
17
- createdAt: activeToken.createdAt.toISOString(),
18
- lastUsedAt: activeToken.lastUsedAt ? activeToken.lastUsedAt.toISOString() : null
19
- }
20
- };
21
- };
22
- const createMcpAdminService = (db = models) => {
23
- return {
24
- async getStatus() {
25
- const enabledCache = await db.cache.findUnique({ where: { key: MCP_ENABLED_CACHE_KEY } });
26
- const activeToken = await db.mcpToken.findFirst({
27
- where: { revokedAt: null },
28
- orderBy: { createdAt: "desc" }
29
- });
30
- return toStatus(enabledCache?.value === "true", activeToken);
31
- },
32
- async setEnabled(enabled) {
33
- await db.cache.upsert({
34
- where: { key: MCP_ENABLED_CACHE_KEY },
35
- update: { value: String(enabled) },
36
- create: {
37
- key: MCP_ENABLED_CACHE_KEY,
38
- value: String(enabled)
39
- }
40
- });
41
- },
42
- async rotateToken() {
43
- const now = /* @__PURE__ */ new Date();
44
- const issued = issueMcpToken();
45
- await db.$transaction(async (transaction) => {
46
- await transaction.mcpToken.updateMany({
47
- where: { revokedAt: null },
48
- data: { revokedAt: now }
49
- });
50
- await transaction.mcpToken.create({ data: { tokenHash: issued.hash } });
51
- });
52
- return { token: issued.plaintext };
53
- },
54
- async revokeActiveToken() {
55
- await db.mcpToken.updateMany({
56
- where: { revokedAt: null },
57
- data: { revokedAt: /* @__PURE__ */ new Date() }
58
- });
59
- },
60
- async validatePresentedToken(token) {
61
- const activeToken = await db.mcpToken.findFirst({
62
- where: { revokedAt: null },
63
- orderBy: { createdAt: "desc" }
64
- });
65
- if (!activeToken) {
66
- return {
67
- ok: false,
68
- reason: "not_configured"
69
- };
70
- }
71
- if (!verifyMcpToken(activeToken.tokenHash, token)) {
72
- return {
73
- ok: false,
74
- reason: "forbidden"
75
- };
76
- }
77
- await db.mcpToken.update({
78
- where: { id: activeToken.id },
79
- data: { lastUsedAt: /* @__PURE__ */ new Date() }
80
- });
81
- return { ok: true };
82
- }
83
- };
84
- };
20
+ });
85
21
  export {
86
22
  createMcpAdminService
87
23
  };
@@ -4,7 +4,7 @@ import { renderNoteSnapshotContentAsMarkdown } from "../../../features/note/serv
4
4
  import models from "../../../models.js";
5
5
  const noteFieldResolvers = {
6
6
  tags: async (note) => models.tag.findMany({ where: { notes: { some: { id: note.id } } } }),
7
- properties: async (note) => listNoteProperties(note.id),
7
+ properties: async (note, { keys }) => listNoteProperties(note.id, keys ?? note.selectedPropertyKeys),
8
8
  contentPreview: (note) => buildNoteContentPreview(note.content),
9
9
  contentAsMarkdown: async (note) => {
10
10
  const { blocksToMarkdown } = await import("../../../modules/blocknote.js");
@@ -21,6 +21,7 @@ import {
21
21
  import { getTrashedNoteById, listTrashedNotes } from "../../../features/note/services/trash.js";
22
22
  import models from "../../../models.js";
23
23
  import { runDataMaintenanceInBackground } from "../../../modules/data-maintenance.js";
24
+ import { readMarkdownRange } from "../services/markdown-read.js";
24
25
  const MAX_SQLITE_INT_ID = 2147483647;
25
26
  const isValidNoteIdString = (id) => {
26
27
  if (!/^[1-9]\d*$/.test(id)) {
@@ -386,6 +387,11 @@ const noteQueryResolvers = {
386
387
  }),
387
388
  backReferences: createBackReferencesQueryResolver(),
388
389
  note: createNoteQueryResolver(),
390
+ noteRead: async (_, { id, ...input }) => {
391
+ const note = await createNoteQueryResolver()(_, { id });
392
+ const { blocksToMarkdown } = await import("../../../modules/blocknote.js");
393
+ return { note, ...readMarkdownRange(await blocksToMarkdown(note.content), note.updatedAt, input) };
394
+ },
389
395
  noteCleanupCandidates: async (_, {
390
396
  query,
391
397
  pagination = {
@@ -106,7 +106,7 @@ const noteType = gql`
106
106
  order: Int!
107
107
  layout: NoteLayout!
108
108
  tags: [Tag!]!
109
- properties: [NoteProperty!]!
109
+ properties(keys: [String!]): [NoteProperty!]!
110
110
  }
111
111
 
112
112
  type NoteProperty {
@@ -269,6 +269,17 @@ const noteQuery = gql`
269
269
  links: [GraphLink!]!
270
270
  }
271
271
 
272
+ type NoteContentRange { start: Int!, end: Int!, totalLength: Int!, sectionEnd: Int!, hasMore: Boolean!, nextOffset: Int }
273
+ type NoteHeadingCandidate { heading: String!, level: Int!, start: Int!, end: Int! }
274
+ type NoteReadResult {
275
+ status: String!
276
+ reason: String
277
+ message: String
278
+ note: Note!
279
+ markdown: String!
280
+ contentRange: NoteContentRange
281
+ candidates: [NoteHeadingCandidate!]!
282
+ }
272
283
  type Query {
273
284
  allNotes(searchFilter: SearchFilterInput, pagination: PaginationInput): Notes!
274
285
  tagNotes(searchFilter: SearchFilterInput, pagination: PaginationInput): Notes!
@@ -278,6 +289,7 @@ const noteQuery = gql`
278
289
  imageNotes(src: String!): [Note!]!
279
290
  backReferences(id: ID!): [Note]!
280
291
  note(id: ID!): Note!
292
+ noteRead(id: ID!, offset: Int, maxLength: Int, heading: String, expectedUpdatedAt: String): NoteReadResult!
281
293
  noteCleanupCandidates(query: String, pagination: PaginationInput): [NoteCleanupCandidate!]!
282
294
  noteCleanupPreview(id: ID!): NoteCleanupPreview
283
295
  noteSnapshots(id: ID!, limit: Int): [NoteSnapshot!]!