@dudamel/mcp-memory 2026.1008.1 → 2026.1015.1

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.
@@ -1,20 +1,567 @@
1
1
  #!/usr/bin/env node
2
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
- import { createMemoryServer } from './server.js';
4
- const apiUrl = process.env['DUDAMEL_API_URL'] ?? 'http://orchestrator:3000';
5
- const apiKey = process.env['DUDAMEL_API_KEY'];
6
- const userId = process.env['DUDAMEL_USER_ID'] ?? '';
7
- // Absent (or empty) when there is no agent in context — a user's own API key from their
8
- // terminal (Plan 52 A.1). It must reach the API as a REAL null, never as '': an empty string
9
- // would be sent as `?agent=` and read as "unknown agent", failing every call.
10
- const agentName = process.env['DUDAMEL_AGENT_NAME']?.trim() || null;
11
- // Injected by the conversational driver (Plan 43 G1) — absent for a workflow step.
12
- const conversationId = process.env['DUDAMEL_CONVERSATION_ID'];
2
+
3
+ // src/stdio-server.ts
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+
6
+ // ../common/dist/http-client.js
7
+ function parseApiErrorBody(body) {
8
+ try {
9
+ const parsed = JSON.parse(body);
10
+ if (typeof parsed !== "object" || parsed === null || !("error" in parsed))
11
+ return void 0;
12
+ const error = parsed.error;
13
+ if (typeof error === "object" && error !== null && typeof error.code === "string" && typeof error.message === "string") {
14
+ return error;
15
+ }
16
+ return void 0;
17
+ } catch {
18
+ return void 0;
19
+ }
20
+ }
21
+ var DudamelApiError = class extends Error {
22
+ status;
23
+ statusText;
24
+ body;
25
+ /** Stable machine-readable code from the API error body, or 'UNKNOWN' for non-conforming bodies. */
26
+ code;
27
+ /** Per-field validation errors, when the API provided them. */
28
+ details;
29
+ /** Extra machine-readable data (e.g. `inputsSchema` on execute validation failures). */
30
+ context;
31
+ constructor(status, statusText, body) {
32
+ const apiError = parseApiErrorBody(body);
33
+ const lines = [
34
+ apiError ? `Dudamel API error ${status} ${statusText}: ${apiError.code}: ${apiError.message}` : `Dudamel API error ${status} ${statusText}: ${body}`
35
+ ];
36
+ if (apiError?.details?.length) {
37
+ lines.push(...apiError.details.map((d) => ` - ${d.path}: ${d.message}`));
38
+ }
39
+ if (apiError?.context?.["inputsSchema"] !== void 0) {
40
+ lines.push(`Expected inputs schema: ${JSON.stringify(apiError.context["inputsSchema"])}`);
41
+ }
42
+ super(lines.join("\n"));
43
+ this.status = status;
44
+ this.statusText = statusText;
45
+ this.body = body;
46
+ this.name = "DudamelApiError";
47
+ this.code = apiError?.code ?? "UNKNOWN";
48
+ this.details = apiError?.details;
49
+ this.context = apiError?.context;
50
+ }
51
+ };
52
+ var DudamelApiClient = class {
53
+ baseUrl;
54
+ headers;
55
+ constructor(baseUrl, apiKey2, authScheme) {
56
+ this.baseUrl = baseUrl.replace(/\/+$/, "");
57
+ this.headers = {
58
+ Authorization: `${authScheme ?? "ApiKey"} ${apiKey2}`,
59
+ "Content-Type": "application/json"
60
+ };
61
+ }
62
+ async get(path, params) {
63
+ const url = this.buildUrl(path, params);
64
+ return this.request(url, { method: "GET" });
65
+ }
66
+ async post(path, body, params) {
67
+ const url = this.buildUrl(path, params);
68
+ return this.request(url, {
69
+ method: "POST",
70
+ body: body !== void 0 ? JSON.stringify(body) : void 0
71
+ });
72
+ }
73
+ async put(path, body, params) {
74
+ const url = this.buildUrl(path, params);
75
+ return this.request(url, {
76
+ method: "PUT",
77
+ body: body !== void 0 ? JSON.stringify(body) : void 0
78
+ });
79
+ }
80
+ async delete(path, params) {
81
+ const url = this.buildUrl(path, params);
82
+ return this.request(url, { method: "DELETE" });
83
+ }
84
+ buildUrl(path, params) {
85
+ const url = new URL(path, this.baseUrl);
86
+ if (params) {
87
+ for (const [key, value] of Object.entries(params)) {
88
+ if (value !== void 0 && value !== "") {
89
+ url.searchParams.set(key, value);
90
+ }
91
+ }
92
+ }
93
+ return url.toString();
94
+ }
95
+ async request(url, init) {
96
+ const response = await fetch(url, {
97
+ ...init,
98
+ headers: this.headers
99
+ });
100
+ if (!response.ok) {
101
+ const body = await response.text();
102
+ throw new DudamelApiError(response.status, response.statusText, body);
103
+ }
104
+ const text = await response.text();
105
+ if (!text) {
106
+ return void 0;
107
+ }
108
+ return JSON.parse(text);
109
+ }
110
+ };
111
+
112
+ // ../common/dist/read-only-filter.js
113
+ function withReadOnlyFilter(server2, readOnly2) {
114
+ if (!readOnly2)
115
+ return server2;
116
+ const original = server2.registerTool.bind(server2);
117
+ const filtered = (...args) => {
118
+ const config = args[1];
119
+ if (config?.annotations?.readOnlyHint === false) {
120
+ return void 0;
121
+ }
122
+ return original(...args);
123
+ };
124
+ return new Proxy(server2, {
125
+ get(target, prop, receiver) {
126
+ if (prop === "registerTool")
127
+ return filtered;
128
+ return Reflect.get(target, prop, receiver);
129
+ }
130
+ });
131
+ }
132
+
133
+ // ../common/dist/create-server.js
134
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
135
+
136
+ // ../common/dist/stdio.js
137
+ function resolveStdioContext() {
138
+ return {
139
+ apiUrl: process.env["DUDAMEL_API_URL"] ?? "http://localhost:3000",
140
+ apiKey: process.env["DUDAMEL_API_KEY"],
141
+ envRole: process.env["DUDAMEL_USER_ROLE"],
142
+ // read/write access is per agent-assignment; the spawner injects the mode.
143
+ readOnly: process.env["DUDAMEL_MCP_MODE"] === "readonly",
144
+ agentName: process.env["DUDAMEL_AGENT_NAME"]
145
+ };
146
+ }
147
+
148
+ // ../common/dist/util.js
149
+ function jsonResult(data) {
150
+ return {
151
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
152
+ };
153
+ }
154
+ function errorResult(error) {
155
+ const message = error instanceof Error ? error.message : String(error);
156
+ return {
157
+ content: [{ type: "text", text: message }],
158
+ isError: true
159
+ };
160
+ }
161
+
162
+ // src/server.ts
163
+ import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
164
+
165
+ // src/tools/memories.ts
166
+ import { z as z2 } from "zod";
167
+
168
+ // src/util.ts
169
+ import { z } from "zod";
170
+ var AGENT_NAME_FIELD = z.string().optional().describe(
171
+ 'The Dudamel agent you are acting as (e.g. "dudamel-expert"). Pass it on every call when you are one \u2014 it is what scopes memories, files and conversations to that agent. Omit it only when you are not acting as any Dudamel agent.'
172
+ );
173
+ function agentNameInput(ctx) {
174
+ return ctx.agentName === null ? { agentName: AGENT_NAME_FIELD } : {};
175
+ }
176
+ function effectiveAgent(ctx, requested) {
177
+ return ctx.agentName ?? (requested?.trim() || null);
178
+ }
179
+ function agentParams(ctx, requested) {
180
+ const agent = effectiveAgent(ctx, requested);
181
+ return agent ? { agent } : {};
182
+ }
183
+ function agentBody(ctx, requested) {
184
+ const agent = effectiveAgent(ctx, requested);
185
+ return agent ? { agentName: agent } : {};
186
+ }
187
+
188
+ // src/tools/memories.ts
189
+ var SCOPE_HEADER = "Scope of the memory \u2014 this is WHO WILL BE ABLE TO READ IT. Pick the NARROWEST that fits:\n";
190
+ var RELATIONAL_LINE = "- 'relational': you (this agent) + this one user. Nobody else. The default for a preference or fact about the person you are talking to.\n";
191
+ var PROFILE_LINE = "- 'profile': this one user, across ALL agents \u2014 every agent they talk to sees it. For facts about them that any agent should know (their role, timezone, how to reach them).\n";
192
+ var AGENT_LINE = "- 'agent': you (this agent), across ALL users \u2014 everyone who talks to you sees it. For your own working knowledge; never for something about a particular person.\n";
193
+ var ORG_LINE = "- 'org': EVERY agent and EVERY user in the organisation. The widest reach there is \u2014 only for a fact true of the whole organisation that you would be comfortable showing anyone in it.\n";
194
+ function scopeDescription(unbound) {
195
+ return SCOPE_HEADER + RELATIONAL_LINE + PROFILE_LINE + AGENT_LINE + ORG_LINE + (unbound ? "'relational' and 'agent' need agentName \u2014 without an agent they have no one to belong to. " : "") + "Ask, in order: is this about ONE person (relational/profile) or about nobody in particular (agent/org)? Then: does it really need to be visible beyond this conversation?";
196
+ }
197
+ var AGENT_SCOPES = /* @__PURE__ */ new Set(["relational", "agent"]);
198
+ function registerMemoryTools(server2, client, ctx) {
199
+ const unbound = ctx.agentName === null;
200
+ const scopes = z2.enum(["relational", "profile", "agent", "org"]);
201
+ const agentInput = agentNameInput(ctx);
202
+ server2.registerTool(
203
+ "list-memories",
204
+ {
205
+ description: "List memories relevant to the current user (summaries only, NOT full content). Returns the union of everything you know that applies here: your notes on this user, their profile, and your/org general knowledge" + (unbound ? " (without agentName: only their profile and org knowledge). " : ". ") + "Call this at the start of an interaction. You MUST call this before creating a memory. Use get-memory to read a full entry.",
206
+ inputSchema: {
207
+ q: z2.string().optional().describe("Keyword filter over summary + content (omit to list all)"),
208
+ scope: scopes.optional().describe("Filter to a single scope"),
209
+ ...agentInput
210
+ },
211
+ annotations: { readOnlyHint: true }
212
+ },
213
+ async ({ q, scope, agentName: agentName2 }) => {
214
+ try {
215
+ const params = agentParams(ctx, agentName2);
216
+ if (q) params["q"] = q;
217
+ if (scope) params["scope"] = scope;
218
+ const rows = await client.get("/api/chat/memories", params);
219
+ if (Array.isArray(rows)) {
220
+ for (const r of rows) if (r.id) ctx.seenIds.add(r.id);
221
+ }
222
+ ctx.hasListed = true;
223
+ return jsonResult(rows);
224
+ } catch (err) {
225
+ return errorResult(err);
226
+ }
227
+ }
228
+ );
229
+ server2.registerTool(
230
+ "get-memory",
231
+ {
232
+ description: "Get the full content of a single memory by id. You MUST call this before updating a memory (so you edit its real content, not blindly). Ids come from list-memories.",
233
+ inputSchema: { id: z2.string().describe("Memory id (from list-memories)"), ...agentInput },
234
+ annotations: { readOnlyHint: true }
235
+ },
236
+ async ({ id, agentName: agentName2 }) => {
237
+ try {
238
+ const memory = await client.get(
239
+ `/api/chat/memories/${id}`,
240
+ agentParams(ctx, agentName2)
241
+ );
242
+ if (memory?.id) {
243
+ ctx.fetchedIds.add(memory.id);
244
+ ctx.seenIds.add(memory.id);
245
+ }
246
+ return jsonResult(memory);
247
+ } catch (err) {
248
+ return errorResult(err);
249
+ }
250
+ }
251
+ );
252
+ server2.registerTool(
253
+ "create-memory",
254
+ {
255
+ description: "Create a NEW memory. Requires a prior list-memories call this session (to avoid duplicates \u2014 if a similar memory exists, use update-memory instead). Content can be multi-line markdown. A one-line summary is generated automatically; if the summary service is unavailable the memory is NOT saved and you should retry later.",
256
+ inputSchema: {
257
+ scope: scopes.describe(scopeDescription(unbound)),
258
+ content: z2.string().describe("Full memory text (multi-line markdown allowed)"),
259
+ ...agentInput
260
+ },
261
+ annotations: { readOnlyHint: false, destructiveHint: false }
262
+ },
263
+ async ({ scope, content, agentName: agentName2 }) => {
264
+ if (!ctx.hasListed) {
265
+ return errorResult(
266
+ new Error(
267
+ "Call list-memories first (to check for an existing memory to update instead of duplicating)."
268
+ )
269
+ );
270
+ }
271
+ if (AGENT_SCOPES.has(scope) && !effectiveAgent(ctx, agentName2)) {
272
+ return errorResult(
273
+ new Error(
274
+ `Scope '${scope}' belongs to an agent: pass agentName (the agent you are acting as), or use 'profile' / 'org'.`
275
+ )
276
+ );
277
+ }
278
+ try {
279
+ const result = await client.post("/api/chat/memories", {
280
+ ...agentBody(ctx, agentName2),
281
+ scope,
282
+ content
283
+ });
284
+ if (result?.id) ctx.seenIds.add(result.id);
285
+ return jsonResult(result);
286
+ } catch (err) {
287
+ return errorResult(err);
288
+ }
289
+ }
290
+ );
291
+ server2.registerTool(
292
+ "update-memory",
293
+ {
294
+ description: "Update an existing memory's content (its summary is regenerated). You MUST call get-memory on this id first, so you edit its real content. If the summary service is unavailable the memory is NOT updated and you should retry later.",
295
+ inputSchema: {
296
+ id: z2.string().describe("Memory id to update"),
297
+ content: z2.string().describe("New full memory text (replaces the old content)"),
298
+ ...agentInput
299
+ },
300
+ annotations: { readOnlyHint: false, destructiveHint: false }
301
+ },
302
+ async ({ id, content, agentName: agentName2 }) => {
303
+ if (!ctx.fetchedIds.has(id)) {
304
+ return errorResult(
305
+ new Error(`Call get-memory("${id}") first so you update its real content, not a guess.`)
306
+ );
307
+ }
308
+ try {
309
+ const result = await client.put(
310
+ `/api/chat/memories/${id}`,
311
+ { content },
312
+ agentParams(ctx, agentName2)
313
+ );
314
+ return jsonResult(result);
315
+ } catch (err) {
316
+ return errorResult(err);
317
+ }
318
+ }
319
+ );
320
+ server2.registerTool(
321
+ "delete-memory",
322
+ {
323
+ description: "Delete a memory by id. Requires the id to have been seen via list-memories or get-memory.",
324
+ inputSchema: { id: z2.string().describe("Memory id to delete"), ...agentInput },
325
+ annotations: { readOnlyHint: false, destructiveHint: true }
326
+ },
327
+ async ({ id, agentName: agentName2 }) => {
328
+ if (!ctx.seenIds.has(id)) {
329
+ return errorResult(
330
+ new Error(`Cannot delete ${id}: not seen in a prior list/get. Call list-memories first.`)
331
+ );
332
+ }
333
+ try {
334
+ await client.delete(`/api/chat/memories/${id}`, agentParams(ctx, agentName2));
335
+ ctx.seenIds.delete(id);
336
+ ctx.fetchedIds.delete(id);
337
+ return jsonResult({ deleted: true, id });
338
+ } catch (err) {
339
+ return errorResult(err);
340
+ }
341
+ }
342
+ );
343
+ server2.registerTool(
344
+ "mark-memory-useful",
345
+ {
346
+ description: "Mark a memory as useful now (bumps its recency ranking). Requires the id to have been seen via list-memories or get-memory.",
347
+ inputSchema: { id: z2.string().describe("Memory id to mark as useful"), ...agentInput },
348
+ annotations: { readOnlyHint: false, destructiveHint: false }
349
+ },
350
+ async ({ id, agentName: agentName2 }) => {
351
+ if (!ctx.seenIds.has(id)) {
352
+ return errorResult(
353
+ new Error(`Cannot mark ${id}: not seen in a prior list/get. Call list-memories first.`)
354
+ );
355
+ }
356
+ try {
357
+ const result = await client.post(
358
+ `/api/chat/memories/${id}/useful`,
359
+ void 0,
360
+ agentParams(ctx, agentName2)
361
+ );
362
+ return jsonResult(result);
363
+ } catch (err) {
364
+ return errorResult(err);
365
+ }
366
+ }
367
+ );
368
+ }
369
+
370
+ // src/tools/conversations.ts
371
+ import { z as z3 } from "zod";
372
+ function registerConversationTools(server2, client, ctx) {
373
+ server2.registerTool(
374
+ "list-conversations",
375
+ {
376
+ description: "List your past conversations. Scoped to the current agent when one is in context; otherwise every conversation you have had, with any agent. Returns conversation metadata including ids, timestamps, and summaries.",
377
+ inputSchema: { ...agentNameInput(ctx) },
378
+ annotations: { readOnlyHint: true }
379
+ },
380
+ async ({ agentName: agentName2 }) => {
381
+ try {
382
+ const result = await client.get("/api/chat/conversations", agentParams(ctx, agentName2));
383
+ return jsonResult(result);
384
+ } catch (err) {
385
+ return errorResult(err);
386
+ }
387
+ }
388
+ );
389
+ server2.registerTool(
390
+ "get-conversation-messages",
391
+ {
392
+ description: "Get the full message history for a specific conversation. Returns all messages in chronological order.",
393
+ inputSchema: {
394
+ conversationId: z3.string().describe("The conversation ID to retrieve messages for")
395
+ },
396
+ annotations: { readOnlyHint: true }
397
+ },
398
+ async ({ conversationId: conversationId2 }) => {
399
+ try {
400
+ const result = await client.get(`/api/chat/conversations/${conversationId2}/messages`);
401
+ return jsonResult(result);
402
+ } catch (err) {
403
+ return errorResult(err);
404
+ }
405
+ }
406
+ );
407
+ }
408
+
409
+ // src/tools/files.ts
410
+ import { z as z4 } from "zod";
411
+ function registerFileTools(server2, client, ctx) {
412
+ const agentInput = agentNameInput(ctx);
413
+ server2.registerTool(
414
+ "save-file",
415
+ {
416
+ description: "Store a file so it survives this turn. The turn workspace is wiped when the turn ends, so anything you want to keep \u2014 a report, a diff, a generated CSV \u2014 has to be saved here. Returns an id you can hand back to the user or use in a later turn. Content goes base64-encoded.",
417
+ inputSchema: {
418
+ fileName: z4.string().describe('Name for the file, e.g. "informe-q3.csv"'),
419
+ mimeType: z4.string().describe('MIME type, e.g. "text/csv" or "application/pdf"'),
420
+ contentBase64: z4.string().describe("File content, base64-encoded"),
421
+ conversationId: z4.string().optional().describe("Conversation this belongs to. Defaults to the current one."),
422
+ ...agentInput
423
+ },
424
+ annotations: { readOnlyHint: false, destructiveHint: false }
425
+ },
426
+ async ({ fileName, mimeType, contentBase64, conversationId: conversationId2, agentName: agentName2 }) => {
427
+ try {
428
+ return jsonResult(
429
+ await client.post("/api/chat/files", {
430
+ fileName,
431
+ mimeType,
432
+ contentBase64,
433
+ // The current conversation unless told otherwise, so the common case needs no
434
+ // argument and the file is findable where it was produced.
435
+ conversationId: conversationId2 ?? ctx.conversationId,
436
+ ...agentBody(ctx, agentName2)
437
+ })
438
+ );
439
+ } catch (err) {
440
+ return errorResult(err);
441
+ }
442
+ }
443
+ );
444
+ server2.registerTool(
445
+ "list-files",
446
+ {
447
+ description: "List stored files for this conversation (default) or for this agent across conversations. Returns metadata only \u2014 ids, names, types, sizes \u2014 not content.",
448
+ inputSchema: {
449
+ conversationId: z4.string().optional().describe("Conversation to list. Defaults to the current one."),
450
+ allConversations: z4.boolean().optional().describe("List everything this agent saved, not just this conversation's"),
451
+ limit: z4.number().optional().describe("Max files to return (default 50)"),
452
+ ...agentInput
453
+ },
454
+ annotations: { readOnlyHint: true }
455
+ },
456
+ async ({ conversationId: conversationId2, allConversations, limit, agentName: agentName2 }) => {
457
+ try {
458
+ const params = agentParams(ctx, agentName2);
459
+ if (!allConversations) {
460
+ const target = conversationId2 ?? ctx.conversationId;
461
+ if (target) params.conversationId = target;
462
+ }
463
+ if (limit) params.limit = String(limit);
464
+ return jsonResult(await client.get("/api/chat/files", params));
465
+ } catch (err) {
466
+ return errorResult(err);
467
+ }
468
+ }
469
+ );
470
+ server2.registerTool(
471
+ "get-file",
472
+ {
473
+ description: "Read a stored file by id. Small text files come back with their content inline; anything large or binary returns metadata only, and you should pass the id along rather than trying to read the bytes \u2014 they would not fit in your context anyway.",
474
+ inputSchema: {
475
+ id: z4.string().describe("File id from save-file or list-files")
476
+ },
477
+ annotations: { readOnlyHint: true }
478
+ },
479
+ async ({ id }) => {
480
+ try {
481
+ return jsonResult(await client.get(`/api/chat/files/${encodeURIComponent(id)}`));
482
+ } catch (err) {
483
+ return errorResult(err);
484
+ }
485
+ }
486
+ );
487
+ server2.registerTool(
488
+ "send-file",
489
+ {
490
+ description: "Hand a stored file to the user in this conversation. Use it after save-file when the user should actually receive the file \u2014 saving alone only keeps it. It is delivered right after your reply, through whatever channel you are talking on; you do not need to know which.",
491
+ inputSchema: {
492
+ id: z4.string().describe("File id from save-file or list-files")
493
+ },
494
+ annotations: { readOnlyHint: false, destructiveHint: false }
495
+ },
496
+ async ({ id }) => {
497
+ try {
498
+ return jsonResult(await client.post(`/api/chat/files/${encodeURIComponent(id)}/send`, {}));
499
+ } catch (err) {
500
+ return errorResult(err);
501
+ }
502
+ }
503
+ );
504
+ server2.registerTool(
505
+ "delete-file",
506
+ {
507
+ description: "Delete a stored file permanently. Use it for things you produced and no longer need \u2014 not for files a user uploaded, unless they asked.",
508
+ inputSchema: {
509
+ id: z4.string().describe("File id from save-file or list-files")
510
+ },
511
+ annotations: { readOnlyHint: false, destructiveHint: true }
512
+ },
513
+ async ({ id }) => {
514
+ try {
515
+ await client.delete(`/api/chat/files/${encodeURIComponent(id)}`);
516
+ return jsonResult({ deleted: true, id });
517
+ } catch (err) {
518
+ return errorResult(err);
519
+ }
520
+ }
521
+ );
522
+ }
523
+
524
+ // src/server.ts
525
+ function createMemoryServer(config) {
526
+ const server2 = new McpServer2(
527
+ { name: "dudamel-memory", version: "0.1.0" },
528
+ {
529
+ capabilities: { tools: {} },
530
+ instructions: "Dudamel Memory MCP \u2014 query and manage your memories and past conversations. Always search before creating or updating a memory to avoid duplicates. Write tools require a prior search in the same session. Files: the turn workspace is wiped when the turn ends, so use save-file for anything that must outlive it."
531
+ }
532
+ );
533
+ const client = new DudamelApiClient(config.apiUrl, config.apiKey, config.authScheme);
534
+ const ctx = {
535
+ userId: config.userId,
536
+ agentName: config.agentName,
537
+ seenIds: /* @__PURE__ */ new Set(),
538
+ fetchedIds: /* @__PURE__ */ new Set(),
539
+ hasListed: false,
540
+ conversationId: config.conversationId
541
+ };
542
+ const host = withReadOnlyFilter(server2, config.readOnly ?? false);
543
+ registerMemoryTools(host, client, ctx);
544
+ registerConversationTools(host, client, ctx);
545
+ registerFileTools(host, client, ctx);
546
+ return server2;
547
+ }
548
+
549
+ // src/stdio-server.ts
550
+ var { apiUrl, apiKey, readOnly, agentName: rawAgentName } = resolveStdioContext();
551
+ var userId = process.env["DUDAMEL_USER_ID"] ?? "";
552
+ var agentName = rawAgentName?.trim() || null;
553
+ var conversationId = process.env["DUDAMEL_CONVERSATION_ID"];
13
554
  if (!apiKey) {
14
- process.stderr.write('Error: DUDAMEL_API_KEY environment variable is required.\n');
15
- process.exit(1);
555
+ process.stderr.write("Error: DUDAMEL_API_KEY environment variable is required.\n");
556
+ process.exit(1);
16
557
  }
