@melandlabs/memory-store 0.1.2 → 0.1.5
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/http.d.ts +13 -0
- package/dist/http.js +52 -6
- package/dist/http.js.map +1 -1
- package/dist/mcp.js +55 -6
- package/dist/mcp.js.map +1 -1
- package/dist/server/cli-http.js +383 -18
- package/dist/server/cli-http.js.map +1 -1
- package/dist/server/cli-mcp.js +395 -7
- package/dist/server/cli-mcp.js.map +1 -1
- package/dist/server/cli-shared.d.ts +39 -0
- package/dist/server/cli-shared.js +373 -0
- package/dist/server/cli-shared.js.map +1 -0
- package/package.json +12 -7
package/dist/server/cli-mcp.js
CHANGED
|
@@ -465,6 +465,34 @@ function createUnifiedSearch(deps = {}) {
|
|
|
465
465
|
// src/mcp.ts
|
|
466
466
|
var DEFAULT_NAME = "@melandlabs/memory-store";
|
|
467
467
|
var DEFAULT_VERSION = "0.1.0";
|
|
468
|
+
async function embedMissingMessages(messages, deps) {
|
|
469
|
+
if (typeof deps.embedQuery !== "function") {
|
|
470
|
+
return messages;
|
|
471
|
+
}
|
|
472
|
+
const out = [];
|
|
473
|
+
for (const message of messages) {
|
|
474
|
+
if (Array.isArray(message.embedding) && message.embedding.length > 0) {
|
|
475
|
+
out.push(message);
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
if (typeof message.content !== "string" || message.content.length === 0) {
|
|
479
|
+
out.push(message);
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
const vector = await deps.embedQuery({
|
|
483
|
+
userId: message.userId,
|
|
484
|
+
query: message.content
|
|
485
|
+
});
|
|
486
|
+
out.push({
|
|
487
|
+
...message,
|
|
488
|
+
embedding: vector,
|
|
489
|
+
embeddingModel: message.embeddingModel ?? "server",
|
|
490
|
+
embeddingDimensions: vector.length,
|
|
491
|
+
embeddingUpdatedAt: Date.now()
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
return out;
|
|
495
|
+
}
|
|
468
496
|
async function startMcpServer(options = {}) {
|
|
469
497
|
const server = new McpServer({
|
|
470
498
|
name: options.name ?? DEFAULT_NAME,
|
|
@@ -490,6 +518,9 @@ async function startMcpServer(options = {}) {
|
|
|
490
518
|
};
|
|
491
519
|
const writeSchema = {
|
|
492
520
|
userId: z.string(),
|
|
521
|
+
embedOnInsert: z.boolean().optional().describe(
|
|
522
|
+
"When true AND the host wired `unified.embedQuery`, fill in any missing `embedding` server-side via that embedder. Required when the active memory backend is sqlite-vec and the caller doesn't pre-embed."
|
|
523
|
+
),
|
|
493
524
|
message: z.object({
|
|
494
525
|
id: z.string().optional(),
|
|
495
526
|
messageId: z.string().optional(),
|
|
@@ -500,6 +531,9 @@ async function startMcpServer(options = {}) {
|
|
|
500
531
|
channel: z.string().optional(),
|
|
501
532
|
person: z.string().optional(),
|
|
502
533
|
timestamp: z.number().optional(),
|
|
534
|
+
embedding: z.array(z.number()).optional(),
|
|
535
|
+
embeddingModel: z.string().optional(),
|
|
536
|
+
embeddingDimensions: z.number().optional(),
|
|
503
537
|
metadata: z.record(z.string(), z.unknown()).optional()
|
|
504
538
|
}).passthrough()
|
|
505
539
|
};
|
|
@@ -532,17 +566,32 @@ async function startMcpServer(options = {}) {
|
|
|
532
566
|
async (args) => {
|
|
533
567
|
const a = args;
|
|
534
568
|
const manager2 = await rawStore.getManager();
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
569
|
+
const incoming = [a.message].map((m) => ({ ...m, userId: m.userId ?? a.userId }));
|
|
570
|
+
const messages = a.embedOnInsert === true ? await embedMissingMessages(incoming, options.unified ?? {}) : incoming;
|
|
571
|
+
let result;
|
|
572
|
+
if (typeof manager2.upsertRawMessages === "function") {
|
|
573
|
+
result = await manager2.upsertRawMessages({
|
|
574
|
+
userId: a.userId,
|
|
575
|
+
messages
|
|
576
|
+
});
|
|
577
|
+
} else if (typeof manager2.storeMessages === "function") {
|
|
578
|
+
const ids = await manager2.storeMessages(messages);
|
|
579
|
+
result = { inserted: ids.length, ids };
|
|
580
|
+
} else {
|
|
581
|
+
throw new Error("active raw-message manager exposes neither upsertRawMessages nor storeMessages");
|
|
582
|
+
}
|
|
539
583
|
try {
|
|
540
|
-
await upsertRawMessagesToChroma(
|
|
584
|
+
await upsertRawMessagesToChroma(messages);
|
|
541
585
|
} catch (error) {
|
|
542
586
|
console.warn("[memory-store/mcp] chroma upsert failed:", error);
|
|
543
587
|
}
|
|
544
588
|
return {
|
|
545
|
-
content: [
|
|
589
|
+
content: [
|
|
590
|
+
{
|
|
591
|
+
type: "text",
|
|
592
|
+
text: JSON.stringify({ ok: true, count: messages.length, result })
|
|
593
|
+
}
|
|
594
|
+
]
|
|
546
595
|
};
|
|
547
596
|
}
|
|
548
597
|
);
|
|
@@ -572,9 +621,348 @@ async function startMcpServer(options = {}) {
|
|
|
572
621
|
return server;
|
|
573
622
|
}
|
|
574
623
|
|
|
624
|
+
// src/storage/sqlite-vector-index.ts
|
|
625
|
+
function getMemoryVectorStoreBackend() {
|
|
626
|
+
const backend = (process.env.INSIGHT_VECTOR_STORE_BACKEND || process.env.MEMORY_VECTOR_STORE_BACKEND || process.env.VECTOR_STORE_BACKEND || "sqlite-vec").toLowerCase();
|
|
627
|
+
return backend === "chroma" ? "chroma" : "sqlite-vec";
|
|
628
|
+
}
|
|
629
|
+
function isInsightSQLiteVecEnabled(_env) {
|
|
630
|
+
return getMemoryVectorStoreBackend() === "sqlite-vec";
|
|
631
|
+
}
|
|
632
|
+
async function getInsightSQLiteVecStore(_env) {
|
|
633
|
+
const dbPath = resolveSQLiteRawMessageDbPath();
|
|
634
|
+
const { getSQLiteVecStore } = await import('@melandlabs/rag/sqlite-vec-store');
|
|
635
|
+
return await getSQLiteVecStore(dbPath, void 0, {
|
|
636
|
+
collectionName: process.env.SQLITE_VEC_INSIGHTS_COLLECTION || "opencontext_insights"
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
async function searchInsightsWithSQLiteVec(input, env) {
|
|
640
|
+
const store = await getInsightSQLiteVecStore();
|
|
641
|
+
const overfetchLimit = Math.max(input.limit * 8, input.limit);
|
|
642
|
+
const results = await store.similaritySearchWithOptions(input.queryEmbedding, {
|
|
643
|
+
limit: overfetchLimit,
|
|
644
|
+
filter: { userId: input.userId }
|
|
645
|
+
});
|
|
646
|
+
return results.filter((result) => result.score >= input.threshold).filter((result) => {
|
|
647
|
+
const metadata = result.metadata ?? {};
|
|
648
|
+
if (input.botIds?.length && !input.botIds.includes(String(metadata.botId ?? ""))) {
|
|
649
|
+
return false;
|
|
650
|
+
}
|
|
651
|
+
return input.includeArchived || metadata.archived !== true;
|
|
652
|
+
}).slice(0, input.limit);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// src/server/cli-shared.ts
|
|
656
|
+
async function loadAiRag() {
|
|
657
|
+
try {
|
|
658
|
+
const [local, chroma] = await Promise.all([
|
|
659
|
+
import('@melandlabs/ai-rag/local-transformers-embedding-provider'),
|
|
660
|
+
import('@melandlabs/ai-rag/chroma-store')
|
|
661
|
+
]);
|
|
662
|
+
return {
|
|
663
|
+
LocalTransformersEmbeddingProvider: local.LocalTransformersEmbeddingProvider,
|
|
664
|
+
ChromaVectorStore: chroma.ChromaVectorStore
|
|
665
|
+
};
|
|
666
|
+
} catch (error) {
|
|
667
|
+
throw new Error(
|
|
668
|
+
`--embedding-provider local / --*-backend=chroma require @melandlabs/ai-rag to be installed. Run \`pnpm add @melandlabs/ai-rag\` and try again. (${error.message})`
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
function parseUnifiedArgs(argv) {
|
|
673
|
+
const env = process.env;
|
|
674
|
+
const args = {
|
|
675
|
+
embeddingProvider: env.EMBEDDING_PROVIDER ?? "none",
|
|
676
|
+
embeddingModel: env.EMBEDDING_MODEL,
|
|
677
|
+
chromaUrl: env.CHROMA_URL,
|
|
678
|
+
memoryBackend: env.MEMORY_BACKEND ?? "none",
|
|
679
|
+
insightsBackend: env.INSIGHTS_BACKEND ?? "none",
|
|
680
|
+
insightsCollection: env.INSIGHTS_COLLECTION ?? "opencontext_insights",
|
|
681
|
+
knowledgeBackend: env.KNOWLEDGE_BACKEND ?? "none",
|
|
682
|
+
knowledgeCollection: env.KNOWLEDGE_COLLECTION ?? "opencontext_knowledge"
|
|
683
|
+
};
|
|
684
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
685
|
+
const arg = argv[i];
|
|
686
|
+
const next = argv[i + 1];
|
|
687
|
+
const takeValue = () => {
|
|
688
|
+
if (next === void 0) throw new Error(`[memory-store/http] ${arg} requires a value`);
|
|
689
|
+
i += 1;
|
|
690
|
+
return next;
|
|
691
|
+
};
|
|
692
|
+
switch (arg) {
|
|
693
|
+
case "--embedding-provider":
|
|
694
|
+
args.embeddingProvider = takeValue();
|
|
695
|
+
break;
|
|
696
|
+
case "--embedding-model":
|
|
697
|
+
args.embeddingModel = takeValue();
|
|
698
|
+
break;
|
|
699
|
+
case "--chroma-url":
|
|
700
|
+
args.chromaUrl = takeValue();
|
|
701
|
+
break;
|
|
702
|
+
case "--memory-backend":
|
|
703
|
+
args.memoryBackend = takeValue();
|
|
704
|
+
break;
|
|
705
|
+
case "--insights-backend":
|
|
706
|
+
args.insightsBackend = takeValue();
|
|
707
|
+
break;
|
|
708
|
+
case "--insights-collection":
|
|
709
|
+
args.insightsCollection = takeValue();
|
|
710
|
+
break;
|
|
711
|
+
case "--knowledge-backend":
|
|
712
|
+
args.knowledgeBackend = takeValue();
|
|
713
|
+
break;
|
|
714
|
+
case "--knowledge-collection":
|
|
715
|
+
args.knowledgeCollection = takeValue();
|
|
716
|
+
break;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
for (const [name, value] of [
|
|
720
|
+
["--embedding-provider", args.embeddingProvider],
|
|
721
|
+
["--memory-backend", args.memoryBackend],
|
|
722
|
+
["--insights-backend", args.insightsBackend],
|
|
723
|
+
["--knowledge-backend", args.knowledgeBackend]
|
|
724
|
+
]) {
|
|
725
|
+
const allowed = ["local", "openrouter", "none", "chroma", "sqlite-vec"];
|
|
726
|
+
if (!allowed.includes(value)) {
|
|
727
|
+
throw new Error(`[memory-store/cli] ${name} must be one of: ${allowed.join(", ")} (got "${value}")`);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
return args;
|
|
731
|
+
}
|
|
732
|
+
async function buildUnified(args) {
|
|
733
|
+
const unified = {};
|
|
734
|
+
const log = (msg) => console.warn(`[memory-store/cli] ${msg}`);
|
|
735
|
+
const wantsAiRag = args.embeddingProvider === "local" || args.memoryBackend === "chroma" || args.insightsBackend === "chroma" || args.knowledgeBackend === "chroma";
|
|
736
|
+
const aiRag = wantsAiRag ? await loadAiRag() : null;
|
|
737
|
+
if (args.embeddingProvider === "local") {
|
|
738
|
+
const provider = new aiRag.LocalTransformersEmbeddingProvider({ modelName: args.embeddingModel });
|
|
739
|
+
unified.embedQuery = async ({ query }) => provider.embedQuery(query);
|
|
740
|
+
log(
|
|
741
|
+
`embedQuery wired via LocalTransformersEmbeddingProvider (model=${args.embeddingModel ?? "Xenova/all-MiniLM-L6-v2"})`
|
|
742
|
+
);
|
|
743
|
+
} else if (args.embeddingProvider === "openrouter") {
|
|
744
|
+
const apiKey = process.env.OPENROUTER_API_KEY;
|
|
745
|
+
if (!apiKey) {
|
|
746
|
+
throw new Error("--embedding-provider openrouter requires OPENROUTER_API_KEY in the environment");
|
|
747
|
+
}
|
|
748
|
+
const model = args.embeddingModel ?? "text-embedding-3-small";
|
|
749
|
+
const baseURL = "https://openrouter.ai/api/v1";
|
|
750
|
+
unified.embedQuery = async ({ query }) => {
|
|
751
|
+
const res = await fetch(`${baseURL}/embeddings`, {
|
|
752
|
+
method: "POST",
|
|
753
|
+
headers: {
|
|
754
|
+
"content-type": "application/json",
|
|
755
|
+
authorization: `Bearer ${apiKey}`,
|
|
756
|
+
"HTTP-Referer": process.env.NEXT_PUBLIC_APP_URL ?? "https://opencontext.ai",
|
|
757
|
+
"X-Title": "opencontext AI"
|
|
758
|
+
},
|
|
759
|
+
body: JSON.stringify({ model, input: query })
|
|
760
|
+
});
|
|
761
|
+
if (!res.ok) {
|
|
762
|
+
throw new Error(`openrouter embeddings ${res.status}: ${await res.text()}`);
|
|
763
|
+
}
|
|
764
|
+
const body = await res.json();
|
|
765
|
+
const embedding = body.data?.[0]?.embedding;
|
|
766
|
+
if (!embedding) throw new Error("openrouter embeddings response missing data[0].embedding");
|
|
767
|
+
return embedding;
|
|
768
|
+
};
|
|
769
|
+
log(`embedQuery wired via openrouter (model=${model})`);
|
|
770
|
+
}
|
|
771
|
+
if (args.memoryBackend === "sqlite-vec") {
|
|
772
|
+
const store = createRawMessageStore({ env: void 0 });
|
|
773
|
+
const manager2 = await store.getManager();
|
|
774
|
+
if (typeof manager2.searchMessagesSemantically !== "function") {
|
|
775
|
+
throw new Error(
|
|
776
|
+
"the active raw-message manager does not implement searchMessagesSemantically; sqlite-vec ANN for the memory source is unavailable in this build"
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
unified.searchRawMessagesAnn = async ({ userId, queryEmbedding, limit, threshold, botId }) => {
|
|
780
|
+
const rows = await manager2.searchMessagesSemantically({
|
|
781
|
+
userId,
|
|
782
|
+
queryEmbedding,
|
|
783
|
+
limit,
|
|
784
|
+
threshold,
|
|
785
|
+
botId
|
|
786
|
+
});
|
|
787
|
+
return rows.map((r) => ({
|
|
788
|
+
id: r.id,
|
|
789
|
+
content: r.content,
|
|
790
|
+
similarity: r.similarity,
|
|
791
|
+
metadata: r.metadata ?? {}
|
|
792
|
+
}));
|
|
793
|
+
};
|
|
794
|
+
log("memory backend wired via sqlite-vec (uses the manager's searchMessagesSemantically)");
|
|
795
|
+
} else if (args.memoryBackend === "chroma") {
|
|
796
|
+
if (!args.chromaUrl)
|
|
797
|
+
throw new Error("--memory-backend=chroma requires --chroma-url <url> (or CHROMA_URL env)");
|
|
798
|
+
const store = new aiRag.ChromaVectorStore({
|
|
799
|
+
url: args.chromaUrl,
|
|
800
|
+
collectionName: "opencontext_raw_messages"
|
|
801
|
+
});
|
|
802
|
+
unified.searchRawMessagesAnn = async ({ queryEmbedding, limit, threshold }) => {
|
|
803
|
+
const results = await store.similaritySearchWithOptions(queryEmbedding, { limit: limit + 1 });
|
|
804
|
+
return results.filter((r) => r.score >= threshold).map((r) => ({ id: r.id, content: r.content, similarity: r.score, metadata: r.metadata ?? {} }));
|
|
805
|
+
};
|
|
806
|
+
log("memory backend wired via chroma (collection=opencontext_raw_messages)");
|
|
807
|
+
}
|
|
808
|
+
if (args.insightsBackend === "sqlite-vec" && isInsightSQLiteVecEnabled()) {
|
|
809
|
+
if (!unified.embedQuery) {
|
|
810
|
+
throw new Error("--insights-backend=sqlite-vec requires --embedding-provider local|openrouter");
|
|
811
|
+
}
|
|
812
|
+
const embedQuery = unified.embedQuery;
|
|
813
|
+
unified.searchInsights = async ({ userId, query, limit, threshold, botIds, includeArchived }) => {
|
|
814
|
+
const emb = await embedQuery({ userId, query });
|
|
815
|
+
const rows = await searchInsightsWithSQLiteVec({
|
|
816
|
+
userId,
|
|
817
|
+
queryEmbedding: emb,
|
|
818
|
+
limit,
|
|
819
|
+
threshold,
|
|
820
|
+
botIds,
|
|
821
|
+
includeArchived
|
|
822
|
+
});
|
|
823
|
+
return rows.map((r) => ({
|
|
824
|
+
id: r.id,
|
|
825
|
+
content: r.content,
|
|
826
|
+
similarity: r.score,
|
|
827
|
+
metadata: r.metadata ?? {}
|
|
828
|
+
}));
|
|
829
|
+
};
|
|
830
|
+
log("insights backend wired via sqlite-vec");
|
|
831
|
+
} else if (args.insightsBackend === "chroma") {
|
|
832
|
+
if (!args.chromaUrl)
|
|
833
|
+
throw new Error("--insights-backend=chroma requires --chroma-url <url> (or CHROMA_URL env)");
|
|
834
|
+
if (!unified.embedQuery)
|
|
835
|
+
throw new Error("--insights-backend=chroma requires --embedding-provider local|openrouter");
|
|
836
|
+
const store = new aiRag.ChromaVectorStore({
|
|
837
|
+
url: args.chromaUrl,
|
|
838
|
+
collectionName: args.insightsCollection
|
|
839
|
+
});
|
|
840
|
+
const embedQuery = unified.embedQuery;
|
|
841
|
+
unified.searchInsights = async ({ query, limit, threshold }) => {
|
|
842
|
+
const emb = await embedQuery({ userId: "", query });
|
|
843
|
+
const results = await store.similaritySearchWithOptions(emb, { limit: limit + 1 });
|
|
844
|
+
return results.filter((r) => r.score >= threshold).map((r) => ({ id: r.id, content: r.content, similarity: r.score, metadata: r.metadata ?? {} }));
|
|
845
|
+
};
|
|
846
|
+
log(`insights backend wired via chroma (collection=${args.insightsCollection})`);
|
|
847
|
+
}
|
|
848
|
+
if (args.knowledgeBackend === "chroma") {
|
|
849
|
+
if (!args.chromaUrl)
|
|
850
|
+
throw new Error("--knowledge-backend=chroma requires --chroma-url <url> (or CHROMA_URL env)");
|
|
851
|
+
if (!unified.embedQuery)
|
|
852
|
+
throw new Error("--knowledge-backend=chroma requires --embedding-provider local|openrouter");
|
|
853
|
+
const store = new aiRag.ChromaVectorStore({
|
|
854
|
+
url: args.chromaUrl,
|
|
855
|
+
collectionName: args.knowledgeCollection
|
|
856
|
+
});
|
|
857
|
+
const embedQuery = unified.embedQuery;
|
|
858
|
+
unified.searchKnowledge = async ({ query, options }) => {
|
|
859
|
+
const emb = await embedQuery({ userId: "", query });
|
|
860
|
+
const results = await store.similaritySearchWithOptions(emb, { limit: options.limit + 1 });
|
|
861
|
+
return results.filter((r) => r.score >= options.threshold).map((r, i) => ({
|
|
862
|
+
chunkId: r.id,
|
|
863
|
+
documentId: r.documentId || String(r.metadata?.documentId ?? r.id),
|
|
864
|
+
documentName: String(r.metadata?.documentName ?? ""),
|
|
865
|
+
content: r.content,
|
|
866
|
+
similarity: r.score,
|
|
867
|
+
chunkIndex: i
|
|
868
|
+
}));
|
|
869
|
+
};
|
|
870
|
+
log(`knowledge backend wired via chroma (collection=${args.knowledgeCollection})`);
|
|
871
|
+
}
|
|
872
|
+
return unified;
|
|
873
|
+
}
|
|
874
|
+
function printUnifiedHelp() {
|
|
875
|
+
console.log(`Embedding (wires unified.embedQuery):
|
|
876
|
+
--embedding-provider <name> local | openrouter | none
|
|
877
|
+
(env: EMBEDDING_PROVIDER, default: none)
|
|
878
|
+
--embedding-model <name> Model name
|
|
879
|
+
(env: EMBEDDING_MODEL; local \u2192 Xenova/all-MiniLM-L6-v2,
|
|
880
|
+
openrouter \u2192 text-embedding-3-small)
|
|
881
|
+
Note: "local" dynamically imports @melandlabs/ai-rag (a peer install).
|
|
882
|
+
|
|
883
|
+
Cross-source search (wires unified.searchKnowledge / searchInsights / searchRawMessagesAnn):
|
|
884
|
+
--chroma-url <url> Chroma server URL
|
|
885
|
+
(env: CHROMA_URL; required when any *-backend=chroma)
|
|
886
|
+
--memory-backend <name> sqlite-vec | chroma | none
|
|
887
|
+
(env: MEMORY_BACKEND, default: none)
|
|
888
|
+
--insights-backend <name> sqlite-vec | chroma | none
|
|
889
|
+
(env: INSIGHTS_BACKEND, default: none)
|
|
890
|
+
--insights-collection <name> Chroma collection (default: opencontext_insights)
|
|
891
|
+
--knowledge-backend <name> chroma | none
|
|
892
|
+
(env: KNOWLEDGE_BACKEND, default: none)
|
|
893
|
+
--knowledge-collection <name> Chroma collection (default: opencontext_knowledge)
|
|
894
|
+
Note: the chroma backends dynamically import @melandlabs/ai-rag.`);
|
|
895
|
+
}
|
|
896
|
+
|
|
575
897
|
// src/server/cli-mcp.ts
|
|
898
|
+
function parseArgs(argv) {
|
|
899
|
+
const env = process.env;
|
|
900
|
+
const unified = parseUnifiedArgs(argv);
|
|
901
|
+
const args = {
|
|
902
|
+
...unified,
|
|
903
|
+
name: env.MEMORY_MCP_NAME,
|
|
904
|
+
version: env.MEMORY_MCP_VERSION
|
|
905
|
+
};
|
|
906
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
907
|
+
const arg = argv[i];
|
|
908
|
+
const next = argv[i + 1];
|
|
909
|
+
const takeValue = () => {
|
|
910
|
+
if (next === void 0) throw new Error(`[memory-store/mcp] ${arg} requires a value`);
|
|
911
|
+
i += 1;
|
|
912
|
+
return next;
|
|
913
|
+
};
|
|
914
|
+
switch (arg) {
|
|
915
|
+
case "--name":
|
|
916
|
+
args.name = takeValue();
|
|
917
|
+
break;
|
|
918
|
+
case "--version":
|
|
919
|
+
args.version = takeValue();
|
|
920
|
+
break;
|
|
921
|
+
case "--help":
|
|
922
|
+
case "-h":
|
|
923
|
+
printHelp();
|
|
924
|
+
process.exit(0);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
return args;
|
|
928
|
+
}
|
|
929
|
+
function printHelp() {
|
|
930
|
+
console.log(`opencontext-memory-mcp \u2014 standalone memory-store MCP daemon.
|
|
931
|
+
|
|
932
|
+
Usage:
|
|
933
|
+
opencontext-memory-mcp [options]
|
|
934
|
+
|
|
935
|
+
Server identity (advertised to MCP clients):
|
|
936
|
+
--name <name> Server name (env: MEMORY_MCP_NAME)
|
|
937
|
+
--version <version> Server version (env: MEMORY_MCP_VERSION)
|
|
938
|
+
`);
|
|
939
|
+
printUnifiedHelp();
|
|
940
|
+
console.log(`
|
|
941
|
+
Examples:
|
|
942
|
+
# Minimal \u2014 same as before, all three *not_configured warnings remain
|
|
943
|
+
opencontext-memory-mcp
|
|
944
|
+
|
|
945
|
+
# Local ONNX embedder + sqlite-vec ANN for the memory source
|
|
946
|
+
opencontext-memory-mcp \\
|
|
947
|
+
--embedding-provider local \\
|
|
948
|
+
--memory-backend sqlite-vec
|
|
949
|
+
|
|
950
|
+
# Wire everything via a running Chroma server
|
|
951
|
+
opencontext-memory-mcp \\
|
|
952
|
+
--embedding-provider openrouter \\
|
|
953
|
+
--chroma-url http://127.0.0.1:8000 \\
|
|
954
|
+
--memory-backend chroma \\
|
|
955
|
+
--insights-backend chroma \\
|
|
956
|
+
--knowledge-backend chroma`);
|
|
957
|
+
}
|
|
576
958
|
async function main() {
|
|
577
|
-
const
|
|
959
|
+
const args = parseArgs(process.argv.slice(2));
|
|
960
|
+
const unified = await buildUnified(args);
|
|
961
|
+
const server = await startMcpServer({
|
|
962
|
+
unified,
|
|
963
|
+
name: args.name,
|
|
964
|
+
version: args.version
|
|
965
|
+
});
|
|
578
966
|
console.error("[memory-store/mcp] listening on stdio");
|
|
579
967
|
const shutdown = async (signal) => {
|
|
580
968
|
console.error(`[memory-store/mcp] ${signal} received, shutting down\u2026`);
|