@anuraghq/neuron-mcp-server 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/index.js +501 -26
  2. package/index.js.map +4 -4
  3. package/package.json +1 -1
package/index.js CHANGED
@@ -34817,6 +34817,177 @@ function compressToPacket(project, scoredMemories, query) {
34817
34817
  return packet;
34818
34818
  }
34819
34819
 
34820
+ // ../context-engine/dist/ai/llm-tasks.js
34821
+ async function summarizeMemoriesWithLlm(llm, rawSummary, projectName) {
34822
+ if (!rawSummary.trim())
34823
+ return "No project memories stored yet.";
34824
+ return llm.chat([
34825
+ {
34826
+ role: "system",
34827
+ content: "You summarize software project knowledge for AI coding assistants. Be concise, structured, and actionable. Use markdown headings and bullet points."
34828
+ },
34829
+ {
34830
+ role: "user",
34831
+ content: `Project: ${projectName ?? "Unknown"}
34832
+
34833
+ Raw memory notes:
34834
+ ${rawSummary}
34835
+
34836
+ Write a compact project brief a developer can paste into an AI assistant.`
34837
+ }
34838
+ ], { maxTokens: 800, temperature: 0.2 });
34839
+ }
34840
+ async function enhanceContextNarrative(llm, query, memorySnippets) {
34841
+ if (!memorySnippets.length)
34842
+ return "";
34843
+ return llm.chat([
34844
+ {
34845
+ role: "system",
34846
+ content: "You synthesize project context for an AI coding session. Focus only on facts from the snippets. Max 150 words."
34847
+ },
34848
+ {
34849
+ role: "user",
34850
+ content: `Task/query: ${query}
34851
+
34852
+ Relevant memories:
34853
+ ${memorySnippets.join("\n---\n")}
34854
+
34855
+ Write a focused context paragraph.`
34856
+ }
34857
+ ], { maxTokens: 300, temperature: 0.2 });
34858
+ }
34859
+ async function generateMemorySummary(llm, title, content) {
34860
+ return llm.chat([
34861
+ {
34862
+ role: "system",
34863
+ content: "Write a one-sentence summary (max 25 words) of this project memory."
34864
+ },
34865
+ {
34866
+ role: "user",
34867
+ content: `Title: ${title}
34868
+ Content: ${content.slice(0, 2e3)}`
34869
+ }
34870
+ ], { maxTokens: 60, temperature: 0.1 });
34871
+ }
34872
+ async function suggestMemoryTags(llm, title, content) {
34873
+ const raw = await llm.chat([
34874
+ {
34875
+ role: "system",
34876
+ content: "Return 3-5 lowercase tags for this memory as a JSON array of strings. No markdown."
34877
+ },
34878
+ {
34879
+ role: "user",
34880
+ content: `Title: ${title}
34881
+ Content: ${content.slice(0, 1500)}`
34882
+ }
34883
+ ], { maxTokens: 80, temperature: 0.1 });
34884
+ try {
34885
+ const parsed = JSON.parse(raw);
34886
+ if (Array.isArray(parsed)) {
34887
+ return parsed.filter((t) => typeof t === "string").slice(0, 5);
34888
+ }
34889
+ } catch {
34890
+ }
34891
+ return [];
34892
+ }
34893
+ async function findDuplicateCandidates(llm, focus, candidates) {
34894
+ const others = candidates.filter((c) => c.id !== focus.id).slice(0, 20);
34895
+ if (!others.length)
34896
+ return [];
34897
+ const raw = await llm.chat([
34898
+ {
34899
+ role: "system",
34900
+ content: 'Find likely duplicate project memories. Return JSON array: [{ "memoryId": "uuid", "similarity": 0.0-1.0, "reason": "brief" }]. Only include pairs with similarity >= 0.5. No markdown.'
34901
+ },
34902
+ {
34903
+ role: "user",
34904
+ content: `Focus memory:
34905
+ ${focus.title}
34906
+ ${focus.content.slice(0, 800)}
34907
+
34908
+ Candidates:
34909
+ ${others.map((c) => `[${c.id}] ${c.title}: ${c.content.slice(0, 200)}`).join("\n")}`
34910
+ }
34911
+ ], { maxTokens: 400, temperature: 0.1 });
34912
+ try {
34913
+ const parsed = JSON.parse(raw);
34914
+ if (Array.isArray(parsed))
34915
+ return parsed.filter((d) => d.memoryId && d.similarity >= 0.5);
34916
+ } catch {
34917
+ }
34918
+ return [];
34919
+ }
34920
+ async function extractMemoriesFromText(llm, conversation) {
34921
+ const raw = await llm.chat([
34922
+ {
34923
+ role: "system",
34924
+ content: 'Extract durable project facts from a dev conversation. Return JSON array: [{ "type": "fact|decision|pattern|bug|task", "title": "...", "content": "...", "tags": [] }]. Max 5 items. No markdown.'
34925
+ },
34926
+ {
34927
+ role: "user",
34928
+ content: conversation.slice(0, 8e3)
34929
+ }
34930
+ ], { maxTokens: 800, temperature: 0.2 });
34931
+ try {
34932
+ const parsed = JSON.parse(raw);
34933
+ if (Array.isArray(parsed)) {
34934
+ return parsed.filter((d) => d.title && d.content).slice(0, 5);
34935
+ }
34936
+ } catch {
34937
+ }
34938
+ return [];
34939
+ }
34940
+
34941
+ // ../context-engine/dist/ai/llm-rerank.js
34942
+ async function rerankMemoriesWithLlm(llm, query, memories, limit) {
34943
+ if (!memories.length)
34944
+ return [];
34945
+ const listing = memories.slice(0, 30).map((m, i) => `[${i}] ${m.title}: ${(m.summary ?? m.content).slice(0, 120)}`).join("\n");
34946
+ const raw = await llm.chat([
34947
+ {
34948
+ role: "system",
34949
+ content: 'Rank memory indices by relevance to the query. Reply with JSON: {"ranked":[0,2,1,...]} using only indices from the list.'
34950
+ },
34951
+ { role: "user", content: `Query: ${query}
34952
+
34953
+ Memories:
34954
+ ${listing}` }
34955
+ ], { maxTokens: 120, temperature: 0 });
34956
+ try {
34957
+ const parsed = JSON.parse(raw);
34958
+ const ranked = parsed.ranked ?? [];
34959
+ return ranked.filter((i) => i >= 0 && i < memories.length).slice(0, limit).map((i, rank) => ({
34960
+ memory: memories[i],
34961
+ score: 1 - rank * 0.05,
34962
+ scoreBreakdown: {
34963
+ semanticSimilarity: 0.8,
34964
+ keywordMatch: 0.5,
34965
+ graphProximity: 0,
34966
+ recency: 0.5,
34967
+ importance: memories[i].importance,
34968
+ layerPriority: 0.5,
34969
+ confidence: memories[i].confidence,
34970
+ composite: 1 - rank * 0.05
34971
+ }
34972
+ }));
34973
+ } catch {
34974
+ return memories.slice(0, limit).map((m) => ({
34975
+ memory: m,
34976
+ score: 0.5,
34977
+ scoreBreakdown: {
34978
+ semanticSimilarity: 0,
34979
+ keywordMatch: 0.5,
34980
+ graphProximity: 0,
34981
+ recency: 0.5,
34982
+ importance: m.importance,
34983
+ layerPriority: 0.5,
34984
+ confidence: m.confidence,
34985
+ composite: 0.5
34986
+ }
34987
+ }));
34988
+ }
34989
+ }
34990
+
34820
34991
  // ../context-engine/dist/engine.js
