@memoarchitect/tools 0.6.3 → 0.6.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.
Files changed (89) hide show
  1. package/README.md +1 -1
  2. package/package.json +2 -2
  3. package/packages/tools/lib/bin/memo.js +38 -2
  4. package/packages/tools/lib/bin/memo.js.map +1 -1
  5. package/packages/tools/lib/browser/index.d.ts +3 -0
  6. package/packages/tools/lib/browser/index.d.ts.map +1 -1
  7. package/packages/tools/lib/commands/dev.d.ts.map +1 -1
  8. package/packages/tools/lib/commands/dev.js +4 -1
  9. package/packages/tools/lib/commands/dev.js.map +1 -1
  10. package/packages/tools/lib/commands/init.d.ts +2 -0
  11. package/packages/tools/lib/commands/init.d.ts.map +1 -1
  12. package/packages/tools/lib/commands/init.js +32 -16
  13. package/packages/tools/lib/commands/init.js.map +1 -1
  14. package/packages/tools/lib/commands/mcp.d.ts +15 -0
  15. package/packages/tools/lib/commands/mcp.d.ts.map +1 -0
  16. package/packages/tools/lib/commands/mcp.js +128 -0
  17. package/packages/tools/lib/commands/mcp.js.map +1 -0
  18. package/packages/tools/lib/commands/validate.js +1 -1
  19. package/packages/tools/lib/commands/validate.js.map +1 -1
  20. package/packages/tools/lib/index.d.ts.map +1 -1
  21. package/packages/tools/lib/index.js +4 -0
  22. package/packages/tools/lib/index.js.map +1 -1
  23. package/packages/tools/lib/llm/chat-engine.d.ts +97 -0
  24. package/packages/tools/lib/llm/chat-engine.d.ts.map +1 -0
  25. package/packages/tools/lib/llm/chat-engine.js +463 -0
  26. package/packages/tools/lib/llm/chat-engine.js.map +1 -0
  27. package/packages/tools/lib/llm/index.d.ts +2 -0
  28. package/packages/tools/lib/llm/index.d.ts.map +1 -1
  29. package/packages/tools/lib/llm/index.js +2 -0
  30. package/packages/tools/lib/llm/index.js.map +1 -1
  31. package/packages/tools/lib/llm/llm-provider.d.ts +40 -4
  32. package/packages/tools/lib/llm/llm-provider.d.ts.map +1 -1
  33. package/packages/tools/lib/llm/llm-provider.js +180 -37
  34. package/packages/tools/lib/llm/llm-provider.js.map +1 -1
  35. package/packages/tools/lib/llm/llm-settings.d.ts +84 -0
  36. package/packages/tools/lib/llm/llm-settings.d.ts.map +1 -0
  37. package/packages/tools/lib/llm/llm-settings.js +249 -0
  38. package/packages/tools/lib/llm/llm-settings.js.map +1 -0
  39. package/packages/tools/lib/mcp/index.d.ts +4 -0
  40. package/packages/tools/lib/mcp/index.d.ts.map +1 -0
  41. package/packages/tools/lib/mcp/index.js +9 -0
  42. package/packages/tools/lib/mcp/index.js.map +1 -0
  43. package/packages/tools/lib/mcp/model-loader.d.ts +11 -0
  44. package/packages/tools/lib/mcp/model-loader.d.ts.map +1 -0
  45. package/packages/tools/lib/mcp/model-loader.js +87 -0
  46. package/packages/tools/lib/mcp/model-loader.js.map +1 -0
  47. package/packages/tools/lib/mcp/server.d.ts +8 -0
  48. package/packages/tools/lib/mcp/server.d.ts.map +1 -0
  49. package/packages/tools/lib/mcp/server.js +132 -0
  50. package/packages/tools/lib/mcp/server.js.map +1 -0
  51. package/packages/tools/lib/mcp/tools.d.ts +18 -0
  52. package/packages/tools/lib/mcp/tools.d.ts.map +1 -0
  53. package/packages/tools/lib/mcp/tools.js +220 -0
  54. package/packages/tools/lib/mcp/tools.js.map +1 -0
  55. package/packages/tools/lib/model/builder.js +8 -2
  56. package/packages/tools/lib/model/builder.js.map +1 -1
  57. package/packages/tools/lib/model/config.d.ts +6 -0
  58. package/packages/tools/lib/model/config.d.ts.map +1 -1
  59. package/packages/tools/lib/model/semantic.d.ts +6 -0
  60. package/packages/tools/lib/model/semantic.d.ts.map +1 -1
  61. package/packages/tools/lib/model/semantic.js.map +1 -1
  62. package/packages/tools/lib/model/view-deriver.d.ts +3 -2
  63. package/packages/tools/lib/model/view-deriver.d.ts.map +1 -1
  64. package/packages/tools/lib/model/view-deriver.js +61 -20
  65. package/packages/tools/lib/model/view-deriver.js.map +1 -1
  66. package/packages/tools/lib/operations/project-snapshot.d.ts.map +1 -1
  67. package/packages/tools/lib/operations/project-snapshot.js +2 -1
  68. package/packages/tools/lib/operations/project-snapshot.js.map +1 -1
  69. package/packages/tools/lib/protocol/messages.d.ts +106 -2
  70. package/packages/tools/lib/protocol/messages.d.ts.map +1 -1
  71. package/packages/tools/lib/server/dev-server.d.ts.map +1 -1
  72. package/packages/tools/lib/server/dev-server.js +205 -11
  73. package/packages/tools/lib/server/dev-server.js.map +1 -1
  74. package/packages/tools/lib/server/dhf-doc-store.d.ts +2 -0
  75. package/packages/tools/lib/server/dhf-doc-store.d.ts.map +1 -1
  76. package/packages/tools/lib/server/dhf-doc-store.js +31 -8
  77. package/packages/tools/lib/server/dhf-doc-store.js.map +1 -1
  78. package/packages/tools/lib/validator/constraint-eval.d.ts +3 -2
  79. package/packages/tools/lib/validator/constraint-eval.d.ts.map +1 -1
  80. package/packages/tools/lib/validator/constraint-eval.js +31 -5
  81. package/packages/tools/lib/validator/constraint-eval.js.map +1 -1
  82. package/packages/tools/lib/validator/rule-engine.d.ts +2 -1
  83. package/packages/tools/lib/validator/rule-engine.d.ts.map +1 -1
  84. package/packages/tools/lib/validator/rule-engine.js +2 -2
  85. package/packages/tools/lib/validator/rule-engine.js.map +1 -1
  86. package/packages/tools/lib/validator/view-validator.d.ts +1 -0
  87. package/packages/tools/lib/validator/view-validator.d.ts.map +1 -1
  88. package/packages/tools/lib/validator/view-validator.js +19 -4
  89. package/packages/tools/lib/validator/view-validator.js.map +1 -1
