@edda-business/mcp 0.56.0 → 0.57.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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/server.js +46 -11
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edda-business/mcp",
3
- "version": "0.56.0",
3
+ "version": "0.57.0",
4
4
  "description": "Edda — the company data layer for AI agents.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
package/src/server.js CHANGED
@@ -13,7 +13,7 @@
13
13
  * Tools, by group (v0.56 — consolidated, Cerebras-style):
14
14
  * RETRIEVE search (unified, scope=all|company|personal|notes) · get_context ·
15
15
  * get_account · who_knows · query · attention · verify
16
- * WRITE save_note · save_to_vault · propose_company_file
16
+ * WRITE save_note · save_to_vault · update_vault_file · propose_company_file
17
17
  * FIX merge_contacts (action=merge|split)
18
18
  * RUN list_integrations · connect_integration
19
19
  *
@@ -26,7 +26,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
26
26
  import { z } from "zod";
27
27
  import { get, post, del } from "./client.js";
28
28
 
29
- export const SERVER_VERSION = "0.56.0";
29
+ export const SERVER_VERSION = "0.57.0";
30
30
 
31
31
  // ─── helpers ──────────────────────────────────────────────────────────────────
32
32
 
@@ -610,7 +610,7 @@ export function createServer() {
610
610
  },
611
611
  async ({ folder, name, content, subfolder }) => {
612
612
  const r = await post("/v2/personal/files", { folder, name, content, subfolder });
613
- return { content: [{ type: "text", text: `Saved "${name}" to your ${r?.folder || folder} vault — it's searchable now.` }] };
613
+ return { content: [{ type: "text", text: `"${name}" is waiting in your Inbox to approve — once you do, it files into ${r?.folder || folder}.` }] };
614
614
  },
615
615
  );
616
616
 
@@ -800,13 +800,14 @@ export function createServer() {
800
800
  // ===========================================================================
801
801
  server.tool(
802
802
  "save_to_vault",
803
- "Save a file into the MEMBER'S OWN private knowledge vault, in a folder they name " +
804
- "(inbox, projects, decisions, companies, people, resources, archive, thoughts). Use it when the " +
805
- "member asks you to keep or file something for them 'save this to projects', 'put this PDF in " +
806
- "resources'. Pass markdown/text as `content`, OR a PDF/DOCX as base64 in `file` (it's extracted to " +
807
- "text automatically and stored so they can search it later). PRIVATE to the member nothing is " +
808
- "shared until they promote it to their department. NOT the shared company vault (use " +
809
- "propose_company_file), NOT a note on a contact (use save_note).",
803
+ "Save a file into the MEMBER'S OWN private knowledge vault. It lands in their INBOX for review " +
804
+ "with the target folder you name remembered (inbox, projects, decisions, companies, people, " +
805
+ "resources, archive, thoughts); the member approves it in the app, which files it into that folder " +
806
+ "and makes it searchable. Nothing enters their knowledge without their approval. Use it when the " +
807
+ "member asks you to keep or file something 'save this to resources', 'put this PDF in projects'. " +
808
+ "Pass markdown/text as `content`, OR a PDF/DOCX as base64 in `file` (extracted to text automatically). " +
809
+ "PRIVATE to the member. NOT the shared company vault (use propose_company_file), NOT a note on a " +
810
+ "contact (use save_note).",
810
811
  {
811
812
  folder: z.string().describe("The vault folder: inbox | projects | decisions | companies | people | resources | archive | thoughts."),
812
813
  name: z.string().describe("The file name, e.g. 'Q3 Plan.md' or 'vendor-contract.pdf'."),
@@ -818,7 +819,7 @@ export function createServer() {
818
819
  async ({ folder, name, content, file, mime, subfolder }) => {
819
820
  try {
820
821
  const r = await post("/v2/personal/files", { folder, name, content, file_base64: file, mime, subfolder });
821
- return { content: [{ type: "text", text: `Saved "${name}" to your ${r?.folder || folder} vault it's searchable now.` }] };
822
+ return { content: [{ type: "text", text: `"${name}" is waiting in your Inbox to approve — once you do, it files into ${r?.folder || folder} and becomes searchable.` }] };
822
823
  } catch (e) {
823
824
  const msg = String(e?.message || e);
824
825
  if (msg.includes("no_text_extracted")) return { content: [{ type: "text", text: `Couldn't read any text from that file — a scanned/image-only PDF has no extractable text. Text, markdown, or a text-based PDF/DOCX works.` }] };
@@ -828,6 +829,40 @@ export function createServer() {
828
829
  }
829
830
  );
830
831
 
832
+ // ===========================================================================
833
+ // TOOL: update_vault_file — POST /v2/personal/files/update
834
+ // Edit an existing personal file. Snapshots the old content to version history,
835
+ // then replaces/appends + re-embeds. save_to_vault is for NEW files (→ Inbox);
836
+ // this edits one that's already live.
837
+ // ===========================================================================
838
+ server.tool(
839
+ "update_vault_file",
840
+ "Edit an EXISTING file in the member's own vault — revise it or append to it. Identify the file by " +
841
+ "`file_id` (from a search or save result) or by `path` ('folder/[subfolder/]name.md'). The previous " +
842
+ "content is saved to version history FIRST (never lost), then your new content replaces it — or is added " +
843
+ "to the end with append:true — and the file is re-embedded; the member sees a fresh 'updated' time. Use " +
844
+ "this to keep a living document current: a rolling brief, a spec, a running log. For a NEW file use " +
845
+ "save_to_vault (which lands in the Inbox for approval); this edits one that already exists and is live.",
846
+ {
847
+ file_id: z.string().optional().describe("The file's id (from a search/save result). Provide this OR path."),
848
+ path: z.string().optional().describe("The file's vault path, e.g. 'resources/Edda Architecture.md'. Provide this OR file_id."),
849
+ content: z.string().describe("The new markdown content. Replaces the file's content unless append is true."),
850
+ append: z.boolean().optional().describe("If true, append this to the end of the file instead of replacing it."),
851
+ },
852
+ async ({ file_id, path, content, append }) => {
853
+ try {
854
+ const r = await post("/v2/personal/files/update", { file_id, path, content, append });
855
+ return { content: [{ type: "text", text: r?.note || "Updated the file." }] };
856
+ } catch (e) {
857
+ const msg = String(e?.message || e);
858
+ if (msg.includes("not_found")) return { content: [{ type: "text", text: "Couldn't find that file — check the file_id or path (folder/name.md)." }] };
859
+ if (msg.includes("file_id_or_path_required")) return { content: [{ type: "text", text: "Tell me which file to edit — pass file_id or path." }] };
860
+ if (msg.includes("content_required")) return { content: [{ type: "text", text: "Give me the new content to write." }] };
861
+ throw e;
862
+ }
863
+ }
864
+ );
865
+
831
866
  server.tool(
832
867
  "list_integrations",
833
868
  "List the integrations connected to this workspace (Gmail, HubSpot, Apollo, Instantly, LinkedIn, …) " +