34821
34992
  var DEFAULT_TOKEN_BUDGET = 4e3;
34822
34993
  var ContextEngine = class {
@@ -34838,10 +35009,23 @@ var ContextEngine = class {
34838
35009
  }
34839
35010
  }
34840
35011
  if (this.deps.embeddingProvider) {
34841
- const text = `${memory.title}
35012
+ try {
35013
+ const text = `${memory.title}
34842
35014
  ${memory.content}`;
34843
- const vector = await this.deps.embeddingProvider.embed(text);
34844
- await this.deps.embeddings.create(memory.id, input.projectId, vector, this.deps.embeddingProvider.model);
35015
+ const vector = await this.deps.embeddingProvider.embed(text);
35016
+ await this.deps.embeddings.create(memory.id, input.projectId, vector, this.deps.embeddingProvider.model);
35017
+ } catch (err) {
35018
+ console.warn("Embedding skipped:", err instanceof Error ? err.message : err);
35019
+ }
35020
+ }
35021
+ if (this.deps.llm && !memory.summary) {
35022
+ try {
35023
+ const summary = await generateMemorySummary(this.deps.llm, memory.title, memory.content);
35024
+ const tags = input.tags?.length ? input.tags : await suggestMemoryTags(this.deps.llm, memory.title, memory.content);
35025
+ return await this.deps.memories.update(memory.id, { summary, tags });
35026
+ } catch (err) {
35027
+ console.warn("AI memory enrichment skipped:", err instanceof Error ? err.message : err);
35028
+ }
34845
35029
  }
34846
35030
  return memory;
34847
35031
  }
@@ -34865,11 +35049,21 @@ ${memory.content}`;
34865
35049
  status: MemoryStatus.Active,
34866
35050
  limit: 100
34867
35051
  });
