@membank/mcp 0.15.0 → 0.16.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/dist/bin.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +2 -2
package/dist/bin.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{readFileSync as e}from"node:fs";import{dirname as t,join as n}from"node:path";import{fileURLToPath as r}from"node:url";import{Command as i}from"commander";import{DatabaseManager as a,EmbeddingService as o,GLOBAL_SCOPE_HASH as s,MEMORY_TYPE_VALUES as c,MIGRATIONS as l,MemoryTypeSchema as u,PIN_BUDGET_THRESHOLD as d,QueryEngine as f,SynthesisEngine as p,clusterFlagged as m,createActivityLogger as h,createMemoryRepository as g,createProjectRepository as _,createSynthesisAgentRunner as v,createSynthesisRepository as y,deleteManyMemories as ee,deleteMemory as te,isSynthesisEnabled as ne,mergeMemories as b,resolveProject as x,resolveReviewMany as S,runScopeToProjectsMigration as C,saveMemory as w,updateMemory as T}from"@membank/core";import{StdioServerTransport as E}from"@modelcontextprotocol/sdk/server/stdio.js";import{homedir as D}from"node:os";import{Server as O}from"@modelcontextprotocol/sdk/server";import{CallToolRequestSchema as k,ErrorCode as A,ListToolsRequestSchema as j,McpError as M}from"@modelcontextprotocol/sdk/types.js";import{ZodError as N,z as P}from"zod";const F=P.enum([`current`,`global`,`all`]).optional().describe(`"current" (default) = this project + global memories; "global" = global memories only; "all" = every project`),re=P.enum([`current`,`global`]).optional().describe(`"current" (default) = scoped to this project; "global" = saved as a global memory`),I=P.object({content:P.string().min(1),type:u,tags:P.array(P.string()).optional(),scope:re}),L=P.object({id:P.string().min(1),content:P.string().min(1).optional(),type:u.optional(),tags:P.array(P.string()).optional()}),R=P.object({id:P.string().min(1)}),z=P.object({query:P.string().min(1),type:u.optional(),limit:P.number().int().positive().optional(),includePinned:P.boolean().optional(),scope:F}),B=P.object({name:P.string().min(1).describe(`Migration name to execute`)}),V=P.object({id:P.string().min(1)}),H=P.object({id:P.string().min(1)}),U=P.object({scope:F,limit:P.number().int().positive().max(100).optional().describe(`Maximum number of flagged memories to return`),minSimilarity:P.number().min(0).max(1).optional().describe(`Only include memories whose review event similarity is at or above this threshold`),maxSimilarity:P.number().min(0).max(1).optional().describe(`Only include memories whose review event similarity is at or below this threshold`)}),W=P.object({scope:F}),G=P.object({ids:P.array(P.string().min(1)).min(1).max(100)}),K=P.object({ids:P.array(P.string().min(1)).min(1).max(100)}),q=P.object({keep_id:P.string().min(1),drop_ids:P.array(P.string().min(1)).min(1).max(20),merged_content:P.string().min(1)});function J(){let t=n(D(),`.membank`,`config.json`);try{let n=e(t,`utf8`),r=JSON.parse(n);return{enabled:r.synthesis?.enabled===!0,maxTokensPerRun:r.synthesis?.maxTokensPerRun,debounceMs:r.synthesis?.debounceMs,stalenessDays:r.synthesis?.stalenessDays,inFlightTimeoutMs:r.synthesis?.inFlightTimeoutMs}}catch{return{enabled:!1}}}function Y(e,t){return{queryMemory:async e=>{let n=e.global===!0?void 0:e.projectHash??(await x()).hash,r=await t.query({query:e.query,projectHash:n,limit:e.limit??20,includePinned:!0});return JSON.stringify(r)},getMemorySummary:async()=>{let t=await x();return JSON.stringify(e.stats(t.hash))}}}function X(e={}){let t=e.useInMemoryDb?a.openInMemory():a.open(e.dbPath),n=new o,r=_(t),i=g(t,r),s=h(t),c=new f(t,n,i,s),l=J(),u;return l.enabled&&(u=new p(y(t),l,v(Y(i,c),l))),{db:t,embedding:n,repo:i,query:c,projects:r,activityLogger:s,synthEngine:u}}async function Z(e){if(e===`global`)return s;if(e!==`all`)return(await x()).hash}function Q(e,t){try{return e.parse(t)}catch(e){let t=e instanceof N?e.issues[0]?.message??e.message:String(e);throw new M(A.InvalidParams,t)}}function ie(e){let t=new O({name:`membank`,version:`0.1.0`},{capabilities:{tools:{}}});return t.setRequestHandler(j,()=>({tools:[{name:`save_memory`,description:`Save a new memory. Handles deduplication automatically — near-identical memories (cosine similarity >0.92, same type and project) overwrite the existing record.`,inputSchema:{type:`object`,properties:{content:{type:`string`,description:`Memory content to save`},type:{type:`string`,enum:[...c],description:`Memory type`},tags:{type:`array`,items:{type:`string`},description:`Optional tags`},scope:{type:`string`,enum:[`current`,`global`],description:`"current" (default) = scoped to this project; "global" = saved as a global memory`}},required:[`content`,`type`]}},{name:`update_memory`,description:`Update the content, type, and/or tags of an existing memory by id. All fields except id are optional.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to update`},content:{type:`string`,description:`New content for the memory`},type:{type:`string`,enum:[...c],description:`New type for the memory (reclassification)`},tags:{type:`array`,items:{type:`string`},description:`Replacement tags (optional)`}},required:[`id`]}},{name:`delete_memory`,description:`Delete a memory by id.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to delete`}},required:[`id`]}},{name:`query_memory`,description:`Search memories by semantic similarity. Returns results ranked by confidence score. scope="current" (default) searches this project and global memories; scope="global" returns global memories only; scope="all" returns across every project.`,inputSchema:{type:`object`,properties:{query:{type:`string`,description:`Search text`},type:{type:`string`,enum:[...c],description:`Filter by memory type`},limit:{type:`number`,description:`Maximum results to return (default 10)`},includePinned:{type:`boolean`,description:`Include pinned memories in results. Pinned memories are already injected into session context, so excluded by default to avoid duplicates.`},scope:{type:`string`,enum:[`current`,`global`,`all`],description:`"current" (default) = project + global; "global" = global memories only; "all" = all projects`}},required:[`query`]}},{name:`list_migrations`,description:`List available named data migrations. Use run_migration to execute one.`,inputSchema:{type:`object`,properties:{},required:[]}},{name:`run_migration`,description:`Execute a named data migration. Use list_migrations first to see available migration names.`,inputSchema:{type:`object`,properties:{name:{type:`string`,description:`Migration name to execute`}},required:[`name`]}},{name:`pin_memory`,description:`Pin a memory by id. Pinned memories are always injected into the session context.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to pin`}},required:[`id`]}},{name:`unpin_memory`,description:`Unpin a memory by id. Removes the memory from guaranteed session injection.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to unpin`}},required:[`id`]}},{name:`get_memory_summary`,description:`Returns aggregate stats for session orientation: total memories, counts by type, pinned count, and review queue size.`,inputSchema:{type:`object`,properties:{scope:{type:`string`,enum:[`current`,`global`,`all`],description:`"current" (default) = this project; "global" = global memories only; "all" = all projects`}},required:[]}},{name:`list_flagged_memories`,description:`List memories that have unresolved dedup review events. These were flagged automatically when a near-duplicate was saved (cosine similarity 0.75–0.92).`,inputSchema:{type:`object`,properties:{scope:{type:`string`,enum:[`current`,`global`,`all`],description:`"current" (default) = this project; "global" = global memories only; "all" = all projects`},limit:{type:`number`,description:`Maximum number of flagged memories to return (max 100)`},minSimilarity:{type:`number`,description:`Only include memories whose review event similarity is at or above this threshold (0–1)`},maxSimilarity:{type:`number`,description:`Only include memories whose review event similarity is at or below this threshold (0–1)`}},required:[]}},{name:`resolve_review`,description:`Mark all review events for this memory as resolved. Use after reviewing the flagged memory and deciding it is intentionally distinct from its near-duplicates and should be kept.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to resolve review events for`}},required:[`id`]}},{name:`delete_many`,description:`Delete multiple memories in a single call. Best-effort: each id is processed independently. Returns per-id status so you can see which deletions succeeded or failed.`,inputSchema:{type:`object`,properties:{ids:{type:`array`,items:{type:`string`},description:`Memory ids to delete (max 100)`}},required:[`ids`]}},{name:`resolve_many`,description:`Resolve review events for multiple memories in a single call. Best-effort: each id is processed independently. Returns per-id status.`,inputSchema:{type:`object`,properties:{ids:{type:`array`,items:{type:`string`},description:`Memory ids to resolve review events for (max 100)`}},required:[`ids`]}},{name:`merge_memories`,description:`Merge two or more memories into one. Writes merged_content to the kept memory, unions tags and projects from all dropped memories, then deletes the dropped memories. Re-runs dedup on the result. Use when flagged memories each carry unique information that should be combined.`,inputSchema:{type:`object`,properties:{keep_id:{type:`string`,description:`Memory id to keep and update with merged content`},drop_ids:{type:`array`,items:{type:`string`},description:`Memory ids to delete after merging their content in (max 20)`},merged_content:{type:`string`,description:`The combined content to write to the kept memory`}},required:[`keep_id`,`drop_ids`,`merged_content`]}}]})),t.setRequestHandler(k,async t=>{if(t.params.name===`save_memory`){let n=Q(I,t.params.arguments),r=n.scope===`global`?void 0:await x();try{let t=await w({content:n.content,type:n.type,tags:n.tags,projectScope:r},{repo:e.repo,embedder:e.embedding,activityLogger:e.activityLogger});if(e.synthEngine!==void 0){let n=t.projects.length>0?t.projects[0]?.scopeHash??s:`global`;e.synthEngine.markDirty(n)}return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`update_memory`){let n=Q(L,t.params.arguments);try{let t=await T(n.id,{content:n.content,type:n.type,tags:n.tags},{repo:e.repo,embedder:e.embedding,activityLogger:e.activityLogger});if(e.synthEngine!==void 0){let n=t.projects.length>0?t.projects[0]?.scopeHash??s:`global`;e.synthEngine.markDirty(n)}return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`delete_memory`){let n=Q(R,t.params.arguments);try{let t=e.repo.findById(n.id);if(t===void 0)return{content:[{type:`text`,text:`Memory not found: ${n.id}`}],isError:!0};let r=e.synthEngine===void 0?void 0:t.projects[0]?.scopeHash??s;return await te(n.id,e.repo,e.activityLogger),e.synthEngine!==void 0&&r!==void 0&&e.synthEngine.markDirty(r),{content:[{type:`text`,text:JSON.stringify({success:!0,id:n.id})}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`query_memory`){let n=Q(z,t.params.arguments),r=await Z(n.scope);try{let t=(await e.query.query({query:n.query,type:n.type,projectHash:r,limit:n.limit??10,includePinned:n.includePinned})).map(e=>({id:e.id,content:e.content,type:e.type,tags:e.tags,projects:e.projects,pinned:e.pinned,reviewEvents:e.reviewEvents,createdAt:e.createdAt,updatedAt:e.updatedAt,sourceHarness:e.sourceHarness,score:e.score}));return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`list_migrations`)return{content:[{type:`text`,text:JSON.stringify(l)}]};if(t.params.name===`run_migration`){let n=Q(B,t.params.arguments);if(n.name===`scope-to-projects`)try{let t=await C(e.projects);return t===null?{content:[{type:`text`,text:JSON.stringify({error:`No project found for current directory.`})}],isError:!0}:{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}throw new M(A.InvalidParams,`Unknown migration: "${n.name}". Available: ${l.map(e=>e.name).join(`, `)}`)}if(t.params.name===`pin_memory`||t.params.name===`unpin_memory`){let n=Q(V,t.params.arguments),r=t.params.name===`pin_memory`;try{let t=e.repo.setPin(n.id,r);if(r){let{hash:n}=await x(),r=e.repo.getPinnedCharCount(n);if(r>d&&!ne()){let e={...t,pinBudgetWarning:`Pinned memories now use ${r} characters (threshold: ${d}). Consider unpinning older memories or enabling synthesis to compress them.`};return{content:[{type:`text`,text:JSON.stringify(e)}]}}}return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`get_memory_summary`){let n=Q(W,t.params.arguments);try{let t=await Z(n.scope),r=e.repo.stats(t),i=e.repo.reviewQueueStats(t),a=m(e.repo.listReviewEdges(t)),o={...r,reviewQueue:{...i,clusters:a.length}};return{content:[{type:`text`,text:JSON.stringify(o)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`list_flagged_memories`){let n=Q(U,t.params.arguments);try{let t=await Z(n.scope),r=e.repo.listFlagged({projectHash:t,limit:n.limit,minSimilarity:n.minSimilarity,maxSimilarity:n.maxSimilarity}),i=r.flatMap(e=>e.reviewEvents.map(e=>e.conflictingMemoryId).filter(e=>e!==null)),a=[...new Set(i)],o=e.repo.findManyById(a),s=new Map(o.map(e=>[e.id,e])),c=m(e.repo.listReviewEdges(t)),l=new Map;for(let e of c)for(let t of e.memoryIds)l.set(t,e.clusterId);let u=new Set(r.map(e=>e.id)),d=c.filter(e=>e.memoryIds.some(e=>u.has(e))),f={memories:r.map(e=>({...e,clusterId:l.get(e.id)??null,reviewEvents:e.reviewEvents.map(e=>({...e,conflictingMemory:e.conflictingMemoryId===null?null:s.get(e.conflictingMemoryId)??null}))})),clusters:d};return{content:[{type:`text`,text:JSON.stringify(f)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`resolve_review`){let n=Q(H,t.params.arguments);try{return e.repo.findById(n.id)===void 0?{content:[{type:`text`,text:`Memory not found: ${n.id}`}],isError:!0}:(e.repo.resolveReviewEvents(n.id),{content:[{type:`text`,text:JSON.stringify({success:!0,id:n.id})}]})}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`delete_many`){let n=Q(G,t.params.arguments);try{let t=e.synthEngine===void 0?[]:[...new Set(n.ids.map(t=>e.repo.findById(t)).filter(e=>e!==void 0).map(e=>e.projects[0]?.scopeHash??s))],r=await ee(n.ids,e.repo,e.activityLogger);if(e.synthEngine!==void 0)for(let n of t)e.synthEngine.markDirty(n);return{content:[{type:`text`,text:JSON.stringify(r)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`resolve_many`){let n=Q(K,t.params.arguments);try{let t=S(n.ids,e.repo);return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`merge_memories`){let n=Q(q,t.params.arguments);try{let t=await b({keepId:n.keep_id,dropIds:n.drop_ids,mergedContent:n.merged_content},{repo:e.repo,embedder:e.embedding,activityLogger:e.activityLogger});if(e.synthEngine!==void 0){let n=t.kept.projects[0]?.scopeHash??s;e.synthEngine.markDirty(n)}return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}throw Error(`Unknown tool: ${t.params.name}`)}),t}async function ae(){let e;try{e=X()}catch(e){let t=e instanceof Error?e.message:String(e);process.stderr.write(`membank: failed to initialise core: ${t}\n`),process.exit(1)}if(e.synthEngine!==void 0)try{await e.synthEngine.init()}catch(e){let t=e instanceof Error?e.message:String(e);process.stderr.write(`membank: synthesis engine init failed: ${t}\n`)}let t=async()=>{e.synthEngine!==void 0&&await e.synthEngine.shutdown(),process.exit(0)};process.on(`SIGTERM`,()=>{t()}),process.on(`SIGINT`,()=>{t()});let n=ie(e),r=new E;await n.connect(r)}const{version:oe}=JSON.parse(e(n(t(r(import.meta.url)),`../package.json`),`utf8`)),$=new i;$.name(`membank-mcp`).description(`Membank MCP stdio server — for harness integration`).version(oe).action(ae),await $.parseAsync(process.argv);export{};
|
|
2
|
+
import{readFileSync as e}from"node:fs";import{dirname as t,join as n}from"node:path";import{fileURLToPath as r}from"node:url";import{Command as i}from"commander";import{DatabaseManager as a,EmbeddingService as o,GLOBAL_PROJECT_NAME as s,GLOBAL_SCOPE_HASH as c,MEMORY_TYPE_VALUES as l,MIGRATIONS as u,MemoryTypeSchema as d,PIN_BUDGET_THRESHOLD as f,QueryEngine as p,SynthesisEngine as m,clusterFlagged as h,createActivityLogger as g,createMemoryRepository as ee,createProjectRepository as te,createSynthesisAgentRunner as _,createSynthesisRepository as v,deleteManyMemories as y,deleteMemory as b,isSynthesisEnabled as ne,mergeMemories as x,resolveProject as S,resolveReviewMany as C,runScopeToProjectsMigration as w,saveMemory as T,updateMemory as E}from"@membank/core";import{StdioServerTransport as D}from"@modelcontextprotocol/sdk/server/stdio.js";import{homedir as O}from"node:os";import{Server as k}from"@modelcontextprotocol/sdk/server";import{CallToolRequestSchema as A,ErrorCode as j,ListToolsRequestSchema as M,McpError as N}from"@modelcontextprotocol/sdk/types.js";import{ZodError as P,z as F}from"zod";const I=F.enum([`current`,`global`,`all`]).optional().describe(`"current" (default) = this project + global memories; "global" = global memories only; "all" = every project`),L=F.enum([`current`,`global`]).optional().describe(`"current" (default) = scoped to this project; "global" = saved as a global memory`),R=F.object({content:F.string().min(1),type:d,tags:F.array(F.string()).optional(),scope:L}),z=F.object({id:F.string().min(1),content:F.string().min(1).optional(),type:d.optional(),tags:F.array(F.string()).optional()}),re=F.object({id:F.string().min(1)}),B=F.object({query:F.string().min(1),type:d.optional(),limit:F.number().int().positive().optional(),includePinned:F.boolean().optional(),scope:I}),V=F.object({name:F.string().min(1).describe(`Migration name to execute`)}),H=F.object({id:F.string().min(1)}),U=F.object({id:F.string().min(1)}),W=F.object({scope:I,limit:F.number().int().positive().max(100).optional().describe(`Maximum number of flagged memories to return`),minSimilarity:F.number().min(0).max(1).optional().describe(`Only include memories whose review event similarity is at or above this threshold`),maxSimilarity:F.number().min(0).max(1).optional().describe(`Only include memories whose review event similarity is at or below this threshold`)}),G=F.object({scope:I}),K=F.object({ids:F.array(F.string().min(1)).min(1).max(100)}),q=F.object({ids:F.array(F.string().min(1)).min(1).max(100)}),J=F.object({keep_id:F.string().min(1),drop_ids:F.array(F.string().min(1)).min(1).max(20),merged_content:F.string().min(1)}),Y=F.object({id:F.string().min(1).describe(`Memory ID to retrieve version history for`)}),X=F.object({scope:F.string().min(1).describe(`Scope to retrieve synthesis history for. Use "global" for global scope, a project name, or a 16-char hex scope hash.`)});function ie(){let t=n(O(),`.membank`,`config.json`);try{let n=e(t,`utf8`),r=JSON.parse(n);return{enabled:r.synthesis?.enabled===!0,...r.synthesis?.maxTokensPerRun!==void 0&&{maxTokensPerRun:r.synthesis.maxTokensPerRun},...r.synthesis?.debounceMs!==void 0&&{debounceMs:r.synthesis.debounceMs},...r.synthesis?.stalenessDays!==void 0&&{stalenessDays:r.synthesis.stalenessDays},...r.synthesis?.inFlightTimeoutMs!==void 0&&{inFlightTimeoutMs:r.synthesis.inFlightTimeoutMs}}}catch{return{enabled:!1}}}function ae(e,t){return{queryMemory:async e=>{let n=e.global===!0?void 0:e.projectHash??(await S()).hash,r=await t.query({query:e.query,projectHash:n,limit:e.limit??20,includePinned:!0});return JSON.stringify(r)},getMemorySummary:async()=>{let t=await S();return JSON.stringify(e.stats(t.hash))}}}function oe(e={}){let t=e.useInMemoryDb?a.openInMemory():a.open(e.dbPath),n=new o,r=te(t),i=ee(t,r),s=g(t),c=new p(t,n,i,s),l=v(t),u=ie(),d;return u.enabled&&(d=new m(l,u,_(ae(i,c),u))),{db:t,embedding:n,repo:i,query:c,projects:r,activityLogger:s,synthRepo:l,...d!==void 0&&{synthEngine:d}}}async function Z(e){if(e===s)return c;if(e!==`all`)return(await S()).hash}function Q(e,t){try{return e.parse(t)}catch(e){let t=e instanceof P?e.issues[0]?.message??e.message:String(e);throw new N(j.InvalidParams,t)}}function se(e){let t=new k({name:`membank`,version:`0.1.0`},{capabilities:{tools:{}}});return t.setRequestHandler(M,()=>({tools:[{name:`save_memory`,description:`Save a new memory. Handles deduplication automatically — near-identical memories (cosine similarity >0.92, same type and project) overwrite the existing record.`,inputSchema:{type:`object`,properties:{content:{type:`string`,description:`Memory content to save`},type:{type:`string`,enum:[...l],description:`Memory type`},tags:{type:`array`,items:{type:`string`},description:`Optional tags`},scope:{type:`string`,enum:[`current`,`global`],description:`"current" (default) = scoped to this project; "global" = saved as a global memory`}},required:[`content`,`type`]}},{name:`update_memory`,description:`Update the content, type, and/or tags of an existing memory by id. All fields except id are optional.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to update`},content:{type:`string`,description:`New content for the memory`},type:{type:`string`,enum:[...l],description:`New type for the memory (reclassification)`},tags:{type:`array`,items:{type:`string`},description:`Replacement tags (optional)`}},required:[`id`]}},{name:`delete_memory`,description:`Delete a memory by id.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to delete`}},required:[`id`]}},{name:`query_memory`,description:`Search memories by semantic similarity. Returns results ranked by confidence score. scope="current" (default) searches this project and global memories; scope="global" returns global memories only; scope="all" returns across every project.`,inputSchema:{type:`object`,properties:{query:{type:`string`,description:`Search text`},type:{type:`string`,enum:[...l],description:`Filter by memory type`},limit:{type:`number`,description:`Maximum results to return (default 10)`},includePinned:{type:`boolean`,description:`Include pinned memories in results. Pinned memories are already injected into session context, so excluded by default to avoid duplicates.`},scope:{type:`string`,enum:[`current`,`global`,`all`],description:`"current" (default) = project + global; "global" = global memories only; "all" = all projects`}},required:[`query`]}},{name:`list_migrations`,description:`List available named data migrations. Use run_migration to execute one.`,inputSchema:{type:`object`,properties:{},required:[]}},{name:`run_migration`,description:`Execute a named data migration. Use list_migrations first to see available migration names.`,inputSchema:{type:`object`,properties:{name:{type:`string`,description:`Migration name to execute`}},required:[`name`]}},{name:`pin_memory`,description:`Pin a memory by id. Pinned memories are always injected into the session context.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to pin`}},required:[`id`]}},{name:`unpin_memory`,description:`Unpin a memory by id. Removes the memory from guaranteed session injection.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to unpin`}},required:[`id`]}},{name:`get_memory_summary`,description:`Returns aggregate stats for session orientation: total memories, counts by type, pinned count, and review queue size.`,inputSchema:{type:`object`,properties:{scope:{type:`string`,enum:[`current`,`global`,`all`],description:`"current" (default) = this project; "global" = global memories only; "all" = all projects`}},required:[]}},{name:`list_flagged_memories`,description:`List memories that have unresolved dedup review events. These were flagged automatically when a near-duplicate was saved (cosine similarity 0.75–0.92).`,inputSchema:{type:`object`,properties:{scope:{type:`string`,enum:[`current`,`global`,`all`],description:`"current" (default) = this project; "global" = global memories only; "all" = all projects`},limit:{type:`number`,description:`Maximum number of flagged memories to return (max 100)`},minSimilarity:{type:`number`,description:`Only include memories whose review event similarity is at or above this threshold (0–1)`},maxSimilarity:{type:`number`,description:`Only include memories whose review event similarity is at or below this threshold (0–1)`}},required:[]}},{name:`resolve_review`,description:`Mark all review events for this memory as resolved. Use after reviewing the flagged memory and deciding it is intentionally distinct from its near-duplicates and should be kept.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to resolve review events for`}},required:[`id`]}},{name:`delete_many`,description:`Delete multiple memories in a single call. Best-effort: each id is processed independently. Returns per-id status so you can see which deletions succeeded or failed.`,inputSchema:{type:`object`,properties:{ids:{type:`array`,items:{type:`string`},description:`Memory ids to delete (max 100)`}},required:[`ids`]}},{name:`resolve_many`,description:`Resolve review events for multiple memories in a single call. Best-effort: each id is processed independently. Returns per-id status.`,inputSchema:{type:`object`,properties:{ids:{type:`array`,items:{type:`string`},description:`Memory ids to resolve review events for (max 100)`}},required:[`ids`]}},{name:`merge_memories`,description:`Merge two or more memories into one. Writes merged_content to the kept memory, unions tags and projects from all dropped memories, then deletes the dropped memories. Re-runs dedup on the result. Use when flagged memories each carry unique information that should be combined.`,inputSchema:{type:`object`,properties:{keep_id:{type:`string`,description:`Memory id to keep and update with merged content`},drop_ids:{type:`array`,items:{type:`string`},description:`Memory ids to delete after merging their content in (max 20)`},merged_content:{type:`string`,description:`The combined content to write to the kept memory`}},required:[`keep_id`,`drop_ids`,`merged_content`]}},{name:`list_memory_history`,description:`List the version history of a memory. Returns up to 10 past content snapshots in descending version order. To revert, call update_memory with the content from the desired version.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory ID to retrieve version history for`}},required:[`id`]}},{name:`list_synthesis_history`,description:`List the version history of a synthesis. Returns up to 5 past synthesis snapshots in descending version order. To revert, use the CLI: membank synthesize revert <version>.`,inputSchema:{type:`object`,properties:{scope:{type:`string`,description:`Scope to retrieve history for. Use "global" for global scope or a 16-char hex scope hash.`}},required:[`scope`]}}]})),t.setRequestHandler(A,async t=>{if(t.params.name===`save_memory`){let n=Q(R,t.params.arguments),r=n.scope===`global`?void 0:await S();try{let t=await T({content:n.content,type:n.type,tags:n.tags,projectScope:r},{repo:e.repo,embedder:e.embedding,activityLogger:e.activityLogger});if(e.synthEngine!==void 0){let n=t.projects.length>0?t.projects[0]?.scopeHash??c:`global`;e.synthEngine.markDirty(n)}return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`update_memory`){let n=Q(z,t.params.arguments);try{let t=await E(n.id,{content:n.content,type:n.type,tags:n.tags},{repo:e.repo,embedder:e.embedding,activityLogger:e.activityLogger});if(e.synthEngine!==void 0){let n=t.projects.length>0?t.projects[0]?.scopeHash??c:`global`;e.synthEngine.markDirty(n)}return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`delete_memory`){let n=Q(re,t.params.arguments);try{let t=e.repo.findById(n.id);if(t===void 0)return{content:[{type:`text`,text:`Memory not found: ${n.id}`}],isError:!0};let r=e.synthEngine===void 0?void 0:t.projects[0]?.scopeHash??c;return await b(n.id,e.repo,e.activityLogger),e.synthEngine!==void 0&&r!==void 0&&e.synthEngine.markDirty(r),{content:[{type:`text`,text:JSON.stringify({success:!0,id:n.id})}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`query_memory`){let n=Q(B,t.params.arguments),r=await Z(n.scope);try{let t=(await e.query.query({query:n.query,type:n.type,projectHash:r,limit:n.limit??10,includePinned:n.includePinned})).map(e=>({id:e.id,content:e.content,type:e.type,tags:e.tags,projects:e.projects,pinned:e.pinned,reviewEvents:e.reviewEvents,createdAt:e.createdAt,updatedAt:e.updatedAt,sourceHarness:e.sourceHarness,score:e.score}));return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`list_migrations`)return{content:[{type:`text`,text:JSON.stringify(u)}]};if(t.params.name===`run_migration`){let n=Q(V,t.params.arguments);if(n.name===`scope-to-projects`)try{let t=await w(e.projects);return t===null?{content:[{type:`text`,text:JSON.stringify({error:`No project found for current directory.`})}],isError:!0}:{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}throw new N(j.InvalidParams,`Unknown migration: "${n.name}". Available: ${u.map(e=>e.name).join(`, `)}`)}if(t.params.name===`pin_memory`||t.params.name===`unpin_memory`){let n=Q(H,t.params.arguments),r=t.params.name===`pin_memory`;try{let t=e.repo.setPin(n.id,r);if(r){let{hash:n}=await S(),r=e.repo.getPinnedCharCount(n);if(r>f&&!ne()){let e={...t,pinBudgetWarning:`Pinned memories now use ${r} characters (threshold: ${f}). Consider unpinning older memories or enabling synthesis to compress them.`};return{content:[{type:`text`,text:JSON.stringify(e)}]}}}return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`get_memory_summary`){let n=Q(G,t.params.arguments);try{let t=await Z(n.scope),r=e.repo.stats(t),i=e.repo.reviewQueueStats(t),a=h(e.repo.listReviewEdges(t)),o={...r,reviewQueue:{...i,clusters:a.length}};return{content:[{type:`text`,text:JSON.stringify(o)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`list_flagged_memories`){let n=Q(W,t.params.arguments);try{let t=await Z(n.scope),r=e.repo.listFlagged({...t!==void 0&&{projectHash:t},...n.limit!==void 0&&{limit:n.limit},...n.minSimilarity!==void 0&&{minSimilarity:n.minSimilarity},...n.maxSimilarity!==void 0&&{maxSimilarity:n.maxSimilarity}}),i=r.flatMap(e=>e.reviewEvents.map(e=>e.conflictingMemoryId).filter(e=>e!==null)),a=[...new Set(i)],o=e.repo.findManyById(a),s=new Map(o.map(e=>[e.id,e])),c=h(e.repo.listReviewEdges(t)),l=new Map;for(let e of c)for(let t of e.memoryIds)l.set(t,e.clusterId);let u=new Set(r.map(e=>e.id)),d=c.filter(e=>e.memoryIds.some(e=>u.has(e))),f={memories:r.map(e=>({...e,clusterId:l.get(e.id)??null,reviewEvents:e.reviewEvents.map(e=>({...e,conflictingMemory:e.conflictingMemoryId===null?null:s.get(e.conflictingMemoryId)??null}))})),clusters:d};return{content:[{type:`text`,text:JSON.stringify(f)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`resolve_review`){let n=Q(U,t.params.arguments);try{return e.repo.findById(n.id)===void 0?{content:[{type:`text`,text:`Memory not found: ${n.id}`}],isError:!0}:(e.repo.resolveReviewEvents(n.id),{content:[{type:`text`,text:JSON.stringify({success:!0,id:n.id})}]})}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`delete_many`){let n=Q(K,t.params.arguments);try{let t=e.synthEngine===void 0?[]:[...new Set(n.ids.map(t=>e.repo.findById(t)).filter(e=>e!==void 0).map(e=>e.projects[0]?.scopeHash??c))],r=await y(n.ids,e.repo,e.activityLogger);if(e.synthEngine!==void 0)for(let n of t)e.synthEngine.markDirty(n);return{content:[{type:`text`,text:JSON.stringify(r)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`resolve_many`){let n=Q(q,t.params.arguments);try{let t=C(n.ids,e.repo);return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`merge_memories`){let n=Q(J,t.params.arguments);try{let t=await x({keepId:n.keep_id,dropIds:n.drop_ids,mergedContent:n.merged_content},{repo:e.repo,embedder:e.embedding,activityLogger:e.activityLogger});if(e.synthEngine!==void 0){let n=t.kept.projects[0]?.scopeHash??c;e.synthEngine.markDirty(n)}return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`list_memory_history`){let n=Q(Y,t.params.arguments),r=e.repo.listVersions(n.id);return{content:[{type:`text`,text:JSON.stringify(r)}]}}if(t.params.name===`list_synthesis_history`){let n=Q(X,t.params.arguments),r=n.scope===s?c:n.scope,i=e.synthRepo.listVersions(r);return{content:[{type:`text`,text:JSON.stringify(i)}]}}throw Error(`Unknown tool: ${t.params.name}`)}),t}async function ce(){let e;try{e=oe()}catch(e){let t=e instanceof Error?e.message:String(e);process.stderr.write(`membank: failed to initialise core: ${t}\n`),process.exit(1)}if(e.synthEngine!==void 0)try{await e.synthEngine.init()}catch(e){let t=e instanceof Error?e.message:String(e);process.stderr.write(`membank: synthesis engine init failed: ${t}\n`)}let t=async()=>{e.synthEngine!==void 0&&await e.synthEngine.shutdown(),process.exit(0)};process.on(`SIGTERM`,()=>{t()}),process.on(`SIGINT`,()=>{t()});let n=se(e),r=new D;await n.connect(r)}const{version:le}=JSON.parse(e(n(t(r(import.meta.url)),`../package.json`),`utf8`)),$=new i;$.name(`membank-mcp`).description(`Membank MCP stdio server — for harness integration`).version(le).action(ce),await $.parseAsync(process.argv);export{};
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{DatabaseManager as e,EmbeddingService as t,GLOBAL_SCOPE_HASH as n,MEMORY_TYPE_VALUES as r,MIGRATIONS as i,MemoryTypeSchema as a,PIN_BUDGET_THRESHOLD as o,QueryEngine as s,SynthesisEngine as c,clusterFlagged as l,createActivityLogger as u,createClaudeCodeTranscriptReader as d,createExtractionAgentRunner as f,createExtractionRunRepository as p,createMemoryRepository as m,createProjectRepository as h,createSynthesisAgentRunner as g,createSynthesisRepository as _,deleteManyMemories as ee,deleteMemory as v,isSynthesisEnabled as y,mergeMemories as te,resolveProject as b,resolveReviewMany as x,runExtraction as S,runScopeToProjectsMigration as C,runSynthesis as ne,saveMemory as w,updateMemory as T}from"@membank/core";import{StdioServerTransport as re}from"@modelcontextprotocol/sdk/server/stdio.js";import{readFileSync as E}from"node:fs";import{homedir as D}from"node:os";import{join as O}from"node:path";import{Server as k}from"@modelcontextprotocol/sdk/server";import{CallToolRequestSchema as A,ErrorCode as j,ListToolsRequestSchema as M,McpError as N}from"@modelcontextprotocol/sdk/types.js";import{ZodError as P,z as F}from"zod";const I=F.enum([`current`,`global`,`all`]).optional().describe(`"current" (default) = this project + global memories; "global" = global memories only; "all" = every project`),L=F.enum([`current`,`global`]).optional().describe(`"current" (default) = scoped to this project; "global" = saved as a global memory`),R=F.object({content:F.string().min(1),type:a,tags:F.array(F.string()).optional(),scope:L}),z=F.object({id:F.string().min(1),content:F.string().min(1).optional(),type:a.optional(),tags:F.array(F.string()).optional()}),B=F.object({id:F.string().min(1)}),V=F.object({query:F.string().min(1),type:a.optional(),limit:F.number().int().positive().optional(),includePinned:F.boolean().optional(),scope:I}),ie=F.object({name:F.string().min(1).describe(`Migration name to execute`)}),H=F.object({id:F.string().min(1)}),U=F.object({id:F.string().min(1)}),W=F.object({scope:I,limit:F.number().int().positive().max(100).optional().describe(`Maximum number of flagged memories to return`),minSimilarity:F.number().min(0).max(1).optional().describe(`Only include memories whose review event similarity is at or above this threshold`),maxSimilarity:F.number().min(0).max(1).optional().describe(`Only include memories whose review event similarity is at or below this threshold`)}),G=F.object({scope:I}),K=F.object({ids:F.array(F.string().min(1)).min(1).max(100)}),q=F.object({ids:F.array(F.string().min(1)).min(1).max(100)}),J=F.object({keep_id:F.string().min(1),drop_ids:F.array(F.string().min(1)).min(1).max(20),merged_content:F.string().min(1)});function Y(){let e=O(D(),`.membank`,`config.json`);try{let t=E(e,`utf8`),n=JSON.parse(t);return{enabled:n.synthesis?.enabled===!0,maxTokensPerRun:n.synthesis?.maxTokensPerRun,debounceMs:n.synthesis?.debounceMs,stalenessDays:n.synthesis?.stalenessDays,inFlightTimeoutMs:n.synthesis?.inFlightTimeoutMs}}catch{return{enabled:!1}}}function ae(e,t,r){return{queryMemory:async e=>{let r=e.global===!0?n:e.projectHash??(await b()).hash,i=await t.query({query:e.query,projectHash:r,limit:e.limit??10,includePinned:!0});return JSON.stringify(i)},saveMemory:async t=>{let n=t.global===!0?void 0:await b(),i=await w({content:t.content,type:a.parse(t.type),tags:t.tags,projectScope:n,sourceHarness:`membank-extraction`},{repo:e,embedder:r});return JSON.stringify(i)},updateMemory:async t=>{let n=await T(t.id,{content:t.content,type:t.type===void 0?void 0:a.parse(t.type),tags:t.tags},{repo:e,embedder:r});return JSON.stringify(n)}}}function X(e,t){return{queryMemory:async e=>{let n=e.global===!0?void 0:e.projectHash??(await b()).hash,r=await t.query({query:e.query,projectHash:n,limit:e.limit??20,includePinned:!0});return JSON.stringify(r)},getMemorySummary:async()=>{let t=await b();return JSON.stringify(e.stats(t.hash))}}}function oe(n={}){let r=n.useInMemoryDb?e.openInMemory():e.open(n.dbPath),i=new t,a=h(r),o=m(r,a),l=u(r),d=new s(r,i,o,l),f=Y(),p;return f.enabled&&(p=new c(_(r),f,g(X(o,d),f))),{db:r,embedding:i,repo:o,query:d,projects:a,activityLogger:l,synthEngine:p}}async function Z(e){if(e===`global`)return n;if(e!==`all`)return(await b()).hash}function Q(e,t){try{return e.parse(t)}catch(e){let t=e instanceof P?e.issues[0]?.message??e.message:String(e);throw new N(j.InvalidParams,t)}}function se(e){let t=new k({name:`membank`,version:`0.1.0`},{capabilities:{tools:{}}});return t.setRequestHandler(M,()=>({tools:[{name:`save_memory`,description:`Save a new memory. Handles deduplication automatically — near-identical memories (cosine similarity >0.92, same type and project) overwrite the existing record.`,inputSchema:{type:`object`,properties:{content:{type:`string`,description:`Memory content to save`},type:{type:`string`,enum:[...r],description:`Memory type`},tags:{type:`array`,items:{type:`string`},description:`Optional tags`},scope:{type:`string`,enum:[`current`,`global`],description:`"current" (default) = scoped to this project; "global" = saved as a global memory`}},required:[`content`,`type`]}},{name:`update_memory`,description:`Update the content, type, and/or tags of an existing memory by id. All fields except id are optional.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to update`},content:{type:`string`,description:`New content for the memory`},type:{type:`string`,enum:[...r],description:`New type for the memory (reclassification)`},tags:{type:`array`,items:{type:`string`},description:`Replacement tags (optional)`}},required:[`id`]}},{name:`delete_memory`,description:`Delete a memory by id.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to delete`}},required:[`id`]}},{name:`query_memory`,description:`Search memories by semantic similarity. Returns results ranked by confidence score. scope="current" (default) searches this project and global memories; scope="global" returns global memories only; scope="all" returns across every project.`,inputSchema:{type:`object`,properties:{query:{type:`string`,description:`Search text`},type:{type:`string`,enum:[...r],description:`Filter by memory type`},limit:{type:`number`,description:`Maximum results to return (default 10)`},includePinned:{type:`boolean`,description:`Include pinned memories in results. Pinned memories are already injected into session context, so excluded by default to avoid duplicates.`},scope:{type:`string`,enum:[`current`,`global`,`all`],description:`"current" (default) = project + global; "global" = global memories only; "all" = all projects`}},required:[`query`]}},{name:`list_migrations`,description:`List available named data migrations. Use run_migration to execute one.`,inputSchema:{type:`object`,properties:{},required:[]}},{name:`run_migration`,description:`Execute a named data migration. Use list_migrations first to see available migration names.`,inputSchema:{type:`object`,properties:{name:{type:`string`,description:`Migration name to execute`}},required:[`name`]}},{name:`pin_memory`,description:`Pin a memory by id. Pinned memories are always injected into the session context.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to pin`}},required:[`id`]}},{name:`unpin_memory`,description:`Unpin a memory by id. Removes the memory from guaranteed session injection.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to unpin`}},required:[`id`]}},{name:`get_memory_summary`,description:`Returns aggregate stats for session orientation: total memories, counts by type, pinned count, and review queue size.`,inputSchema:{type:`object`,properties:{scope:{type:`string`,enum:[`current`,`global`,`all`],description:`"current" (default) = this project; "global" = global memories only; "all" = all projects`}},required:[]}},{name:`list_flagged_memories`,description:`List memories that have unresolved dedup review events. These were flagged automatically when a near-duplicate was saved (cosine similarity 0.75–0.92).`,inputSchema:{type:`object`,properties:{scope:{type:`string`,enum:[`current`,`global`,`all`],description:`"current" (default) = this project; "global" = global memories only; "all" = all projects`},limit:{type:`number`,description:`Maximum number of flagged memories to return (max 100)`},minSimilarity:{type:`number`,description:`Only include memories whose review event similarity is at or above this threshold (0–1)`},maxSimilarity:{type:`number`,description:`Only include memories whose review event similarity is at or below this threshold (0–1)`}},required:[]}},{name:`resolve_review`,description:`Mark all review events for this memory as resolved. Use after reviewing the flagged memory and deciding it is intentionally distinct from its near-duplicates and should be kept.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to resolve review events for`}},required:[`id`]}},{name:`delete_many`,description:`Delete multiple memories in a single call. Best-effort: each id is processed independently. Returns per-id status so you can see which deletions succeeded or failed.`,inputSchema:{type:`object`,properties:{ids:{type:`array`,items:{type:`string`},description:`Memory ids to delete (max 100)`}},required:[`ids`]}},{name:`resolve_many`,description:`Resolve review events for multiple memories in a single call. Best-effort: each id is processed independently. Returns per-id status.`,inputSchema:{type:`object`,properties:{ids:{type:`array`,items:{type:`string`},description:`Memory ids to resolve review events for (max 100)`}},required:[`ids`]}},{name:`merge_memories`,description:`Merge two or more memories into one. Writes merged_content to the kept memory, unions tags and projects from all dropped memories, then deletes the dropped memories. Re-runs dedup on the result. Use when flagged memories each carry unique information that should be combined.`,inputSchema:{type:`object`,properties:{keep_id:{type:`string`,description:`Memory id to keep and update with merged content`},drop_ids:{type:`array`,items:{type:`string`},description:`Memory ids to delete after merging their content in (max 20)`},merged_content:{type:`string`,description:`The combined content to write to the kept memory`}},required:[`keep_id`,`drop_ids`,`merged_content`]}}]})),t.setRequestHandler(A,async t=>{if(t.params.name===`save_memory`){let r=Q(R,t.params.arguments),i=r.scope===`global`?void 0:await b();try{let t=await w({content:r.content,type:r.type,tags:r.tags,projectScope:i},{repo:e.repo,embedder:e.embedding,activityLogger:e.activityLogger});if(e.synthEngine!==void 0){let r=t.projects.length>0?t.projects[0]?.scopeHash??n:`global`;e.synthEngine.markDirty(r)}return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`update_memory`){let r=Q(z,t.params.arguments);try{let t=await T(r.id,{content:r.content,type:r.type,tags:r.tags},{repo:e.repo,embedder:e.embedding,activityLogger:e.activityLogger});if(e.synthEngine!==void 0){let r=t.projects.length>0?t.projects[0]?.scopeHash??n:`global`;e.synthEngine.markDirty(r)}return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`delete_memory`){let r=Q(B,t.params.arguments);try{let t=e.repo.findById(r.id);if(t===void 0)return{content:[{type:`text`,text:`Memory not found: ${r.id}`}],isError:!0};let i=e.synthEngine===void 0?void 0:t.projects[0]?.scopeHash??n;return await v(r.id,e.repo,e.activityLogger),e.synthEngine!==void 0&&i!==void 0&&e.synthEngine.markDirty(i),{content:[{type:`text`,text:JSON.stringify({success:!0,id:r.id})}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`query_memory`){let n=Q(V,t.params.arguments),r=await Z(n.scope);try{let t=(await e.query.query({query:n.query,type:n.type,projectHash:r,limit:n.limit??10,includePinned:n.includePinned})).map(e=>({id:e.id,content:e.content,type:e.type,tags:e.tags,projects:e.projects,pinned:e.pinned,reviewEvents:e.reviewEvents,createdAt:e.createdAt,updatedAt:e.updatedAt,sourceHarness:e.sourceHarness,score:e.score}));return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`list_migrations`)return{content:[{type:`text`,text:JSON.stringify(i)}]};if(t.params.name===`run_migration`){let n=Q(ie,t.params.arguments);if(n.name===`scope-to-projects`)try{let t=await C(e.projects);return t===null?{content:[{type:`text`,text:JSON.stringify({error:`No project found for current directory.`})}],isError:!0}:{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}throw new N(j.InvalidParams,`Unknown migration: "${n.name}". Available: ${i.map(e=>e.name).join(`, `)}`)}if(t.params.name===`pin_memory`||t.params.name===`unpin_memory`){let n=Q(H,t.params.arguments),r=t.params.name===`pin_memory`;try{let t=e.repo.setPin(n.id,r);if(r){let{hash:n}=await b(),r=e.repo.getPinnedCharCount(n);if(r>o&&!y()){let e={...t,pinBudgetWarning:`Pinned memories now use ${r} characters (threshold: ${o}). Consider unpinning older memories or enabling synthesis to compress them.`};return{content:[{type:`text`,text:JSON.stringify(e)}]}}}return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`get_memory_summary`){let n=Q(G,t.params.arguments);try{let t=await Z(n.scope),r=e.repo.stats(t),i=e.repo.reviewQueueStats(t),a=l(e.repo.listReviewEdges(t)),o={...r,reviewQueue:{...i,clusters:a.length}};return{content:[{type:`text`,text:JSON.stringify(o)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`list_flagged_memories`){let n=Q(W,t.params.arguments);try{let t=await Z(n.scope),r=e.repo.listFlagged({projectHash:t,limit:n.limit,minSimilarity:n.minSimilarity,maxSimilarity:n.maxSimilarity}),i=r.flatMap(e=>e.reviewEvents.map(e=>e.conflictingMemoryId).filter(e=>e!==null)),a=[...new Set(i)],o=e.repo.findManyById(a),s=new Map(o.map(e=>[e.id,e])),c=l(e.repo.listReviewEdges(t)),u=new Map;for(let e of c)for(let t of e.memoryIds)u.set(t,e.clusterId);let d=new Set(r.map(e=>e.id)),f=c.filter(e=>e.memoryIds.some(e=>d.has(e))),p={memories:r.map(e=>({...e,clusterId:u.get(e.id)??null,reviewEvents:e.reviewEvents.map(e=>({...e,conflictingMemory:e.conflictingMemoryId===null?null:s.get(e.conflictingMemoryId)??null}))})),clusters:f};return{content:[{type:`text`,text:JSON.stringify(p)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`resolve_review`){let n=Q(U,t.params.arguments);try{return e.repo.findById(n.id)===void 0?{content:[{type:`text`,text:`Memory not found: ${n.id}`}],isError:!0}:(e.repo.resolveReviewEvents(n.id),{content:[{type:`text`,text:JSON.stringify({success:!0,id:n.id})}]})}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`delete_many`){let r=Q(K,t.params.arguments);try{let t=e.synthEngine===void 0?[]:[...new Set(r.ids.map(t=>e.repo.findById(t)).filter(e=>e!==void 0).map(e=>e.projects[0]?.scopeHash??n))],i=await ee(r.ids,e.repo,e.activityLogger);if(e.synthEngine!==void 0)for(let n of t)e.synthEngine.markDirty(n);return{content:[{type:`text`,text:JSON.stringify(i)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`resolve_many`){let n=Q(q,t.params.arguments);try{let t=x(n.ids,e.repo);return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`merge_memories`){let r=Q(J,t.params.arguments);try{let t=await te({keepId:r.keep_id,dropIds:r.drop_ids,mergedContent:r.merged_content},{repo:e.repo,embedder:e.embedding,activityLogger:e.activityLogger});if(e.synthEngine!==void 0){let r=t.kept.projects[0]?.scopeHash??n;e.synthEngine.markDirty(r)}return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}throw Error(`Unknown tool: ${t.params.name}`)}),t}async function ce(){let e;try{e=oe()}catch(e){let t=e instanceof Error?e.message:String(e);process.stderr.write(`membank: failed to initialise core: ${t}\n`),process.exit(1)}if(e.synthEngine!==void 0)try{await e.synthEngine.init()}catch(e){let t=e instanceof Error?e.message:String(e);process.stderr.write(`membank: synthesis engine init failed: ${t}\n`)}let t=async()=>{e.synthEngine!==void 0&&await e.synthEngine.shutdown(),process.exit(0)};process.on(`SIGTERM`,()=>{t()}),process.on(`SIGINT`,()=>{t()});let n=se(e),r=new re;await n.connect(r)}async function le(r){if(!y())throw Error(`Synthesis is not enabled. Run: membank config set synthesis.enabled true`);let i=e.open(),a=new t,o=h(i),c=m(i,o),l=new s(i,a,c),u=_(i),d=g(X(c,l),{enabled:!0}),f=r;if(r===`global`)f=n;else if(!/^[0-9a-f]{16}$/.test(r)){let e=o.getByName(r);e!==void 0&&(f=e.scopeHash)}try{return await ne(f,{synthRepo:u,agentRunner:d})}finally{i.close()}}async function $(n){let r=e.open();try{let e=new t,i=m(r,h(r)),a=new s(r,e,i),o=p(r),c=f(ae(i,a,e)),l=d(),u=n.projectHash??(await b()).hash;return await S({sessionId:n.sessionId,transcriptPath:n.transcriptPath,projectHash:u},{repo:o,transcripts:l,agent:c,config:{}})}finally{r.close()}}export{$ as runExtraction,le as runSynthesis,ce as startServer};
|
|
1
|
+
import{DatabaseManager as e,EmbeddingService as t,GLOBAL_PROJECT_NAME as n,GLOBAL_SCOPE_HASH as r,MEMORY_TYPE_VALUES as i,MIGRATIONS as a,MemoryTypeSchema as o,PIN_BUDGET_THRESHOLD as s,QueryEngine as c,SynthesisEngine as l,clusterFlagged as u,createActivityLogger as d,createClaudeCodeTranscriptReader as f,createExtractionAgentRunner as p,createExtractionRunRepository as m,createMemoryRepository as h,createProjectRepository as g,createSynthesisAgentRunner as _,createSynthesisRepository as v,deleteManyMemories as ee,deleteMemory as y,isSynthesisEnabled as b,mergeMemories as te,resolveProject as x,resolveReviewMany as S,runExtraction as C,runScopeToProjectsMigration as w,runSynthesis as ne,saveMemory as T,updateMemory as E}from"@membank/core";import{StdioServerTransport as re}from"@modelcontextprotocol/sdk/server/stdio.js";import{readFileSync as D}from"node:fs";import{homedir as O}from"node:os";import{join as k}from"node:path";import{Server as A}from"@modelcontextprotocol/sdk/server";import{CallToolRequestSchema as j,ErrorCode as M,ListToolsRequestSchema as N,McpError as P}from"@modelcontextprotocol/sdk/types.js";import{ZodError as F,z as I}from"zod";const L=I.enum([`current`,`global`,`all`]).optional().describe(`"current" (default) = this project + global memories; "global" = global memories only; "all" = every project`),R=I.enum([`current`,`global`]).optional().describe(`"current" (default) = scoped to this project; "global" = saved as a global memory`),z=I.object({content:I.string().min(1),type:o,tags:I.array(I.string()).optional(),scope:R}),B=I.object({id:I.string().min(1),content:I.string().min(1).optional(),type:o.optional(),tags:I.array(I.string()).optional()}),V=I.object({id:I.string().min(1)}),H=I.object({query:I.string().min(1),type:o.optional(),limit:I.number().int().positive().optional(),includePinned:I.boolean().optional(),scope:L}),ie=I.object({name:I.string().min(1).describe(`Migration name to execute`)}),U=I.object({id:I.string().min(1)}),W=I.object({id:I.string().min(1)}),G=I.object({scope:L,limit:I.number().int().positive().max(100).optional().describe(`Maximum number of flagged memories to return`),minSimilarity:I.number().min(0).max(1).optional().describe(`Only include memories whose review event similarity is at or above this threshold`),maxSimilarity:I.number().min(0).max(1).optional().describe(`Only include memories whose review event similarity is at or below this threshold`)}),K=I.object({scope:L}),q=I.object({ids:I.array(I.string().min(1)).min(1).max(100)}),J=I.object({ids:I.array(I.string().min(1)).min(1).max(100)}),Y=I.object({keep_id:I.string().min(1),drop_ids:I.array(I.string().min(1)).min(1).max(20),merged_content:I.string().min(1)}),ae=I.object({id:I.string().min(1).describe(`Memory ID to retrieve version history for`)}),oe=I.object({scope:I.string().min(1).describe(`Scope to retrieve synthesis history for. Use "global" for global scope, a project name, or a 16-char hex scope hash.`)});function se(){let e=k(O(),`.membank`,`config.json`);try{let t=D(e,`utf8`),n=JSON.parse(t);return{enabled:n.synthesis?.enabled===!0,...n.synthesis?.maxTokensPerRun!==void 0&&{maxTokensPerRun:n.synthesis.maxTokensPerRun},...n.synthesis?.debounceMs!==void 0&&{debounceMs:n.synthesis.debounceMs},...n.synthesis?.stalenessDays!==void 0&&{stalenessDays:n.synthesis.stalenessDays},...n.synthesis?.inFlightTimeoutMs!==void 0&&{inFlightTimeoutMs:n.synthesis.inFlightTimeoutMs}}}catch{return{enabled:!1}}}function ce(e,t,n){return{queryMemory:async e=>{let n=e.global===!0?r:e.projectHash??(await x()).hash,i=await t.query({query:e.query,projectHash:n,limit:e.limit??10,includePinned:!0});return JSON.stringify(i)},saveMemory:async t=>{let r=t.global===!0?void 0:await x(),i=await T({content:t.content,type:o.parse(t.type),tags:t.tags,projectScope:r,sourceHarness:`membank-extraction`},{repo:e,embedder:n});return JSON.stringify(i)},updateMemory:async t=>{let r=await E(t.id,{content:t.content,type:t.type===void 0?void 0:o.parse(t.type),tags:t.tags},{repo:e,embedder:n});return JSON.stringify(r)}}}function X(e,t){return{queryMemory:async e=>{let n=e.global===!0?void 0:e.projectHash??(await x()).hash,r=await t.query({query:e.query,projectHash:n,limit:e.limit??20,includePinned:!0});return JSON.stringify(r)},getMemorySummary:async()=>{let t=await x();return JSON.stringify(e.stats(t.hash))}}}function Z(n={}){let r=n.useInMemoryDb?e.openInMemory():e.open(n.dbPath),i=new t,a=g(r),o=h(r,a),s=d(r),u=new c(r,i,o,s),f=v(r),p=se(),m;return p.enabled&&(m=new l(f,p,_(X(o,u),p))),{db:r,embedding:i,repo:o,query:u,projects:a,activityLogger:s,synthRepo:f,...m!==void 0&&{synthEngine:m}}}async function Q(e){if(e===n)return r;if(e!==`all`)return(await x()).hash}function $(e,t){try{return e.parse(t)}catch(e){let t=e instanceof F?e.issues[0]?.message??e.message:String(e);throw new P(M.InvalidParams,t)}}function le(e){let t=new A({name:`membank`,version:`0.1.0`},{capabilities:{tools:{}}});return t.setRequestHandler(N,()=>({tools:[{name:`save_memory`,description:`Save a new memory. Handles deduplication automatically — near-identical memories (cosine similarity >0.92, same type and project) overwrite the existing record.`,inputSchema:{type:`object`,properties:{content:{type:`string`,description:`Memory content to save`},type:{type:`string`,enum:[...i],description:`Memory type`},tags:{type:`array`,items:{type:`string`},description:`Optional tags`},scope:{type:`string`,enum:[`current`,`global`],description:`"current" (default) = scoped to this project; "global" = saved as a global memory`}},required:[`content`,`type`]}},{name:`update_memory`,description:`Update the content, type, and/or tags of an existing memory by id. All fields except id are optional.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to update`},content:{type:`string`,description:`New content for the memory`},type:{type:`string`,enum:[...i],description:`New type for the memory (reclassification)`},tags:{type:`array`,items:{type:`string`},description:`Replacement tags (optional)`}},required:[`id`]}},{name:`delete_memory`,description:`Delete a memory by id.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to delete`}},required:[`id`]}},{name:`query_memory`,description:`Search memories by semantic similarity. Returns results ranked by confidence score. scope="current" (default) searches this project and global memories; scope="global" returns global memories only; scope="all" returns across every project.`,inputSchema:{type:`object`,properties:{query:{type:`string`,description:`Search text`},type:{type:`string`,enum:[...i],description:`Filter by memory type`},limit:{type:`number`,description:`Maximum results to return (default 10)`},includePinned:{type:`boolean`,description:`Include pinned memories in results. Pinned memories are already injected into session context, so excluded by default to avoid duplicates.`},scope:{type:`string`,enum:[`current`,`global`,`all`],description:`"current" (default) = project + global; "global" = global memories only; "all" = all projects`}},required:[`query`]}},{name:`list_migrations`,description:`List available named data migrations. Use run_migration to execute one.`,inputSchema:{type:`object`,properties:{},required:[]}},{name:`run_migration`,description:`Execute a named data migration. Use list_migrations first to see available migration names.`,inputSchema:{type:`object`,properties:{name:{type:`string`,description:`Migration name to execute`}},required:[`name`]}},{name:`pin_memory`,description:`Pin a memory by id. Pinned memories are always injected into the session context.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to pin`}},required:[`id`]}},{name:`unpin_memory`,description:`Unpin a memory by id. Removes the memory from guaranteed session injection.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to unpin`}},required:[`id`]}},{name:`get_memory_summary`,description:`Returns aggregate stats for session orientation: total memories, counts by type, pinned count, and review queue size.`,inputSchema:{type:`object`,properties:{scope:{type:`string`,enum:[`current`,`global`,`all`],description:`"current" (default) = this project; "global" = global memories only; "all" = all projects`}},required:[]}},{name:`list_flagged_memories`,description:`List memories that have unresolved dedup review events. These were flagged automatically when a near-duplicate was saved (cosine similarity 0.75–0.92).`,inputSchema:{type:`object`,properties:{scope:{type:`string`,enum:[`current`,`global`,`all`],description:`"current" (default) = this project; "global" = global memories only; "all" = all projects`},limit:{type:`number`,description:`Maximum number of flagged memories to return (max 100)`},minSimilarity:{type:`number`,description:`Only include memories whose review event similarity is at or above this threshold (0–1)`},maxSimilarity:{type:`number`,description:`Only include memories whose review event similarity is at or below this threshold (0–1)`}},required:[]}},{name:`resolve_review`,description:`Mark all review events for this memory as resolved. Use after reviewing the flagged memory and deciding it is intentionally distinct from its near-duplicates and should be kept.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to resolve review events for`}},required:[`id`]}},{name:`delete_many`,description:`Delete multiple memories in a single call. Best-effort: each id is processed independently. Returns per-id status so you can see which deletions succeeded or failed.`,inputSchema:{type:`object`,properties:{ids:{type:`array`,items:{type:`string`},description:`Memory ids to delete (max 100)`}},required:[`ids`]}},{name:`resolve_many`,description:`Resolve review events for multiple memories in a single call. Best-effort: each id is processed independently. Returns per-id status.`,inputSchema:{type:`object`,properties:{ids:{type:`array`,items:{type:`string`},description:`Memory ids to resolve review events for (max 100)`}},required:[`ids`]}},{name:`merge_memories`,description:`Merge two or more memories into one. Writes merged_content to the kept memory, unions tags and projects from all dropped memories, then deletes the dropped memories. Re-runs dedup on the result. Use when flagged memories each carry unique information that should be combined.`,inputSchema:{type:`object`,properties:{keep_id:{type:`string`,description:`Memory id to keep and update with merged content`},drop_ids:{type:`array`,items:{type:`string`},description:`Memory ids to delete after merging their content in (max 20)`},merged_content:{type:`string`,description:`The combined content to write to the kept memory`}},required:[`keep_id`,`drop_ids`,`merged_content`]}},{name:`list_memory_history`,description:`List the version history of a memory. Returns up to 10 past content snapshots in descending version order. To revert, call update_memory with the content from the desired version.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory ID to retrieve version history for`}},required:[`id`]}},{name:`list_synthesis_history`,description:`List the version history of a synthesis. Returns up to 5 past synthesis snapshots in descending version order. To revert, use the CLI: membank synthesize revert <version>.`,inputSchema:{type:`object`,properties:{scope:{type:`string`,description:`Scope to retrieve history for. Use "global" for global scope or a 16-char hex scope hash.`}},required:[`scope`]}}]})),t.setRequestHandler(j,async t=>{if(t.params.name===`save_memory`){let n=$(z,t.params.arguments),i=n.scope===`global`?void 0:await x();try{let t=await T({content:n.content,type:n.type,tags:n.tags,projectScope:i},{repo:e.repo,embedder:e.embedding,activityLogger:e.activityLogger});if(e.synthEngine!==void 0){let n=t.projects.length>0?t.projects[0]?.scopeHash??r:`global`;e.synthEngine.markDirty(n)}return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`update_memory`){let n=$(B,t.params.arguments);try{let t=await E(n.id,{content:n.content,type:n.type,tags:n.tags},{repo:e.repo,embedder:e.embedding,activityLogger:e.activityLogger});if(e.synthEngine!==void 0){let n=t.projects.length>0?t.projects[0]?.scopeHash??r:`global`;e.synthEngine.markDirty(n)}return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`delete_memory`){let n=$(V,t.params.arguments);try{let t=e.repo.findById(n.id);if(t===void 0)return{content:[{type:`text`,text:`Memory not found: ${n.id}`}],isError:!0};let i=e.synthEngine===void 0?void 0:t.projects[0]?.scopeHash??r;return await y(n.id,e.repo,e.activityLogger),e.synthEngine!==void 0&&i!==void 0&&e.synthEngine.markDirty(i),{content:[{type:`text`,text:JSON.stringify({success:!0,id:n.id})}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`query_memory`){let n=$(H,t.params.arguments),r=await Q(n.scope);try{let t=(await e.query.query({query:n.query,type:n.type,projectHash:r,limit:n.limit??10,includePinned:n.includePinned})).map(e=>({id:e.id,content:e.content,type:e.type,tags:e.tags,projects:e.projects,pinned:e.pinned,reviewEvents:e.reviewEvents,createdAt:e.createdAt,updatedAt:e.updatedAt,sourceHarness:e.sourceHarness,score:e.score}));return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`list_migrations`)return{content:[{type:`text`,text:JSON.stringify(a)}]};if(t.params.name===`run_migration`){let n=$(ie,t.params.arguments);if(n.name===`scope-to-projects`)try{let t=await w(e.projects);return t===null?{content:[{type:`text`,text:JSON.stringify({error:`No project found for current directory.`})}],isError:!0}:{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}throw new P(M.InvalidParams,`Unknown migration: "${n.name}". Available: ${a.map(e=>e.name).join(`, `)}`)}if(t.params.name===`pin_memory`||t.params.name===`unpin_memory`){let n=$(U,t.params.arguments),r=t.params.name===`pin_memory`;try{let t=e.repo.setPin(n.id,r);if(r){let{hash:n}=await x(),r=e.repo.getPinnedCharCount(n);if(r>s&&!b()){let e={...t,pinBudgetWarning:`Pinned memories now use ${r} characters (threshold: ${s}). Consider unpinning older memories or enabling synthesis to compress them.`};return{content:[{type:`text`,text:JSON.stringify(e)}]}}}return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`get_memory_summary`){let n=$(K,t.params.arguments);try{let t=await Q(n.scope),r=e.repo.stats(t),i=e.repo.reviewQueueStats(t),a=u(e.repo.listReviewEdges(t)),o={...r,reviewQueue:{...i,clusters:a.length}};return{content:[{type:`text`,text:JSON.stringify(o)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`list_flagged_memories`){let n=$(G,t.params.arguments);try{let t=await Q(n.scope),r=e.repo.listFlagged({...t!==void 0&&{projectHash:t},...n.limit!==void 0&&{limit:n.limit},...n.minSimilarity!==void 0&&{minSimilarity:n.minSimilarity},...n.maxSimilarity!==void 0&&{maxSimilarity:n.maxSimilarity}}),i=r.flatMap(e=>e.reviewEvents.map(e=>e.conflictingMemoryId).filter(e=>e!==null)),a=[...new Set(i)],o=e.repo.findManyById(a),s=new Map(o.map(e=>[e.id,e])),c=u(e.repo.listReviewEdges(t)),l=new Map;for(let e of c)for(let t of e.memoryIds)l.set(t,e.clusterId);let d=new Set(r.map(e=>e.id)),f=c.filter(e=>e.memoryIds.some(e=>d.has(e))),p={memories:r.map(e=>({...e,clusterId:l.get(e.id)??null,reviewEvents:e.reviewEvents.map(e=>({...e,conflictingMemory:e.conflictingMemoryId===null?null:s.get(e.conflictingMemoryId)??null}))})),clusters:f};return{content:[{type:`text`,text:JSON.stringify(p)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`resolve_review`){let n=$(W,t.params.arguments);try{return e.repo.findById(n.id)===void 0?{content:[{type:`text`,text:`Memory not found: ${n.id}`}],isError:!0}:(e.repo.resolveReviewEvents(n.id),{content:[{type:`text`,text:JSON.stringify({success:!0,id:n.id})}]})}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`delete_many`){let n=$(q,t.params.arguments);try{let t=e.synthEngine===void 0?[]:[...new Set(n.ids.map(t=>e.repo.findById(t)).filter(e=>e!==void 0).map(e=>e.projects[0]?.scopeHash??r))],i=await ee(n.ids,e.repo,e.activityLogger);if(e.synthEngine!==void 0)for(let n of t)e.synthEngine.markDirty(n);return{content:[{type:`text`,text:JSON.stringify(i)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`resolve_many`){let n=$(J,t.params.arguments);try{let t=S(n.ids,e.repo);return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`merge_memories`){let n=$(Y,t.params.arguments);try{let t=await te({keepId:n.keep_id,dropIds:n.drop_ids,mergedContent:n.merged_content},{repo:e.repo,embedder:e.embedding,activityLogger:e.activityLogger});if(e.synthEngine!==void 0){let n=t.kept.projects[0]?.scopeHash??r;e.synthEngine.markDirty(n)}return{content:[{type:`text`,text:JSON.stringify(t)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}if(t.params.name===`list_memory_history`){let n=$(ae,t.params.arguments),r=e.repo.listVersions(n.id);return{content:[{type:`text`,text:JSON.stringify(r)}]}}if(t.params.name===`list_synthesis_history`){let i=$(oe,t.params.arguments),a=i.scope===n?r:i.scope,o=e.synthRepo.listVersions(a);return{content:[{type:`text`,text:JSON.stringify(o)}]}}throw Error(`Unknown tool: ${t.params.name}`)}),t}async function ue(){let e;try{e=Z()}catch(e){let t=e instanceof Error?e.message:String(e);process.stderr.write(`membank: failed to initialise core: ${t}\n`),process.exit(1)}if(e.synthEngine!==void 0)try{await e.synthEngine.init()}catch(e){let t=e instanceof Error?e.message:String(e);process.stderr.write(`membank: synthesis engine init failed: ${t}\n`)}let t=async()=>{e.synthEngine!==void 0&&await e.synthEngine.shutdown(),process.exit(0)};process.on(`SIGTERM`,()=>{t()}),process.on(`SIGINT`,()=>{t()});let n=le(e),r=new re;await n.connect(r)}async function de(n){if(!b())throw Error(`Synthesis is not enabled. Run: membank config set synthesis.enabled true`);let i=e.open(),a=new t,o=g(i),s=h(i,o),l=new c(i,a,s),u=v(i),d=_(X(s,l),{enabled:!0}),f=n;if(n===`global`)f=r;else if(!/^[0-9a-f]{16}$/.test(n)){let e=o.getByName(n);e!==void 0&&(f=e.scopeHash)}try{return await ne(f,{synthRepo:u,agentRunner:d})}finally{i.close()}}async function fe(n){let r=e.open();try{let e=new t,i=h(r,g(r)),a=new c(r,e,i),o=m(r),s=p(ce(i,a,e)),l=f(),u=n.projectHash??(await x()).hash;return await C({sessionId:n.sessionId,transcriptPath:n.transcriptPath,projectHash:u},{repo:o,transcripts:l,agent:s,config:{}})}finally{r.close()}}export{fe as runExtraction,de as runSynthesis,ue as startServer};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@membank/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
25
25
|
"commander": "^14.0.3",
|
|
26
26
|
"zod": "^4.4.3",
|
|
27
|
-
"@membank/core": "0.
|
|
27
|
+
"@membank/core": "0.14.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/node": "^25.6.0",
|