@@ -0,0 +1,249 @@
1
+ // ─── LLM Settings Resolution ─────────────────────────────────────────────────
2
+ //
3
+ // Resolves LLM provider settings from four sources, highest precedence first:
4
+ //
5
+ // 1. The real process environment (ANTHROPIC_API_KEY, …)
6
+ // 2. A project `.env` file (<projectRoot>/.env)
7
+ // 3. Project settings (<projectRoot>/.memo/llm.json)
8
+ // 4. User credentials (~/.memo/credentials.json)
9
+ //
10
+ // Only the last source is written by MEMO itself, and it lives outside the
11
+ // project so a key entered in the workbench can never be committed. Project
12
+ // settings hold provider/model/baseUrl and are safe to version — the loader
13
+ // ignores an `apiKey` there rather than encouraging a key into the repo.
14
+ // ─────────────────────────────────────────────────────────────────────────────
15
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync } from 'node:fs';
16
+ import { resolve, dirname } from 'node:path';
17
+ import { homedir } from 'node:os';
18
+ /** Default model per provider. Both are the current flagship at time of writing. */
19
+ export const DEFAULT_MODELS = {
20
+ anthropic: 'claude-opus-5',
21
+ openai: 'gpt-4o',
22
+ };
23
+ const PROJECT_SETTINGS_RELATIVE = ['.memo', 'llm.json'];
24
+ const CREDENTIALS_RELATIVE = ['.memo', 'credentials.json'];
25
+ // ─── .env parsing ────────────────────────────────────────────────────────────
26
+ /**
27
+ * Parse a `.env` file body into key/value pairs.
28
+ *
29
+ * Deliberately small: `KEY=value` per line, `#` comments, optional `export`
30
+ * prefix, and single- or double-quoted values (escapes are only expanded
31
+ * inside double quotes, matching the common dotenv behaviour). Anything more
32
+ * elaborate belongs in a real shell, not a settings file.
33
+ */
34
+ export function parseEnvFile(contents) {
35
+ const out = {};
36
+ for (const rawLine of contents.split(/\r?\n/)) {
37
+ const line = rawLine.trim();
38
+ if (!line || line.startsWith('#'))
39
+ continue;
40
+ const withoutExport = line.startsWith('export ') ? line.slice('export '.length).trim() : line;
41
+ const eq = withoutExport.indexOf('=');
42
+ if (eq <= 0)
43
+ continue;
44
+ const key = withoutExport.slice(0, eq).trim();
45
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
46
+ continue;
47
+ let value = withoutExport.slice(eq + 1).trim();
48
+ if (value.startsWith('"') && value.endsWith('"') && value.length >= 2) {
49
+ value = value.slice(1, -1).replace(/\\n/g, '\n').replace(/\\t/g, '\t').replace(/\\"/g, '"');
50
+ }
51
+ else if (value.startsWith("'") && value.endsWith("'") && value.length >= 2) {
52
+ value = value.slice(1, -1);
53
+ }
54
+ else {
55
+ // Unquoted values run to an unescaped `#`, so `KEY=value # note` works.
56
+ const hash = value.indexOf(' #');
57
+ if (hash >= 0)
58
+ value = value.slice(0, hash).trimEnd();
59
+ }
60
+ out[key] = value;
61
+ }
62
+ return out;
63
+ }
64
+ /** Read `<projectRoot>/.env`, or an empty map when there is none. */
65
+ export function loadEnvFile(projectRoot) {
66
+ const path = resolve(projectRoot, '.env');
67
+ if (!existsSync(path))
68
+ return {};
69
+ try {
70
+ return parseEnvFile(readFileSync(path, 'utf8'));
71
+ }
72
+ catch {
73
+ return {};
74
+ }
75
+ }
76
+ // ─── Project settings (no secrets) ───────────────────────────────────────────
77
+ export function projectSettingsPath(projectRoot) {
78
+ return resolve(projectRoot, ...PROJECT_SETTINGS_RELATIVE);
79
+ }
80
+ export function loadLlmProjectSettings(projectRoot) {
81
+ const path = projectSettingsPath(projectRoot);
82
+ if (!existsSync(path))
83
+ return {};
84
+ try {
85
+ const parsed = JSON.parse(readFileSync(path, 'utf8'));
86
+ // A key here would be one `git add` away from being published. Drop it.
87
+ const { provider, model, baseUrl } = parsed;
88
+ return {
89
+ provider: provider === 'anthropic' || provider === 'openai' ? provider : undefined,
90
+ model: typeof model === 'string' && model ? model : undefined,
91
+ baseUrl: typeof baseUrl === 'string' && baseUrl ? baseUrl : undefined,
92
+ };
93
+ }
94
+ catch {
95
+ return {};
96
+ }
97
+ }
98
+ export function saveLlmProjectSettings(projectRoot, settings) {
99
+ const path = projectSettingsPath(projectRoot);
100
+ mkdirSync(dirname(path), { recursive: true });
101
+ const clean = {};
102
+ if (settings.provider)
103
+ clean.provider = settings.provider;
104
+ if (settings.model)
105
+ clean.model = settings.model;
106
+ if (settings.baseUrl)
107
+ clean.baseUrl = settings.baseUrl;
108
+ writeFileSync(path, `${JSON.stringify(clean, null, 2)}\n`, 'utf8');
109
+ }
110
+ // ─── User credentials (secrets, outside the project) ─────────────────────────
111
+ /**
112
+ * Where user credentials live.
113
+ *
114
+ * `MEMO_CREDENTIALS_PATH` overrides the default so CI, containers, and tests
115
+ * can point somewhere other than the real home directory — without it, a key a
116
+ * developer saved through the workbench would leak into their test runs.
117
+ */
118
+ export function credentialsPath() {
119
+ const override = process.env.MEMO_CREDENTIALS_PATH;
120
+ if (override)
121
+ return resolve(override);
122
+ return resolve(homedir(), ...CREDENTIALS_RELATIVE);
123
+ }
124
+ export function loadLlmCredentials() {
125
+ const path = credentialsPath();
126
+ if (!existsSync(path))
127
+ return {};
128
+ try {
129
+ const parsed = JSON.parse(readFileSync(path, 'utf8'));
130
+ return parsed && typeof parsed === 'object' ? parsed : {};
131
+ }
132
+ catch {
133
+ return {};
134
+ }
135
+ }
136
+ /**
137
+ * Store an API key for one provider, merging with any key already held for the
138
+ * other. Written 0600 — this file is the one place MEMO persists a secret.
139
+ */
140
+ export function saveLlmCredential(provider, apiKey) {
141
+ const path = credentialsPath();
142
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
143
+ const existing = loadLlmCredentials();
144
+ const next = { ...existing, [provider]: { apiKey } };
145
+ writeFileSync(path, `${JSON.stringify(next, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
146
+ try {
147
+ chmodSync(path, 0o600);
148
+ }
149
+ catch {
150
+ // Best effort — a filesystem without POSIX modes is not a reason to fail the save.
151
+ }
152
+ }
153
+ /** Forget the stored key for one provider. */
154
+ export function clearLlmCredential(provider) {
155
+ const path = credentialsPath();
156
+ if (!existsSync(path))
157
+ return;
158
+ const existing = loadLlmCredentials();
159
+ delete existing[provider];
160
+ writeFileSync(path, `${JSON.stringify(existing, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
161
+ }
162
+ function readLayers(projectRoot) {
163
+ return {
164
+ env: process.env,
165
+ envFile: projectRoot ? loadEnvFile(projectRoot) : {},
166
+ project: projectRoot ? loadLlmProjectSettings(projectRoot) : {},
167
+ credentials: loadLlmCredentials(),
168
+ };
169
+ }
170
+ /** First non-empty value for `name` across the two environment layers. */
171
+ function fromEnvLayers(layers, name) {
172
+ const direct = layers.env[name];
173
+ if (direct)
174
+ return { value: direct, origin: 'env' };
175
+ const fromFile = layers.envFile[name];
176
+ if (fromFile)
177
+ return { value: fromFile, origin: 'env-file' };
178
+ return undefined;
179
+ }
180
+ function resolveKey(layers, provider) {
181
+ const envName = provider === 'anthropic' ? 'ANTHROPIC_API_KEY' : 'OPENAI_API_KEY';
182
+ const fromEnv = fromEnvLayers(layers, envName);
183
+ if (fromEnv)
184
+ return fromEnv;
185
+ const stored = layers.credentials[provider]?.apiKey;
186
+ if (stored)
187
+ return { value: stored, origin: 'user-credentials' };
188
+ return undefined;
189
+ }
190
+ /**
191
+ * Resolve provider settings across every layer.
192
+ *
193
+ * The chosen provider is whichever the project explicitly selects, or — absent
194
+ * that — whichever has a usable key, Anthropic first.
195
+ */
196
+ export function resolveLlmSettings(projectRoot) {
197
+ const layers = readLayers(projectRoot);
198
+ const preferred = layers.project.provider;
199
+ const order = preferred
200
+ ? [preferred, preferred === 'anthropic' ? 'openai' : 'anthropic']
201
+ : ['anthropic', 'openai'];
202
+ for (const provider of order) {
203
+ const key = resolveKey(layers, provider);
204
+ if (!key)
205
+ continue;
206
+ return {
207
+ provider,
208
+ apiKey: key.value,
209
+ model: resolveModel(layers, provider),
210
+ baseUrl: resolveBaseUrl(layers, provider),
211
+ keyOrigin: key.origin,
212
+ configured: true,
213
+ };
214
+ }
215
+ // No key anywhere. Still report the provider/model the project asks for so
216
+ // the settings screen can show what would be used once a key is supplied.
217
+ const provider = preferred;
218
+ return {
219
+ provider,
220
+ apiKey: undefined,
221
+ model: provider ? resolveModel(layers, provider) : DEFAULT_MODELS.anthropic,
222
+ baseUrl: provider ? resolveBaseUrl(layers, provider) : undefined,
223
+ keyOrigin: undefined,
224
+ configured: false,
225
+ };
226
+ }
227
+ function resolveModel(layers, provider) {
228
+ return fromEnvLayers(layers, 'MEMO_LLM_MODEL')?.value
229
+ ?? layers.project.model
230
+ ?? DEFAULT_MODELS[provider];
231
+ }
232
+ function resolveBaseUrl(layers, provider) {
233
+ const envName = provider === 'anthropic' ? 'ANTHROPIC_BASE_URL' : 'OPENAI_BASE_URL';
234
+ return fromEnvLayers(layers, envName)?.value ?? layers.project.baseUrl;
235
+ }
236
+ export function llmSettingsStatus(projectRoot) {
237
+ const resolved = resolveLlmSettings(projectRoot);
238
+ return {
239
+ configured: resolved.configured,
240
+ provider: resolved.provider,
241
+ model: resolved.model,
242
+ baseUrl: resolved.baseUrl,
243
+ keyOrigin: resolved.keyOrigin,
244
+ // An env-supplied key wins over the credentials file, so offering to
245
+ // overwrite it in the UI would be a lie — saving would change nothing.
246
+ keyEditable: !resolved.configured || resolved.keyOrigin === 'user-credentials',
247
+ };
248
+ }
249
+ //# sourceMappingURL=llm-settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-settings.js","sourceRoot":"","sources":["../../src/llm/llm-settings.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export * from './server.js';
2
+ export * from './tools.js';
3
+ export * from './model-loader.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAMA,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,9 @@
1
+ // ─── MCP Module ──────────────────────────────────────────────────────────────
2
+ //
3
+ // Model Context Protocol server exposing the MEMO model to AI coding tools
4
+ // (Cursor, Claude Code, Windsurf). Served over stdio by `memo mcp`.
5
+ // ─────────────────────────────────────────────────────────────────────────────
6
+ export * from './server.js';
7
+ export * from './tools.js';
8
+ export * from './model-loader.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,EAAE;AACF,2EAA2E;AAC3E,oEAAoE;AACpE,gFAAgF;AAEhF,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { QueryContext } from '../dhf/query-engine.js';
2
+ import type { MEMOConfig } from '../model/config.js';
3
+ export declare function findSysmlFiles(dir: string): string[];
4
+ export interface LoadedProject {
5
+ ctx: QueryContext;
6
+ config: MEMOConfig;
7
+ projectRoot: string;
8
+ fileCount: number;
9
+ }
10
+ export declare function loadProject(projectRoot: string): Promise<LoadedProject>;
11
+ //# sourceMappingURL=model-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-loader.d.ts","sourceRoot":"","sources":["../../src/mcp/model-loader.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAKrD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAepD;AAED,MAAM,WAAW,aAAa;IAC1B,GAAG,EAAE,YAAY,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACrB;AAwBD,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA+B7E"}
@@ -0,0 +1,87 @@
1
+ // ─── MCP Model Loader ────────────────────────────────────────────────────────
2
+ //
3
+ // Loads a MEMO project into a QueryContext for the MCP server.
4
+ //
5
+ // The server is long-lived and an IDE agent may call it long after the files
6
+ // changed on disk, so the model is cached with an mtime check rather than read
7
+ // once at startup — a stale answer to "what hazards exist?" is worse than the
8
+ // cost of a reload.
9
+ // ─────────────────────────────────────────────────────────────────────────────
10
+ import { readdirSync, statSync } from 'node:fs';
11
+ import { resolve } from 'node:path';
12
+ // Imported from their defining modules rather than the package index: this
13
+ // module is reached from server/persistor, which imports the package by name.
14
+ import { findConfigFile } from '../model/config-loader.js';
15
+ import { parseFiles } from '../model/parser-utils.js';
16
+ import { buildMemoModel } from '../model/builder.js';
17
+ import { loadOntologyRegistries } from '../model/ontology-loader.js';
18
+ import { validateModel } from '../validator/rule-engine.js';
19
+ import { computeCompleteness } from '../completeness/tracker.js';
20
+ import { createQueryContext } from '../dhf/query-engine.js';
21
+ import { loadAndResolveConfig } from '../server/config-resolver.js';
22
+ const SKIP_DIRS = new Set(['node_modules', '.memo', '.git', 'memo_packages']);
23
+ export function findSysmlFiles(dir) {
24
+ const files = [];
25
+ try {
26
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
27
+ const full = resolve(dir, entry.name);
28
+ if (entry.isDirectory()) {
29
+ if (!SKIP_DIRS.has(entry.name))
30
+ files.push(...findSysmlFiles(full));
31
+ }
32
+ else if (entry.name.endsWith('.sysml')) {
33
+ files.push(full);
34
+ }
35
+ }
36
+ }
37
+ catch {
38
+ // An unreadable directory is not worth failing the whole load over.
39
+ }
40
+ return files;
41
+ }
42
+ const cache = new Map();
43
+ /** Cheap staleness check: file count plus the newest mtime. */
44
+ function signatureOf(files) {
45
+ let newest = 0;
46
+ for (const f of files) {
47
+ try {
48
+ const t = statSync(f).mtimeMs;
49
+ if (t > newest)
50
+ newest = t;
51
+ }
52
+ catch {
53
+ // A file that vanished mid-scan changes the count, which is enough.
54
+ }
55
+ }
56
+ return `${files.length}:${newest}`;
57
+ }
58
+ export async function loadProject(projectRoot) {
59
+ const files = findSysmlFiles(projectRoot);
60
+ const signature = signatureOf(files);
61
+ const cached = cache.get(projectRoot);
62
+ if (cached && cached.signature === signature)
63
+ return cached.project;
64
+ const configPath = findConfigFile(projectRoot);
65
+ if (!configPath) {
66
+ throw new Error(`No MEMO project found at ${projectRoot}. Run \`memo init\` there first.`);
67
+ }
68
+ const config = await loadAndResolveConfig(configPath);
69
+ let ontologyRegistries;
70
+ try {
71
+ const loadResult = await loadOntologyRegistries(configPath);
72
+ if (loadResult.fileCount > 0)
73
+ ontologyRegistries = loadResult.registries;
74
+ }
75
+ catch {
76
+ // A project without a resolvable ontology still has a parseable model.
77
+ }
78
+ const { documents, errors: parseErrors } = await parseFiles(files, `${projectRoot}/`);
79
+ const model = buildMemoModel(documents, config, parseErrors, ontologyRegistries);
80
+ const validation = validateModel(model);
81
+ const completeness = computeCompleteness(model, validation, config);
82
+ const ctx = createQueryContext(model, validation, completeness, config);
83
+ const project = { ctx, config, projectRoot, fileCount: files.length };
84
+ cache.set(projectRoot, { project, signature });
85
+ return project;
86
+ }
87
+ //# sourceMappingURL=model-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-loader.js","sourceRoot":"","sources":["../../src/mcp/model-loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface McpServerOptions {
2
+ projectRoot: string;
3
+ /** Expose the model-editing tools. Read-only when false. */
4
+ allowWrites?: boolean;
5
+ version?: string;
6
+ }
7
+ export declare function startMcpServer(options: McpServerOptions): Promise<void>;
8
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAoCA,MAAM,WAAW,gBAAgB;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuHvE"}
@@ -0,0 +1,132 @@
1
+ // ─── MEMO MCP Server ─────────────────────────────────────────────────────────
2
+ //
3
+ // Model Context Protocol server over stdio, letting Cursor, Claude Code, and
4
+ // any other MCP client query and (optionally) edit the MEMO model.
5
+ //
6
+ // MCP over stdio is JSON-RPC 2.0 in newline-delimited JSON, so it is
7
+ // implemented directly rather than pulling in an SDK — consistent with the
8
+ // fetch-only LLM providers, and one less dependency for consumers of this
9
+ // published package.
10
+ //
11
+ // stdout carries protocol frames and nothing else. Every diagnostic goes to
12
+ // stderr; a stray console.log here corrupts the session.
13
+ // ─────────────────────────────────────────────────────────────────────────────
14
+ import { createInterface } from 'node:readline';
15
+ import { listTools, callTool } from './tools.js';
16
+ /** Protocol revisions this server implements. */
17
+ const SUPPORTED_PROTOCOL_VERSIONS = ['2025-06-18', '2025-03-26', '2024-11-05'];
18
+ const DEFAULT_PROTOCOL_VERSION = SUPPORTED_PROTOCOL_VERSIONS[0];
19
+ const SERVER_NAME = 'memo';
20
+ // JSON-RPC 2.0 reserved error codes.
21
+ const PARSE_ERROR = -32700;
22
+ const INVALID_REQUEST = -32600;
23
+ const METHOD_NOT_FOUND = -32601;
24
+ const INTERNAL_ERROR = -32603;
25
+ export function startMcpServer(options) {
26
+ const { projectRoot, allowWrites = false, version = '0.0.0' } = options;
27
+ function send(payload) {
28
+ process.stdout.write(`${JSON.stringify(payload)}\n`);
29
+ }
30
+ function respond(id, result) {
31
+ send({ jsonrpc: '2.0', id, result });
32
+ }
33
+ function respondError(id, code, message) {
34
+ send({ jsonrpc: '2.0', id, error: { code, message } });
35
+ }
36
+ async function handle(request) {
37
+ const { id, method, params } = request;
38
+ // A message with no id is a notification: act on it, never reply.
39
+ const isNotification = id === undefined || id === null;
40
+ switch (method) {
41
+ case 'initialize': {
42
+ if (isNotification)
43
+ return;
44
+ // Speak the client's revision when we know it, so a client
45
+ // pinned to an older one is not forced to downgrade us.
46
+ const requested = params?.protocolVersion;
47
+ const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.includes(requested)
48
+ ? requested
49
+ : DEFAULT_PROTOCOL_VERSION;
50
+ respond(id, {
51
+ protocolVersion,
52
+ capabilities: { tools: { listChanged: false } },
53
+ serverInfo: { name: SERVER_NAME, version },
54
+ instructions: 'MEMO models medical device architecture in SysML v2. Call memo_ontology before writing or editing any .sysml file so you use the kinds and relationship types this project actually defines, and memo_validate afterwards to check the result.',
55
+ });
56
+ return;
57
+ }
58
+ case 'notifications/initialized':
59
+ case 'notifications/cancelled':
60
+ return; // Nothing to do, and notifications take no reply.
61
+ case 'ping':
62
+ if (!isNotification)
63
+ respond(id, {});
64
+ return;
65
+ case 'tools/list':
66
+ if (isNotification)
67
+ return;
68
+ respond(id, { tools: listTools(allowWrites) });
69
+ return;
70
+ case 'tools/call': {
71
+ if (isNotification)
72
+ return;
73
+ const name = params?.name;
74
+ if (typeof name !== 'string') {
75
+ respondError(id, INVALID_REQUEST, 'tools/call requires a tool name.');
76
+ return;
77
+ }
78
+ try {
79
+ const result = await callTool(name, params?.arguments ?? {}, projectRoot, allowWrites);
80
+ respond(id, {
81
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
82
+ });
83
+ }
84
+ catch (e) {
85
+ // A failing tool is a normal result the model should see and
86
+ // recover from — not a transport-level JSON-RPC error.
87
+ respond(id, {
88
+ content: [{ type: 'text', text: e?.message ?? String(e) }],
89
+ isError: true,
90
+ });
91
+ }
92
+ return;
93
+ }
94
+ default:
95
+ if (!isNotification) {
96
+ respondError(id, METHOD_NOT_FOUND, `Unknown method "${method}".`);
97
+ }
98
+ }
99
+ }
100
+ return new Promise(resolve => {
101
+ const rl = createInterface({ input: process.stdin });
102
+ // Requests are handled one at a time. The model loader caches, so
103
+ // serialising costs little and avoids concurrent reloads of the same
104
+ // project racing each other.
105
+ let queue = Promise.resolve();
106
+ rl.on('line', line => {
107
+ const trimmed = line.trim();
108
+ if (!trimmed)
109
+ return;
110
+ let request;
111
+ try {
112
+ request = JSON.parse(trimmed);
113
+ }
114
+ catch {
115
+ send({ jsonrpc: '2.0', id: null, error: { code: PARSE_ERROR, message: 'Invalid JSON.' } });
116
+ return;
117
+ }
118
+ queue = queue.then(() => handle(request)).catch(e => {
119
+ console.error('[memo mcp] handler failed:', e);
120
+ const id = request.id;
121
+ if (id !== undefined && id !== null) {
122
+ respondError(id, INTERNAL_ERROR, e?.message ?? String(e));
123
+ }
124
+ });
125
+ });
126
+ rl.on('close', () => {
127
+ void queue.finally(() => resolve());
128
+ });
129
+ console.error(`[memo mcp] serving ${projectRoot} (${allowWrites ? 'read-write' : 'read-only'})`);
130
+ });
131
+ }
132
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { QueryContext } from '../dhf/query-engine.js';
2
+ import type { MEMOConfig } from '../model/config.js';
3
+ /** An MCP tool as advertised in `tools/list`. */
4
+ export interface McpTool {
5
+ name: string;
6
+ description: string;
7
+ inputSchema: Record<string, unknown>;
8
+ }
9
+ export interface McpToolContext {
10
+ projectRoot: string;
11
+ ctx: QueryContext;
12
+ config: MEMOConfig;
13
+ }
14
+ export type McpToolHandler = (input: Record<string, any>, context: McpToolContext) => Promise<unknown> | unknown;
15
+ export declare function listTools(allowWrites: boolean): McpTool[];
16
+ /** Run one tool against a freshly-loaded project. */
17
+ export declare function callTool(name: string, input: Record<string, any>, projectRoot: string, allowWrites: boolean): Promise<unknown>;
18
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,iDAAiD;AACjD,MAAM,WAAW,OAAO;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,cAAc;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,YAAY,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;CACtB;AAED,MAAM,MAAM,cAAc,GAAG,CACzB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1B,OAAO,EAAE,cAAc,KACtB,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;AAqNhC,wBAAgB,SAAS,CAAC,WAAW,EAAE,OAAO,GAAG,OAAO,EAAE,CAEzD;AAED,qDAAqD;AACrD,wBAAsB,QAAQ,CAC1B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1B,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,OAAO,GACrB,OAAO,CAAC,OAAO,CAAC,CAQlB"}