34868
- const scored = rankMemories(memories, signals, semanticScores);
35052
+ let results = rankMemories(memories, signals, semanticScores);
34869
35053
  const limit = options?.limit ?? 20;
35054
+ if (this.deps.llm && query.trim()) {
35055
+ try {
35056
+ results = await rerankMemoriesWithLlm(this.deps.llm, query, results.map((r) => r.memory), limit);
35057
+ } catch (err) {
35058
+ console.warn("LLM rerank skipped:", err instanceof Error ? err.message : err);
35059
+ results = results.slice(0, limit);
35060
+ }
35061
+ } else {
35062
+ results = results.slice(0, limit);
35063
+ }
34870
35064
  return {
34871
- results: scored.slice(0, limit),
34872
- totalCount: scored.length,
35065
+ results,
35066
+ totalCount: results.length,
34873
35067
  query
34874
35068
  };
34875
35069
  }
@@ -34920,14 +35114,38 @@ ${memory.content}`;
34920
35114
  for (const s of selected) {
34921
35115
  await this.deps.memories.incrementAccess(s.memory.id);
34922
35116
  }
34923
- return compressToPacket(project, selected, query);
35117
+ const packet = compressToPacket(project, selected, query);
35118
+ if (this.deps.llm && (query.query || query.taskDescription)) {
35119
+ try {
35120
+ const focus = query.query ?? query.taskDescription ?? "";
35121
+ const snippets = selected.map((s) => `- ${s.memory.title}: ${s.memory.summary ?? s.memory.content.slice(0, 200)}`);
35122
+ const narrative = await enhanceContextNarrative(this.deps.llm, focus, snippets);
35123
+ packet.architecture = {
35124
+ summary: narrative,
35125
+ layers: packet.architecture?.layers ?? [],
35126
+ patterns: packet.architecture?.patterns ?? [],
35127
+ keyDecisions: packet.architecture?.keyDecisions ?? []
35128
+ };
35129
+ packet.tokenEstimate = estimateTokens(JSON.stringify(packet));
35130
+ } catch (err) {
35131
+ console.warn("AI context narrative skipped:", err instanceof Error ? err.message : err);
35132
+ }
35133
+ }
35134
+ return packet;
34924
35135
  }
34925
35136
  /** Mark a memory as forgotten */
34926
35137
  async forget(memoryId, reason) {
34927
35138
  await this.deps.memories.forget({ memoryId, reason });
34928
35139
  }
34929
- /** Merge two memories into one */
34930
- async merge(sourceId, targetId) {
35140
+ /** Merge two memories checks duplicates unless force=true */
35141
+ async merge(sourceId, targetId, options) {
35142
+ if (!options?.force && this.deps.llm) {
35143
+ const dupes = await this.findDuplicates((await this.deps.memories.findById(sourceId))?.projectId ?? "", sourceId);
35144
+ const match = dupes.find((d) => d.memoryId === targetId);
35145
+ if (!match || match.similarity < 0.6) {
35146
+ throw new Error(`Memories may not be duplicates (similarity ${match?.similarity ?? 0}). Pass force=true to merge anyway.`);
35147
+ }
35148
+ }
34931
35149
  return this.deps.memories.merge({
34932
35150
  sourceMemoryId: sourceId,
34933
35151
  targetMemoryId: targetId
@@ -34964,10 +35182,100 @@ ${memory.content}`;
34964
35182
  lines.push("## Open Bugs");
34965
35183
  bugs.filter((b) => b.metadata.status === "open").forEach((b) => lines.push(`- [${b.metadata.severity}] ${b.title}`));
34966
35184
  }
