@membank/mcp 0.14.2 → 0.15.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,createActivityLogger as m,createMemoryRepository as h,createProjectRepository as g,createSynthesisAgentRunner as _,createSynthesisRepository as v,deleteMemory as y,isSynthesisEnabled as b,listMemoryTypes as x,resolveProject 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.object({content:P.string().min(1),type:u,tags:P.array(P.string()).optional(),global:P.boolean().optional()}),I=P.object({id:P.string().min(1),content:P.string().min(1).optional(),type:u.optional(),tags:P.array(P.string()).optional()}),L=P.object({id:P.string().min(1)}),R=P.object({query:P.string().min(1),type:u.optional(),limit:P.number().int().positive().optional(),includePinned:P.boolean().optional(),global:P.boolean().optional()}),z=P.discriminatedUnion(`mode`,[P.object({mode:P.literal(`list`)}),P.object({mode:P.literal(`run`),name:P.string().min(1)})]),B=P.object({id:P.string().min(1)}),V=P.object({id:P.string().min(1)});function H(){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 U(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 W(e={}){let t=e.useInMemoryDb?a.openInMemory():a.open(e.dbPath),n=new o,r=g(t),i=h(t,r),s=m(t),c=new f(t,n,i,s),l=H(),u;return l.enabled&&(u=new p(v(t),l,_(U(i,c),l))),{db:t,embedding:n,repo:i,query:c,projects:r,activityLogger:s,synthEngine:u}}function G(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 K(e){let t=new O({name:`membank`,version:`0.1.0`},{capabilities:{tools:{}}});return t.setRequestHandler(j,()=>({tools:[{name:`list_memory_types`,description:`Returns the ordered list of memory type values supported by membank.`,inputSchema:{type:`object`,properties:{},required:[]}},{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`},global:{type:`boolean`,description:`Save as a global memory, not tied to any project`}},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.`,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.`},global:{type:`boolean`,description:`Query global memories only. When omitted or false, queries the current project scope.`}},required:[`query`]}},{name:`migrate`,description:`List or run named data migrations. Use mode "list" to see available migrations; mode "run" with a migration name to execute one.`,inputSchema:{type:`object`,properties:{mode:{type:`string`,enum:[`list`,`run`],description:`Mode: "list" to see available migrations, "run" to execute one`},name:{type:`string`,description:`Migration name (required when mode is "run")`}},required:[`mode`]}},{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:{},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:{},required:[]}},{name:`resolve_review`,description:`Dismiss all unresolved review events for a memory. Use after reviewing the memory and deciding it should be kept as-is.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to resolve review events for`}},required:[`id`]}}]})),t.setRequestHandler(k,async t=>{if(t.params.name===`list_memory_types`)try{return{content:[{type:`text`,text:JSON.stringify(x())}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}if(t.params.name===`save_memory`){let n=G(F,t.params.arguments),r=n.global===!0?void 0:await S();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=G(I,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=G(L,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 y(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=G(R,t.params.arguments),r=n.global===!0?void 0:(await S()).hash;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===`migrate`){let n=G(z,t.params.arguments);if(n.mode===`list`)return{content:[{type:`text`,text:JSON.stringify(l)}]};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=G(B,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>d&&!b()){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`)try{let{hash:t}=await S();return{content:[{type:`text`,text:JSON.stringify(e.repo.stats(t))}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}if(t.params.name===`list_flagged_memories`)try{let{hash:t}=await S(),n=e.repo.listFlagged(t);return{content:[{type:`text`,text:JSON.stringify(n)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}if(t.params.name===`resolve_review`){let n=G(V,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}}}throw Error(`Unknown tool: ${t.params.name}`)}),t}async function q(){let e;try{e=W()}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=K(e),r=new E;await n.connect(r)}const{version:J}=JSON.parse(e(n(t(r(import.meta.url)),`../package.json`),`utf8`)),Y=new i;Y.name(`membank-mcp`).description(`Membank MCP stdio server — for harness integration`).version(J).action(q),await Y.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_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{};
|
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,createActivityLogger as l,createClaudeCodeTranscriptReader as u,createExtractionAgentRunner as d,createExtractionRunRepository as f,createMemoryRepository as p,createProjectRepository as m,createSynthesisAgentRunner as h,createSynthesisRepository as g,deleteMemory as _,isSynthesisEnabled as v,listMemoryTypes as y,resolveProject as b,runExtraction as x,runScopeToProjectsMigration as S,runSynthesis as C,saveMemory as w,updateMemory as T}from"@membank/core";import{StdioServerTransport as E}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.object({content:I.string().min(1),type:a,tags:I.array(I.string()).optional(),global:I.boolean().optional()}),R=I.object({id:I.string().min(1),content:I.string().min(1).optional(),type:a.optional(),tags:I.array(I.string()).optional()}),z=I.object({id:I.string().min(1)}),B=I.object({query:I.string().min(1),type:a.optional(),limit:I.number().int().positive().optional(),includePinned:I.boolean().optional(),global:I.boolean().optional()}),V=I.discriminatedUnion(`mode`,[I.object({mode:I.literal(`list`)}),I.object({mode:I.literal(`run`),name:I.string().min(1)})]),H=I.object({id:I.string().min(1)}),U=I.object({id:I.string().min(1)});function W(){let e=k(O(),`.membank`,`config.json`);try{let t=D(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 G(e,t,n){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??10,includePinned:!0});return JSON.stringify(r)},saveMemory:async t=>{let r=t.global===!0?void 0:await b(),i=await w({content:t.content,type:a.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 T(t.id,{content:t.content,type:t.type===void 0?void 0:a.parse(t.type),tags:t.tags},{repo:e,embedder:n});return JSON.stringify(r)}}}function K(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 q(n={}){let r=n.useInMemoryDb?e.openInMemory():e.open(n.dbPath),i=new t,a=m(r),o=p(r,a),u=l(r),d=new s(r,i,o,u),f=W(),_;return f.enabled&&(_=new c(g(r),f,h(K(o,d),f))),{db:r,embedding:i,repo:o,query:d,projects:a,activityLogger:u,synthEngine:_}}function J(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 Y(e){let t=new A({name:`membank`,version:`0.1.0`},{capabilities:{tools:{}}});return t.setRequestHandler(N,()=>({tools:[{name:`list_memory_types`,description:`Returns the ordered list of memory type values supported by membank.`,inputSchema:{type:`object`,properties:{},required:[]}},{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`},global:{type:`boolean`,description:`Save as a global memory, not tied to any project`}},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.`,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.`},global:{type:`boolean`,description:`Query global memories only. When omitted or false, queries the current project scope.`}},required:[`query`]}},{name:`migrate`,description:`List or run named data migrations. Use mode "list" to see available migrations; mode "run" with a migration name to execute one.`,inputSchema:{type:`object`,properties:{mode:{type:`string`,enum:[`list`,`run`],description:`Mode: "list" to see available migrations, "run" to execute one`},name:{type:`string`,description:`Migration name (required when mode is "run")`}},required:[`mode`]}},{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:{},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:{},required:[]}},{name:`resolve_review`,description:`Dismiss all unresolved review events for a memory. Use after reviewing the memory and deciding it should be kept as-is.`,inputSchema:{type:`object`,properties:{id:{type:`string`,description:`Memory id to resolve review events for`}},required:[`id`]}}]})),t.setRequestHandler(j,async t=>{if(t.params.name===`list_memory_types`)try{return{content:[{type:`text`,text:JSON.stringify(y())}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}if(t.params.name===`save_memory`){let r=J(L,t.params.arguments),i=r.global===!0?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=J(R,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=J(z,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 _(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=J(B,t.params.arguments),r=n.global===!0?void 0:(await b()).hash;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===`migrate`){let n=J(V,t.params.arguments);if(n.mode===`list`)return{content:[{type:`text`,text:JSON.stringify(i)}]};if(n.name===`scope-to-projects`)try{let t=await S(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: ${i.map(e=>e.name).join(`, `)}`)}if(t.params.name===`pin_memory`||t.params.name===`unpin_memory`){let n=J(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&&!v()){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`)try{let{hash:t}=await b();return{content:[{type:`text`,text:JSON.stringify(e.repo.stats(t))}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}if(t.params.name===`list_flagged_memories`)try{let{hash:t}=await b(),n=e.repo.listFlagged(t);return{content:[{type:`text`,text:JSON.stringify(n)}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}if(t.params.name===`resolve_review`){let n=J(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}}}throw Error(`Unknown tool: ${t.params.name}`)}),t}async function X(){let e;try{e=q()}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=Y(e),r=new E;await n.connect(r)}async function Z(r){if(!v())throw Error(`Synthesis is not enabled. Run: membank config set synthesis.enabled true`);let i=e.open(),a=new t,o=m(i),c=p(i,o),l=new s(i,a,c),u=g(i),d=h(K(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 C(f,{synthRepo:u,agentRunner:d})}finally{i.close()}}async function Q(n){let r=e.open();try{let e=new t,i=p(r,m(r)),a=new s(r,e,i),o=f(r),c=d(G(i,a,e)),l=u(),h=n.projectHash??(await b()).hash;return await x({sessionId:n.sessionId,transcriptPath:n.transcriptPath,projectHash:h},{repo:o,transcripts:l,agent:c,config:{}})}finally{r.close()}}export{Q as runExtraction,Z as runSynthesis,X as startServer};
|
|
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};
|
|
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.15.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.13.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/node": "^25.6.0",
|