17
- const server = createMemoryServer({ apiUrl, apiKey, userId, agentName, conversationId });
18
- const transport = new StdioServerTransport();
558
+ var server = createMemoryServer({
559
+ apiUrl,
560
+ apiKey,
561
+ readOnly,
562
+ userId,
563
+ agentName,
564
+ conversationId
565
+ });
566
+ var transport = new StdioServerTransport();
19
567
  await server.connect(transport);
20
- //# sourceMappingURL=stdio-server.js.map
@@ -1,5 +1,5 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import type { DudamelApiClient } from '../http-client.js';
2
+ import type { DudamelApiClient } from '@dudamel/mcp-common';
3
3
  import type { ToolContext } from '../types.js';
4
4
  export declare function registerConversationTools(server: McpServer, client: DudamelApiClient, ctx: ToolContext): void;
5
5
  //# sourceMappingURL=conversations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"conversations.d.ts","sourceRoot":"","sources":["../../src/tools/conversations.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,gBAAgB,EACxB,GAAG,EAAE,WAAW,GACf,IAAI,CAyCN"}
1
+ {"version":3,"file":"conversations.d.ts","sourceRoot":"","sources":["../../src/tools/conversations.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,gBAAgB,EACxB,GAAG,EAAE,WAAW,GACf,IAAI,CAyCN"}
@@ -1,8 +1,8 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import type { DudamelApiClient } from '../http-client.js';
2
+ import type { DudamelApiClient } from '@dudamel/mcp-common';
3
3
  import type { ToolContext } from '../types.js';
4
4
  /**
5
- * File tools (Plan 43 Etapa 5, Decisión 3).
5
+ * File tools.
6
6
  *
7
7
  * The registry exists so an agent can hand something back and pick it up later: files live
8
8
  * outside the turn's workspace, which is still wiped after every turn. Saving is explicit —
@@ -1 +1 @@
1
- {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/tools/files.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,gBAAgB,EACxB,GAAG,EAAE,WAAW,GACf,IAAI,CA4IN"}
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/tools/files.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,gBAAgB,EACxB,GAAG,EAAE,WAAW,GACf,IAAI,CA4IN"}
@@ -1,5 +1,5 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import type { DudamelApiClient } from '../http-client.js';
2
+ import type { DudamelApiClient } from '@dudamel/mcp-common';
3
3
  import type { ToolContext } from '../types.js';
4
4
  export declare function registerMemoryTools(server: McpServer, client: DudamelApiClient, ctx: ToolContext): void;
5
5
  //# sourceMappingURL=memories.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"memories.d.ts","sourceRoot":"","sources":["../../src/tools/memories.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA4D/C,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,gBAAgB,EACxB,GAAG,EAAE,WAAW,GACf,IAAI,CAgNN"}
1
+ {"version":3,"file":"memories.d.ts","sourceRoot":"","sources":["../../src/tools/memories.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA4D/C,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,gBAAgB,EACxB,GAAG,EAAE,WAAW,GACf,IAAI,CAgNN"}
package/dist/types.d.ts CHANGED
@@ -2,10 +2,12 @@ export interface McpMemoryConfig {
2
2
  apiUrl: string;
3
3
  apiKey: string;
4
4
  authScheme?: 'ApiKey' | 'Bearer';
5
+ /** From `DUDAMEL_MCP_MODE=readonly` — hides the write tools. */
6
+ readOnly?: boolean;
5
7
  userId: string;
6
8
  /** null when no agent is in context (a user API key) — the agent-less half of the 2×2. */
7
9
  agentName: string | null;
8
- /** Conversation this MCP serves (Plan 43 G1 env) — scopes file tools by default. */
10
+ /** Conversation this MCP serves (from the env) — scopes file tools by default. */
9
11
  conversationId?: string;
10
12
  }