34967
- return lines.join("\n");
35185
+ const raw = lines.join("\n");
35186
+ if (this.deps.llm) {
35187
+ try {
35188
+ const project = await this.deps.projects.findById(projectId);
35189
+ return await summarizeMemoriesWithLlm(this.deps.llm, raw, project?.name);
35190
+ } catch (err) {
35191
+ console.warn("AI summarize skipped:", err instanceof Error ? err.message : err);
35192
+ }
35193
+ }
35194
+ return raw || "No project memories stored yet.";
35195
+ }
35196
+ /** Find likely duplicate memories using Groq */
35197
+ async findDuplicates(projectId, memoryId) {
35198
+ const memories = await this.deps.memories.findByProject(projectId, {
35199
+ status: MemoryStatus.Active,
35200
+ limit: 80
35201
+ });
35202
+ if (!this.deps.llm || memories.length < 2)
35203
+ return [];
35204
+ const focus = memoryId ? memories.find((m) => m.id === memoryId) : memories[0];
35205
+ if (!focus)
35206
+ return [];
35207
+ try {
35208
+ return await findDuplicateCandidates(this.deps.llm, focus, memories);
35209
+ } catch (err) {
35210
+ console.warn("Duplicate detection skipped:", err instanceof Error ? err.message : err);
35211
+ return [];
35212
+ }
35213
+ }
35214
+ /** Extract structured memories from a Cursor/AI conversation transcript */
35215
+ async extractMemoriesFromConversation(projectId, conversation) {
35216
+ if (!this.deps.llm) {
35217
+ throw new Error("GROQ_API_KEY required for memory extraction");
35218
+ }
35219
+ const drafts = await extractMemoriesFromText(this.deps.llm, conversation);
35220
+ const saved = [];
35221
+ for (const draft of drafts) {
35222
+ const memory = await this.remember({
35223
+ projectId,
35224
+ type: draft.type,
35225
+ title: draft.title,
35226
+ content: draft.content,
35227
+ tags: draft.tags,
35228
+ layer: ContextLayer.Project,
35229
+ source: { type: "conversation" }
35230
+ });
35231
+ saved.push(memory);
35232
+ }
35233
+ return saved;
34968
35234
  }
34969
35235
  };
34970
35236
 
35237
+ // ../context-engine/dist/ai/groq-client.js
35238
+ var GROQ_API_URL = "https://api.groq.com/openai/v1/chat/completions";
35239
+ var DEFAULT_MODEL = "llama-3.3-70b-versatile";
35240
+ function createGroqClient(apiKey) {
35241
+ const key = apiKey ?? process.env.GROQ_API_KEY;
35242
+ if (!key)
35243
+ return void 0;
35244
+ return {
35245
+ model: DEFAULT_MODEL,
35246
+ async chat(messages, options) {
35247
+ const res = await fetch(GROQ_API_URL, {
35248
+ method: "POST",
35249
+ headers: {
35250
+ Authorization: `Bearer ${key}`,
35251
+ "Content-Type": "application/json"
35252
+ },
35253
+ body: JSON.stringify({
35254
+ model: DEFAULT_MODEL,
35255
+ messages,
35256
+ max_tokens: options?.maxTokens ?? 1024,
35257
+ temperature: options?.temperature ?? 0.3
35258
+ })
35259
+ });
35260
+ if (!res.ok) {
35261
+ const body = await res.text();
35262
+ throw new Error(`Groq API error (${res.status}): ${body}`);
35263
+ }
35264
+ const data = await res.json();
35265
+ const content = data.choices?.[0]?.message?.content;
35266
+ if (!content)
35267
+ throw new Error("Groq API returned empty response");
35268
+ return content;
35269
+ }
35270
+ };
35271
+ }
35272
+
35273
+ // ../context-engine/dist/ai/create-ai-deps.js
35274
+ function createAiDeps() {
35275
+ const llm = createGroqClient();
35276
+ return { llm, embeddingProvider: void 0 };
35277
+ }
35278
+
34971
35279
  // src/tools/schemas.ts
