@edda-business/mcp 0.67.0 → 0.69.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 +42 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edda-business/mcp",
3
- "version": "0.67.0",
3
+ "version": "0.69.0",
4
4
  "description": "Edda — the company data layer for AI agents.",
5
5
  "license": "Apache-2.0",
6
6
  "publishConfig": {
package/src/server.js CHANGED
@@ -278,6 +278,36 @@ export function createServer() {
278
278
  },
279
279
  );
280
280
 
281
+ // ===========================================================================
282
+ // TOOL: inbox — GET /v2/company/inbox
283
+ // The waiting room the agent-run ingest reads (Decision Log 2026-09-04: the agent is
284
+ // the analyst; the app is the filing clerk).
285
+ // ===========================================================================
286
+ server.tool(
287
+ "inbox",
288
+ "LIST what is waiting in the company Inbox — each staged item with its id, submitter, the " +
289
+ "submitter's suggested destination (if any), and a content preview. This is the start of the " +
290
+ "company ingest: read each item (use `read` for the full text if the preview is not enough), " +
291
+ "decide where it belongs among the real projects, then file it with " +
292
+ "reconcile { stagingId, path }. Admin/ambassador only.",
293
+ {},
294
+ async () => {
295
+ try {
296
+ const r = await get("/v2/company/inbox");
297
+ const pending = r?.pending ?? [];
298
+ if (!pending.length) return text("The company Inbox is empty — nothing waiting.");
299
+ const lines = pending.map((f) =>
300
+ `• ${f.name} (stagingId: ${f.id})\n staged ${String(f.created_at).slice(0, 10)}` +
301
+ `${f.suggested ? ` · suggested: ${f.suggested}` : ""}\n ${String(f.preview || "").replace(/\s+/g, " ").slice(0, 240)}`);
302
+ return text(`${pending.length} item(s) waiting in the company Inbox:\n\n${lines.join("\n\n")}`);
303
+ } catch (e) {
304
+ const msg = String(e?.message || e);
305
+ if (msg.includes("admins_only") || msg.includes("(403)")) return text("Only an admin or ambassador can read the company Inbox.");
306
+ throw e;
307
+ }
308
+ },
309
+ );
310
+
281
311
  // ===========================================================================
282
312
  // TOOL: reconcile — POST /v2/company/river/reconcile
283
313
  // Process the company Edda's Inbox: the River classifies + places each staged item into the
@@ -285,14 +315,19 @@ export function createServer() {
285
315
  // ===========================================================================
286
316
  server.tool(
287
317
  "reconcile",
288
- "PROCESS the company Inbox — have the company file every item waiting in its Inbox into the " +
289
- "right folder (classify place make searchable). Run this after staging, or when the person " +
290
- "says 'process the company inbox'. Optionally pass a single `stagingId` to file just that item. " +
291
- "Returns where each item landed.",
292
- { stagingId: z.string().optional().describe("Optional id of one staged item to file (from a submit_company_candidate receipt). Omit to process the whole Inbox.") },
293
- async ({ stagingId }) => {
318
+ "FILE items from the company Inbox — NOTHING files without an explicit per-item action " +
319
+ "(company ruling). Without arguments this only REPORTS what is waiting (each with a proposed " +
320
+ "destination). The ingest flow: list items with `inbox`, read and analyze each, then call this " +
321
+ "with that item's `stagingId` — plus an explicit `path` to place it exactly where you decided " +
322
+ "(omit `path` to accept the proposed destination). YOU are the analyst; your call is the review.",
323
+ {
324
+ stagingId: z.string().optional().describe("Id of one staged item to file (from an `inbox` row or a submit receipt). Omit to sweep the whole Inbox."),
325
+ path: z.string().optional().describe("Explicit destination folder path for THAT item (requires stagingId). Missing folders are created."),
326
+ },
327
+ async ({ stagingId, path }) => {
294
328
  try {
295
- const r = await post("/v2/company/river/reconcile", { stagingId });
329
+ if (path && !stagingId) return text("An explicit `path` files ONE analyzed item — pass its stagingId too.");
330
+ const r = await post("/v2/company/river/reconcile", { stagingId, forcePath: path });
296
331
  const receipts = r?.receipts ?? [];
297
332
  if (!receipts.length) return text("Nothing waiting in the company Inbox.");
298
333
  const lines = receipts.map((x) => ` ${x.filed ? "✓ filed" : x.status} → ${x.location}${x.audience && x.audience !== "company" ? ` [${x.audience}]` : ""} (${x.reason})`);