11
13
  export interface ToolContext {
@@ -20,7 +22,7 @@ export interface ToolContext {
20
22
  /** Whether list-memories/search ran this session — gates create-memory. */
21
23
  hasListed: boolean;
22
24
  /**
23
- * The conversation this MCP is serving (Plan 43 G1 injects it into the env).
25
+ * The conversation this MCP is serving (the conversational driver injects it into the env).
24
26
  *
25
27
  * It is what makes `save-file` need no arguments in the common case: a file lands in the
26
28
  * conversation that produced it, so a later turn finds it with `list-files` and no id to
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,0FAA0F;IAC1F,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,oFAAoF;IACpF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf;qFACiF;IACjF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,6EAA6E;IAC7E,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,+EAA+E;IAC/E,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB,2EAA2E;IAC3E,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACjC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,0FAA0F;IAC1F,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,kFAAkF;IAClF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf;qFACiF;IACjF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,6EAA6E;IAC7E,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,+EAA+E;IAC/E,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB,2EAA2E;IAC3E,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB"}
package/dist/util.d.ts CHANGED
@@ -29,18 +29,5 @@ export declare function agentParams(ctx: ToolContext, requested?: string): Recor
29
29
  export declare function agentBody(ctx: ToolContext, requested?: string): {
30
30
  agentName?: string;
31
31
  };
32
- export declare function jsonResult(data: unknown): {
33
- content: {
34
- type: "text";
35
- text: string;
36
- }[];
37
- };
38
- export declare function errorResult(error: unknown): {
39
- content: {
40
- type: "text";
41
- text: string;
42
- }[];
43
- isError: boolean;
44
- };
45
- export {};
32
+ export { jsonResult, errorResult } from '@dudamel/mcp-common';
46
33
  //# sourceMappingURL=util.d.ts.map