34972
35280
  var RememberSchema = external_exports.object({
34973
35281
  project_id: external_exports.string().uuid(),
@@ -43346,6 +43654,10 @@ function createMemoryRepository(client) {
43346
43654
  patch.title = data.title;
43347
43655
  if (data.content)
43348
43656
  patch.content = data.content;
43657
+ if (data.summary !== void 0)
43658
+ patch.summary = data.summary;
43659
+ if (data.tags)
43660
+ patch.tags = data.tags;
43349
43661
  if (data.status)
43350
43662
  patch.status = data.status;
43351
43663
  if (data.confidence !== void 0)
@@ -43487,8 +43799,20 @@ function createEmbeddingRepository(client) {
43487
43799
  console.warn("Embedding insert skipped:", error2.message);
43488
43800
  }
43489
43801
  },
43490
- async search(_projectId, _vector, _limit) {
43491
- return [];
43802
+ async search(projectId, vector, limit) {
43803
+ const { data, error: error2 } = await client.rpc("match_embeddings", {
43804
+ query_embedding: JSON.stringify(vector),
43805
+ p_project_id: projectId,
43806
+ match_count: limit
43807
+ });
43808
+ if (error2) {
43809
+ console.warn("Vector search skipped:", error2.message);
43810
+ return [];
43811
+ }
43812
+ return (data ?? []).map((row) => ({
43813
+ memoryId: row.memory_id,
43814
+ similarity: row.similarity
43815
+ }));
43492
43816
  }
43493
43817
  };
43494
43818
  }
@@ -43500,7 +43824,8 @@ function createContextEngineDeps(useServiceRole = false) {
43500
43824
  memories: createMemoryRepository(client),
43501
43825
  relationships: createRelationshipRepository(client),
43502
43826
  embeddings: createEmbeddingRepository(client),
43503
- projects: createProjectRepository(client)
43827
+ projects: createProjectRepository(client),
43828
+ ...createAiDeps()
43504
43829
  };
43505
43830
  }
43506
43831
 
@@ -43702,46 +44027,187 @@ function resolveEngineDeps() {
43702
44027
  return createInMemoryDeps();
43703
44028
  }
43704
44029
 
44030
+ // src/adapters/hosted-client.ts
44031
+ var DEFAULT_API_URL = "https://neuron-azure.vercel.app";
44032
+ async function hostedFetch(apiUrl, apiKey, tool, args) {
44033
+ const res = await fetch(`${apiUrl.replace(/\/$/, "")}/api/mcp`, {
44034
+ method: "POST",
44035
+ headers: {
44036
+ Authorization: `Bearer ${apiKey}`,
44037
+ "Content-Type": "application/json"
44038
+ },
44039
+ body: JSON.stringify({ tool, args })
44040
+ });
44041
+ const body = await res.json().catch(() => ({}));
44042
+ if (!res.ok) {
44043
+ throw new Error(body.error ?? `Neuron API error (${res.status})`);
44044
+ }
44045
+ return body;
44046
+ }
44047
+ async function resolveHostedProjectId(apiUrl, apiKey) {
44048
+ const res = await fetch(`${apiUrl.replace(/\/$/, "")}/api/mcp`, {
44049
+ headers: { Authorization: `Bearer ${apiKey}` }
44050
+ });
44051
+ const body = await res.json().catch(() => ({}));
44052
+ if (!res.ok || !body.project_id) {
44053
+ throw new Error(body.error ?? "Could not resolve project from NEURON_API_KEY");
44054
+ }
44055
+ return body.project_id;
44056
+ }
44057
+ function createHostedEngine(apiUrl, apiKey, projectId) {
44058
+ const base = apiUrl.replace(/\/$/, "") || DEFAULT_API_URL;
44059
+ const withProject = (args) => ({
44060
+ ...args,
44061
+ project_id: args.project_id ?? projectId
44062
+ });
44063
+ const engine = {
44064
+ async remember(input) {
44065
+ const tool = `remember_${input.type}`;
44066
+ const result = await hostedFetch(base, apiKey, tool, {
44067
+ project_id: input.projectId ?? projectId,
44068
+ title: input.title,
44069
+ content: input.content,
44070
+ confidence: input.confidence,
44071
+ importance: input.importance,
44072
+ tags: input.tags,
44073
+ metadata: input.metadata
44074
+ });
44075
+ return result.memory ?? result;
44076
+ },
44077
+ async searchMemory(pid, query, opts) {
44078
+ const result = await hostedFetch(base, apiKey, "search_memory", withProject({
44079
+ project_id: pid || projectId,
44080
+ query,
44081
+ types: opts?.types,
44082
+ limit: opts?.limit
44083
+ }));
44084
+ return Array.isArray(result) ? result : [];
44085
+ },
44086
+ async getProjectContext(input) {
44087
+ const result = await hostedFetch(base, apiKey, "get_project_context", withProject({
44088
+ project_id: input.projectId || projectId,
44089
+ query: input.query,
44090
+ task_description: input.taskDescription,
44091
+ open_files: input.openFiles,
44092
+ branch_name: input.branchName,
44093
+ token_budget: input.tokenBudget
44094
+ }));
44095
+ return result.packet ?? result;
44096
+ },
44097
+ async findRelated(memoryId, depth) {
44098
+ const result = await hostedFetch(base, apiKey, "find_related", {
44099
+ memory_id: memoryId,
44100
+ depth
44101
+ });
44102
+ return result.memories ?? result;
44103
+ },
44104
+ async summarizeProject(pid) {
44105
+ const result = await hostedFetch(base, apiKey, "summarize_project", {
44106
+ project_id: pid || projectId
44107
+ });
44108
+ return result.summary ?? "";
44109
+ },
44110
+ async forget(memoryId, reason) {
44111
+ await hostedFetch(base, apiKey, "forget_memory", { memory_id: memoryId, reason });
44112
+ },
44113
+ async merge(sourceId, targetId, options) {
44114
+ const result = await hostedFetch(base, apiKey, "merge_memory", {
44115
+ source_memory_id: sourceId,
44116
+ target_memory_id: targetId,
44117
+ force: options?.force
44118
+ });
44119
+ return result.memory ?? result;
44120
+ },
44121
+ async findDuplicates(pid, memoryId) {
44122
+ const result = await hostedFetch(base, apiKey, "find_duplicates", withProject({
44123
+ project_id: pid || projectId,
44124
+ memory_id: memoryId
44125
+ }));
44126
+ return result.duplicates ?? [];
44127
+ },
44128
+ async extractMemoriesFromConversation(pid, conversation) {
44129
+ const result = await hostedFetch(base, apiKey, "extract_memories", withProject({
44130
+ project_id: pid || projectId,
44131
+ conversation
44132
+ }));
44133
+ return result.extracted ?? [];
44134
+ }
44135
+ };
44136
+ return engine;
44137
+ }
44138
+ function isHostedMode() {
44139
+ return !!process.env.NEURON_API_KEY?.startsWith("nrn_");
44140
+ }
44141
+ function getHostedConfig() {
44142
+ return {
44143
+ apiKey: process.env.NEURON_API_KEY,
44144
+ apiUrl: process.env.NEURON_API_URL ?? DEFAULT_API_URL
44145
+ };
44146
+ }
44147
+
43705
44148
  // src/cli/init.ts
43706
44149
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
43707
44150
  import { homedir } from "node:os";
43708
44151
  import { join } from "node:path";
43709
44152
  var NPX_PACKAGE = "@anuraghq/neuron-mcp-server";
44153
+ var DEFAULT_API_URL2 = "https://neuron-azure.vercel.app";
43710
44154
  function parseArgs(argv) {
43711
44155
  const opts = { cursor: true };
43712
44156
  for (let i = 0; i < argv.length; i++) {
43713
44157
  const arg = argv[i];
43714
44158
  if (arg === "--project") opts.project = true;
43715
44159
  if (arg === "--stdout") opts.stdout = true;
44160
+ if (arg === "--api-key") opts.apiKey = argv[++i];
44161
+ if (arg === "--api-url") opts.apiUrl = argv[++i];
43716
44162
  if (arg === "--supabase-url") opts.supabaseUrl = argv[++i];
43717
44163
  if (arg === "--service-key") opts.serviceRoleKey = argv[++i];
43718
44164
  if (arg === "--project-id") opts.projectId = argv[++i];
43719
- if (arg === "--groq-key") opts.groqApiKey = argv[++i];
43720
44165
  }
43721
44166
  return opts;
43722
44167
  }
43723
- function buildNeuronConfig(opts) {
44168
+ function buildHostedConfig(opts) {
44169
+ const apiKey = opts.apiKey ?? process.env.NEURON_API_KEY;
44170
+ const apiUrl = opts.apiUrl ?? process.env.NEURON_API_URL ?? DEFAULT_API_URL2;
44171
+ if (!apiKey?.startsWith("nrn_")) {
44172
+ throw new Error(
44173
+ "Missing NEURON_API_KEY. Get one from the Neuron dashboard \u2192 Settings, then:\n NEURON_API_KEY=nrn_... npx @anuraghq/neuron-mcp-server init\nOr pass: --api-key nrn_... [--api-url https://neuron-azure.vercel.app]"
44174
+ );
44175
+ }
44176
+ return {
44177
+ command: "npx",
44178
+ args: ["-y", NPX_PACKAGE],
44179
+ env: {
44180
+ NEURON_API_KEY: apiKey,
44181
+ NEURON_API_URL: apiUrl
44182
+ }
44183
+ };
44184
+ }
44185
+ function buildDirectConfig(opts) {
43724
44186
  const supabaseUrl = opts.supabaseUrl ?? process.env.NEXT_PUBLIC_SUPABASE_URL;
43725
44187
  const serviceRoleKey = opts.serviceRoleKey ?? process.env.SUPABASE_SERVICE_ROLE_KEY;
43726
44188
  const projectId = opts.projectId ?? process.env.NEURON_PROJECT_ID;
43727
- const groqApiKey = opts.groqApiKey ?? process.env.GROQ_API_KEY;
43728
44189
  if (!supabaseUrl || !serviceRoleKey || !projectId) {
43729
44190
  throw new Error(
43730
- "Missing credentials. Set env vars or pass flags:\n --supabase-url --service-key --project-id\nOr: NEXT_PUBLIC_SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, NEURON_PROJECT_ID"
44191
+ "Direct mode requires Supabase env vars. Prefer hosted mode with NEURON_API_KEY."
43731
44192
  );
43732
44193
  }
43733
- const env = {
43734
- NEXT_PUBLIC_SUPABASE_URL: supabaseUrl,
43735
- SUPABASE_SERVICE_ROLE_KEY: serviceRoleKey,
43736
- NEURON_PROJECT_ID: projectId
43737
- };
43738
- if (groqApiKey) env.GROQ_API_KEY = groqApiKey;
43739
44194
  return {
43740
44195
  command: "npx",
43741
44196
  args: ["-y", NPX_PACKAGE],
43742
- env
44197
+ env: {
44198
+ NEXT_PUBLIC_SUPABASE_URL: supabaseUrl,
44199
+ SUPABASE_SERVICE_ROLE_KEY: serviceRoleKey,
44200
+ NEURON_PROJECT_ID: projectId
44201
+ }
43743
44202
  };
43744
44203
  }
44204
+ function buildNeuronConfig(opts) {
44205
+ const apiKey = opts.apiKey ?? process.env.NEURON_API_KEY;
44206
+ if (apiKey?.startsWith("nrn_")) return buildHostedConfig(opts);
44207
+ const hasDirect = (opts.supabaseUrl ?? process.env.NEXT_PUBLIC_SUPABASE_URL) && (opts.serviceRoleKey ?? process.env.SUPABASE_SERVICE_ROLE_KEY) && (opts.projectId ?? process.env.NEURON_PROJECT_ID);
44208
+ if (hasDirect) return buildDirectConfig(opts);
44209
+ return buildHostedConfig(opts);
44210
+ }
43745
44211
  function mergeMcpJson(existing, neuronConfig) {
43746
44212
  const servers = existing.mcpServers ?? {};
43747
44213
  return { ...existing, mcpServers: { ...servers, neuron: neuronConfig } };
@@ -43778,8 +44244,17 @@ async function runInit(argv) {
43778
44244
 
43779
44245
  // src/index.ts
43780
44246
  async function startServer() {
43781
- const deps = resolveEngineDeps();
43782
- const engine = new ContextEngine(deps);
44247
+ let engine;
44248
+ if (isHostedMode()) {
44249
+ const { apiKey, apiUrl } = getHostedConfig();
44250
+ const projectId = await resolveHostedProjectId(apiUrl, apiKey);
44251
+ engine = createHostedEngine(apiUrl, apiKey, projectId);
44252
+ console.error(`Neuron MCP: hosted mode \u2192 ${apiUrl} (project ${projectId.slice(0, 8)}\u2026)`);
44253
+ } else {
44254
+ const deps = resolveEngineDeps();
44255
+ engine = new ContextEngine(deps);
44256
+ console.error("Neuron MCP: direct mode (Supabase env vars)");
44257
+ }
43783
44258
  const server = new McpServer({
43784
44259
  name: "neuron",
43785
44260
  version: "0.1.0"