@justmpm/memory 0.1.2 → 0.2.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.
package/src/index.ts CHANGED
@@ -1,692 +1,645 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Memory MCP Server - Sistema de memória persistente para subagents
5
- *
6
- * Permite que subagents salvem e recuperem aprendizados entre sessões.
7
- * Cada agent tem sua própria memória, armazenada em .claude/agent-memory/<agent-name>/MEMORY.md
8
- *
9
- * @see https://modelcontextprotocol.io/
10
- */
11
-
12
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
13
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
14
- import {
15
- CallToolRequestSchema,
16
- ListToolsRequestSchema,
17
- } from "@modelcontextprotocol/sdk/types.js";
18
- import {
19
- readFileSync,
20
- writeFileSync,
21
- existsSync,
22
- mkdirSync,
23
- readdirSync,
24
- } from "fs";
25
- import { join, dirname } from "path";
26
- import { fileURLToPath } from "url";
27
-
28
- // ═══════════════════════════════════════════════════════════════════════════
29
- // VERSÃO DO PROJETO
30
- // ═══════════════════════════════════════════════════════════════════════════
31
-
32
- /**
33
- * Lê a versão do package.json dinamicamente
34
- */
35
- function getPackageVersion(): string {
36
- try {
37
- const __filename = fileURLToPath(import.meta.url);
38
- const __dirname = dirname(__filename);
39
- const packagePath = join(__dirname, "..", "package.json");
40
- const packageJson = JSON.parse(readFileSync(packagePath, "utf-8"));
41
- return packageJson.version || "0.1.0";
42
- } catch {
43
- return "0.1.0";
44
- }
45
- }
46
-
47
- const PACKAGE_VERSION = getPackageVersion();
48
-
49
- // ═══════════════════════════════════════════════════════════════════════════
50
- // FUNÇÕES UTILITÁRIAS
51
- // ═══════════════════════════════════════════════════════════════════════════
52
-
53
- /**
54
- * Normaliza nome do agent para usar como nome de pasta
55
- * Ex: "Sentinel" → "sentinel", "QA-Tester" → "qa-tester"
56
- */
57
- function normalizeAgentName(agentName: string): string {
58
- return agentName
59
- .toLowerCase()
60
- .replace(/[^a-z0-9]+/g, "-")
61
- .replace(/^-|-$/g, "");
62
- }
63
-
64
- /**
65
- * Retorna o caminho do diretório de memória para um agent
66
- */
67
- function getAgentMemoryDir(agentName: string): string {
68
- const normalizedName = normalizeAgentName(agentName);
69
- return join(process.cwd(), ".claude", "agent-memory", normalizedName);
70
- }
71
-
72
- /**
73
- * Retorna o caminho do arquivo MEMORY.md de um agent
74
- */
75
- function getAgentMemoryPath(agentName: string): string {
76
- return join(getAgentMemoryDir(agentName), "MEMORY.md");
77
- }
78
-
79
- /**
80
- * Garante que o diretório existe
81
- */
82
- function ensureAgentMemoryDir(agentName: string): void {
83
- const dir = getAgentMemoryDir(agentName);
84
- if (!existsSync(dir)) {
85
- mkdirSync(dir, { recursive: true });
86
- }
87
- }
88
-
89
- /**
90
- * Formata timestamp para uso em entradas de memória
91
- */
92
- function formatTimestamp(): string {
93
- const now = new Date();
94
- return now.toISOString().replace("T", " ").slice(0, 19);
95
- }
96
-
97
- /**
98
- * Limita memória a ~200 linhas (últimas entradas)
99
- */
100
- function limitMemoryLines(content: string, maxLines = 200): string {
101
- const lines = content.split("\n");
102
- if (lines.length <= maxLines) return content;
103
- const keepCount = Math.floor(maxLines * 0.8); // 160 linhas
104
- const removed = lines.length - keepCount;
105
- return `# Memória (últimas ${keepCount} de ${lines.length} linhas)
106
-
107
- [... ${removed} linhas anteriores removidas ...]
108
-
109
- ${lines.slice(-keepCount).join("\n")}`;
110
- }
111
-
112
- /**
113
- * Lista todos os agents que têm memória
114
- */
115
- function listAgentsWithMemory(): string[] {
116
- const memoryRoot = join(process.cwd(), ".claude", "agent-memory");
117
- if (!existsSync(memoryRoot)) return [];
118
-
119
- try {
120
- return readdirSync(memoryRoot).filter((name) => {
121
- const memoryPath = join(memoryRoot, name, "MEMORY.md");
122
- return existsSync(memoryPath);
123
- });
124
- } catch {
125
- return [];
126
- }
127
- }
128
-
129
- // ═══════════════════════════════════════════════════════════════════════════
130
- // HANDLERS DAS OPERAÇÕES
131
- // ═══════════════════════════════════════════════════════════════════════════
132
-
133
- /**
134
- * Handler: read - Lê a memória do agent atual
135
- */
136
- async function handleRead(agentName: string): Promise<string> {
137
- const memoryPath = getAgentMemoryPath(agentName);
138
-
139
- if (!existsSync(memoryPath)) {
140
- return `📝 Memória vazia para agent "${agentName}".
141
-
142
- Use \`command="append", entry="..."\` para criar a primeira entrada.`;
143
- }
144
-
145
- try {
146
- const content = readFileSync(memoryPath, "utf-8");
147
- const lines = content.split("\n").length;
148
- return `📝 Memória de "${agentName}" (${lines} linhas):
149
-
150
- ─────────────────────────────────────────────────────────────────
151
-
152
- ${content}`;
153
- } catch (error) {
154
- return `❌ Erro ao ler memória: ${(error as Error).message}`;
155
- }
156
- }
157
-
158
- /**
159
- * Handler: write - Substitui toda a memória
160
- */
161
- async function handleWrite(
162
- agentName: string,
163
- content: string
164
- ): Promise<string> {
165
- try {
166
- ensureAgentMemoryDir(agentName);
167
- const limited = limitMemoryLines(content);
168
- writeFileSync(getAgentMemoryPath(agentName), limited, "utf-8");
169
-
170
- const lines = limited.split("\n").length;
171
- return `✅ Memória de "${agentName}" atualizada (${lines} linhas).
172
-
173
- ${lines >= 200 ? "⚠️ Memória atingiu o limite de 200 linhas." : ""}`;
174
- } catch (error) {
175
- return `❌ Erro ao escrever memória: ${(error as Error).message}`;
176
- }
177
- }
178
-
179
- /**
180
- * Handler: append - Adiciona uma entrada no final
181
- */
182
- async function handleAppend(
183
- agentName: string,
184
- entry: string
185
- ): Promise<string> {
186
- try {
187
- ensureAgentMemoryDir(agentName);
188
- const memoryPath = getAgentMemoryPath(agentName);
189
-
190
- // Lê conteúdo existente
191
- let existingContent = "";
192
- if (existsSync(memoryPath)) {
193
- existingContent = readFileSync(memoryPath, "utf-8");
194
- }
195
-
196
- // Adiciona nova entrada com timestamp
197
- const timestamp = formatTimestamp();
198
- const newEntry = `\n## [${timestamp}]\n${entry}\n`;
199
- const newContent = existingContent + newEntry;
200
-
201
- // Salva com limite
202
- const limited = limitMemoryLines(newContent);
203
- writeFileSync(memoryPath, limited, "utf-8");
204
-
205
- return `✅ Entrada adicionada à memória de "${agentName}".
206
-
207
- **Timestamp:** ${timestamp}
208
- **Entry:** ${entry.slice(0, 100)}${entry.length > 100 ? "..." : ""}`;
209
- } catch (error) {
210
- return `❌ Erro ao adicionar entrada: ${(error as Error).message}`;
211
- }
212
- }
213
-
214
- /**
215
- * Handler: search - Busca texto na memória
216
- */
217
- async function handleSearch(
218
- agentName: string,
219
- query: string
220
- ): Promise<string> {
221
- const memoryPath = getAgentMemoryPath(agentName);
222
-
223
- if (!existsSync(memoryPath)) {
224
- return `📝 Memória vazia para agent "${agentName}".`;
225
- }
226
-
227
- try {
228
- const content = readFileSync(memoryPath, "utf-8");
229
- const lines = content.split("\n");
230
- const queryLower = query.toLowerCase();
231
-
232
- const matches = lines
233
- .map((line, idx) => ({ line, idx }))
234
- .filter(({ line }) => line.toLowerCase().includes(queryLower));
235
-
236
- if (matches.length === 0) {
237
- return `📝 Nenhuma ocorrência de "${query}" encontrada na memória de "${agentName}".`;
238
- }
239
-
240
- const results = matches
241
- .slice(0, 20) // Máximo 20 resultados
242
- .map(({ line, idx }) => ` L${idx + 1}: ${line}`)
243
- .join("\n");
244
-
245
- const more = matches.length > 20 ? `\n... e mais ${matches.length - 20} ocorrências` : "";
246
-
247
- return `📝 Busca por "${query}" em "${agentName}" (${matches.length} ocorrências):
248
-
249
- ${results}${more}`;
250
- } catch (error) {
251
- return `❌ Erro ao buscar: ${(error as Error).message}`;
252
- }
253
- }
254
-
255
- /**
256
- * Handler: list - Lista todos os agents com memória
257
- */
258
- async function handleList(): Promise<string> {
259
- const agents = listAgentsWithMemory();
260
-
261
- if (agents.length === 0) {
262
- return `📝 Nenhum agent com memória neste projeto.
263
-
264
- Diretório: .claude/agent-memory/`;
265
- }
266
-
267
- const agentList = agents
268
- .map((name) => {
269
- const path = join(".claude", "agent-memory", name, "MEMORY.md");
270
- let lines = 0;
271
- try {
272
- lines = readFileSync(join(process.cwd(), path), "utf-8").split("\n").length;
273
- } catch {}
274
- return ` • ${name} (${lines} linhas)`;
275
- })
276
- .join("\n");
277
-
278
- return `📝 Agents com memória neste projeto (${agents.length}):
279
-
280
- ${agentList}
281
-
282
- Use o comando "read" de cada agent para ver o conteúdo.`;
283
- }
284
-
285
- // ═══════════════════════════════════════════════════════════════════════════
286
- // SERVIDOR MCP
287
- // ═══════════════════════════════════════════════════════════════════════════
288
-
289
- // Cria o servidor MCP
290
- const server = new Server(
291
- {
292
- name: "@justmpm/memory",
293
- version: PACKAGE_VERSION,
294
- },
295
- {
296
- capabilities: {
297
- tools: {},
298
- },
299
- }
300
- );
301
-
302
- /**
303
- * Handler: ListTools - Lista todas as tools disponíveis
304
- */
305
- server.setRequestHandler(ListToolsRequestSchema, async () => {
306
- return {
307
- tools: [
308
- {
309
- name: "memory",
310
- description: `
311
- Gerencia memória persistente para subagents entre sessões.
312
-
313
- **PROPÓSITO:** Permitir que agents salvem e recuperem aprendizados específicos do projeto entre diferentes sessões de trabalho. Cada agent mantém sua própria memória isolada.
314
-
315
- **LOCALIZAÇÃO:** .claude/agent-memory/<agent-name>/MEMORY.md (nome do agent é normalizado: lowercase, hífens para espaços)
316
-
317
- **LIMITE:** Máximo 200 linhas (quando excedido, mantém as últimas 160 entradas + cabeçalho de alerta)
318
-
319
- ═══════════════════════════════════════════════════════════════
320
- COMANDOS DISPONÍVEIS:
321
- ═══════════════════════════════════════════════════════════════
322
-
323
- 1. **read** - a memória de um agent
324
- Uso: Carregar contexto anterior ao iniciar uma sessão
325
- - agent: (opcional) Nome do agent. Se não fornecido, usa "unknown"
326
- - Retorna: Conteúdo completo do MEMORY.md com contagem de linhas
327
- - Se não existir: Retorna mensagem de memória vazia com sugestão
328
-
329
- 2. **write** - Substitui TODO o conteúdo da memória
330
- Uso: Reorganizar, limpar ou reconstruir memória do zero
331
- - agent: (opcional) Nome do agent
332
- - content: (OBRIGATÓRIO) Novo conteúdo completo em markdown
333
- - Aplica: Limite de 200 linhas automaticamente
334
- - Retorna: Confirmação + contagem de linhas
335
-
336
- 3. **append** - Adiciona entrada ao final (COM TIMESTAMP)
337
- Uso: Salvar aprendizados incrementais sem perder histórico
338
- - agent: (opcional) Nome do agent
339
- - entry: (OBRIGATÓRIO) Texto a adicionar
340
- - Formato automático: "## [YYYY-MM-DD HH:MM:SS]\\n<entry>"
341
- - Aplica: Limite de 200 linhas automaticamente
342
- - Retorna: Timestamp + preview da entrada
343
-
344
- 4. **search** - Busca termo específico na memória
345
- Uso: Encontrar informações rápidas sem ler tudo
346
- - agent: (opcional) Nome do agent
347
- - query: (OBRIGATÓRIO) Termo de busca (case-insensitive)
348
- - Retorna: Máximo 20 ocorrências com número da linha
349
- - Se não encontrado: Mensagem clara de "nenhuma ocorrência"
350
-
351
- 5. **list** - Lista todos os agents com memória no projeto
352
- Uso: Descobrir quais agents já usaram memória neste projeto
353
- - Sem parâmetros adicionais
354
- - Retorna: Lista com nome de cada agent + contagem de linhas
355
-
356
- ═══════════════════════════════════════════════════════════════
357
- QUANDO USAR CADA COMANDO:
358
- ═══════════════════════════════════════════════════════════════
359
-
360
- **USE READ:**
361
- - Ao INICIAR uma sessão de trabalho
362
- - Quando precisar entender o contexto anterior
363
- - Antes de fazer decisões baseadas em memória passada
364
-
365
- ✅ **USE APPEND:**
366
- - Ao APRENDER algo novo e importante
367
- - Ao descobrir padrões no código
368
- - Ao encontrar bugs recorrentes
369
- - Ao tomar decisões arquiteturais
370
- - Para manter cronologia de descobertas
371
-
372
- **USE WRITE:**
373
- - Quando memória estiver muito grande (perto de 200 linhas)
374
- - Para reorganizar e consolidar informações
375
- - Para remover entradas obsoletas
376
- - Para reestruturar seções (Padrões, Decisões, Bugs)
377
-
378
- ✅ **USE SEARCH:**
379
- - Para buscar informações específicas rapidamente
380
- - Para verificar se algo já foi documentado
381
- - Para encontrar padrões específicos (ex: "Zod", "TypeScript")
382
-
383
- **USE LIST:**
384
- - Para descobrir agents existentes no projeto
385
- - Para ver quantos agents usaram memória
386
- - Ao iniciar trabalho em projeto desconhecido
387
-
388
- ═══════════════════════════════════════════════════════════════
389
- EXEMPLOS DE USO:
390
- ═══════════════════════════════════════════════════════════════
391
-
392
- Exemplo 1 - Iniciar sessão e carregar memória:
393
- { "command": "read", "agent": "sentinel" }
394
-
395
- Exemplo 2 - Salvar padrão descoberto:
396
- { "command": "append", "agent": "sentinel", "entry": "Padrão: Sempre use Zod para validar inputs do usuário em todos os componentes de formulário" }
397
-
398
- Exemplo 3 - Salvar decisão arquitetural:
399
- { "command": "append", "agent": "nexus", "entry": "Decisão: Usar Zustand para estado global (mais leve que Redux) - Projeto tem até 5 stores independentes" }
400
-
401
- Exemplo 4 - Buscar informações sobre Zod:
402
- { "command": "search", "agent": "sentinel", "query": "Zod" }
403
-
404
- Exemplo 5 - Reorganizar memória grande:
405
- { "command": "write", "agent": "sentinel", "content": "# Memória do Sentinel\\n\\n## Padrões\\n- Use Zod para validação\\n\\n## Bugs\\n- Bug XYZ: ocorre quando..." }
406
-
407
- Exemplo 6 - Listar todos os agents:
408
- { "command": "list" }
409
-
410
- ═══════════════════════════════════════════════════════════════
411
- BOAS PRÁTICAS (O QUE SALVAR):
412
- ═══════════════════════════════════════════════════════════════
413
-
414
- **SEMPRE salve:**
415
- - Padrões de código descobertos (ex: "Sempre use 2 espaços de indentação em TSX")
416
- - Decisões arquiteturais (ex: "Escolhemos Firestore em vez de PostgreSQL porque...")
417
- - Bugs recorrentes (ex: "Erro X acontece quando...")
418
- - Soluções para problemas específicos (ex: "Para resolver problema Y, use...")
419
- - Configurações importantes (ex: "Firebase Auth usa Google Sign-In")
420
- - Preferências do usuário (ex: "Matheus prefere estilos inline para componentes simples")
421
-
422
- **Use markdown organizado:**
423
- ## Padrões - Padrões de código e convenções
424
- ## Decisões - Escolhas arquiteturais e trade-offs
425
- ## Bugs - Problemas conhecidos e workarounds
426
- ## Configurações - Configs e setup específicos
427
- ## Preferências - Preferências pessoais do usuário
428
-
429
- **Adicione contexto:**
430
- - "No projeto X, use Y..." (especifica o projeto)
431
- - "Ao trabalhar com componente Z..." (especifica o contexto)
432
- - "Quando acontecer erro Y..." (especifica a condição)
433
-
434
- ═══════════════════════════════════════════════════════════════
435
- BOAS PRÁTICAS (O QUE NÃO SALVAR):
436
- ═══════════════════════════════════════════════════════════════
437
-
438
- ❌ **NÃO salve:**
439
- - Coisas triviais (ex: "Hoje está chovendo")
440
- - Informações que mudam frequentemente (ex: "Tem 3 arquivos na pasta")
441
- - Coisas que são óbvias (ex: "O código precisa compilar")
442
- - Informações duplicadas
443
- - Logs de conversação
444
- - Erros temporários que já foram resolvidos
445
-
446
- ❌ **NÃO repita:**
447
- - Se um padrão já está salvo, não salve novamente
448
- - Use search para verificar antes de salvar
449
-
450
- ═══════════════════════════════════════════════════════════════
451
- IMPORTANTE:
452
- ═══════════════════════════════════════════════════════════════
453
-
454
- • O nome do agent é NORMALIZADO automaticamente:
455
- - "Sentinel" → "sentinel"
456
- - "QA-Tester" "qa-tester"
457
- - "My Agent" → "my-agent"
458
-
459
- O parâmetro 'agent' é OPCIONAL:
460
- - Se não fornecido, usa "unknown" como nome padrão
461
- - É recomendável SEMPRE fornecer o nome do agent atual
462
-
463
- • Memória é específica por projeto:
464
- - Cada projeto tem sua própria pasta .claude/agent-memory/
465
- - Memórias de diferentes projetos não se misturam
466
-
467
- • Quando a memória atinge 200 linhas:
468
- - As primeiras entradas mais antigas são removidas automaticamente
469
- - Mantém as últimas 160 linhas mais recentes
470
- - Adiciona cabeçalho informando sobre a limpeza
471
- - Use write para reorganizar se isso acontecer com frequência
472
- `.trim(),
473
- inputSchema: {
474
- type: "object",
475
- properties: {
476
- command: {
477
- type: "string",
478
- enum: ["read", "write", "append", "search", "list"],
479
- description: "Comando a executar. 'read' carrega memória, 'write' substitui tudo, 'append' adiciona entrada, 'search' busca termo, 'list' mostra agents.",
480
- },
481
- agent: {
482
- type: "string",
483
- description: 'Nome do agent (opcional, usa "unknown" se não fornecido). O nome será normalizado (lowercase, hífens). Ex: "Sentinel", "QA-Tester", "My Agent"',
484
- },
485
- content: {
486
- type: "string",
487
- description: 'Conteúdo completo em markdown para substituir a memória existente (OBRIGATÓRIO para "write"). Use para reorganizar, limpar ou reconstruir memória do zero.',
488
- },
489
- entry: {
490
- type: "string",
491
- description: 'Texto da entrada a adicionar no final da memória (OBRIGATÓRIO para "append"). Será prefixado automaticamente com timestamp "## [YYYY-MM-DD HH:MM:SS]". Use para salvar aprendizados incrementais.',
492
- },
493
- query: {
494
- type: "string",
495
- description: 'Termo ou padrão de busca case-insensitive (OBRIGATÓRIO para "search"). Retorna até 20 ocorrências com número da linha. Ex: "Zod", "bug", "Firebase"',
496
- },
497
- },
498
- required: ["command"],
499
- },
500
- },
501
- ],
502
- };
503
- });
504
-
505
- /**
506
- * Handler: CallTool - Executa uma tool
507
- */
508
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
509
- const { name, arguments: args } = request.params;
510
-
511
- if (name !== "memory") {
512
- throw new Error(`Tool desconhecida: ${name}`);
513
- }
514
-
515
- const command = args?.command as string;
516
- const agentName = (args?.agent as string) || "unknown";
517
-
518
- // Valida comando
519
- const validCommands = ["read", "write", "append", "search", "list"];
520
- if (!validCommands.includes(command)) {
521
- return {
522
- content: [
523
- {
524
- type: "text",
525
- text: `❌ ERRO: Comando desconhecido "${command}"
526
-
527
- COMANDOS DISPONÍVEIS:
528
-
529
- 1. read - Lê a memória de um agent
530
- 2. write - Substitui todo o conteúdo da memória
531
- 3. append - Adiciona uma entrada ao final (com timestamp)
532
- 4. search - Busca um termo na memória
533
- 5. list - Lista todos os agents com memória no projeto
534
-
535
- PARA MAIS DETALHES, CONSULTE A DESCRIÇÃO DA TOOL "memory"
536
- `,
537
- },
538
- ],
539
- };
540
- }
541
-
542
- // Executa comando específico
543
- let result: string;
544
-
545
- switch (command) {
546
- case "read": {
547
- result = await handleRead(agentName);
548
- break;
549
- }
550
-
551
- case "write": {
552
- const content = args?.content as string;
553
- if (!content) {
554
- result = `❌ ERRO: O parâmetro "content" é OBRIGATÓRIO para o comando "write".
555
-
556
- EXEMPLO DE USO CORRETO:
557
- {
558
- "command": "write",
559
- "agent": "sentinel",
560
- "content": "# Memória do Sentinel\\n\\n## Padrões\\n- Sempre use Zod para validação\\n\\n## Bugs\\n- Bug XYZ ocorre quando..."
561
- }
562
-
563
- DICA: Use "write" para reorganizar memória grande ou reconstruir do zero.
564
- Para adicionar uma entrada preservando o histórico, use "append".
565
- `;
566
- break;
567
- }
568
- result = await handleWrite(agentName, content);
569
- break;
570
- }
571
-
572
- case "append": {
573
- const entry = args?.entry as string;
574
- if (!entry) {
575
- result = `❌ ERRO: O parâmetro "entry" é OBRIGATÓRIO para o comando "append".
576
-
577
- EXEMPLO DE USO CORRETO:
578
- {
579
- "command": "append",
580
- "agent": "sentinel",
581
- "entry": "Padrão descoberto: Sempre use Zod para validar inputs do usuário em todos os componentes de formulário"
582
- }
583
-
584
- DICA: Use "append" para salvar aprendizados incrementais. O timestamp é adicionado automaticamente no formato:
585
- ## [2026-02-09 12:34:56]
586
- Sua entrada aqui
587
-
588
- Para salvar informações mais longas ou estruturadas, considere usar "write".
589
- `;
590
- break;
591
- }
592
- result = await handleAppend(agentName, entry);
593
- break;
594
- }
595
-
596
- case "search": {
597
- const query = args?.query as string;
598
- if (!query) {
599
- result = `❌ ERRO: O parâmetro "query" é OBRIGATÓRIO para o comando "search".
600
-
601
- EXEMPLO DE USO CORRETO:
602
- {
603
- "command": "search",
604
- "agent": "sentinel",
605
- "query": "Zod"
606
- }
607
-
608
- DICA: A busca é case-insensitive e retorna até 20 ocorrências com o número da linha.
609
- Exemplos de busca úteis: "padrão", "bug", "TypeScript", "Firebase", "erro"
610
- `;
611
- break;
612
- }
613
- result = await handleSearch(agentName, query);
614
- break;
615
- }
616
-
617
- case "list": {
618
- result = await handleList();
619
- break;
620
- }
621
-
622
- default:
623
- result = `❌ Comando desconhecido: ${command}`;
624
- }
625
-
626
- return {
627
- content: [
628
- {
629
- type: "text",
630
- text: result,
631
- },
632
- ],
633
- };
634
- });
635
-
636
- // ═══════════════════════════════════════════════════════════════════════════
637
- // INICIALIZAÇÃO
638
- // ═══════════════════════════════════════════════════════════════════════════
639
-
640
- async function main() {
641
- // Verifica argumentos de linha de comando
642
- const args = process.argv.slice(2);
643
-
644
- // --version
645
- if (args.includes("--version") || args.includes("-v")) {
646
- console.log(`@justmpm/memory v${PACKAGE_VERSION}`);
647
- process.exit(0);
648
- }
649
-
650
- // --help
651
- if (args.includes("--help") || args.includes("-h")) {
652
- console.log(`
653
- @justmpm/memory - MCP Server para gerenciar memória persistente de subagents
654
-
655
- Versão: ${PACKAGE_VERSION}
656
-
657
- USO:
658
- memory Inicia o servidor MCP (comunicação via stdio)
659
- memory --version Mostra a versão do pacote
660
- memory --help Mostra esta mensagem de ajuda
661
-
662
- COMANDOS MCP:
663
- - read Lê a memória de um agent
664
- - write Substitui toda a memória
665
- - append Adiciona uma entrada ao final
666
- - search Busca texto na memória
667
- - list Lista todos os agents com memória
668
-
669
- DOCUMENTAÇÃO:
670
- Para mais detalhes, consulte:
671
- - CLAUDE.md (Documentação para IAs)
672
- - AGENTS.md (Guia para subagents)
673
- - README.md (Documentação principal)
674
-
675
- REPOSITÓRIO:
676
- https://github.com/justmpm/memory-mcp
677
-
678
- LICENÇA:
679
- MIT © Koda AI Studio
680
- `);
681
- process.exit(0);
682
- }
683
-
684
- const transport = new StdioServerTransport();
685
- await server.connect(transport);
686
- // Não faz log aqui pois o servidor se comunica via stdio
687
- }
688
-
689
- main().catch((error) => {
690
- console.error("Erro fatal ao iniciar servidor:", error);
691
- process.exit(1);
692
- });
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Memory MCP Server - Sistema de memória persistente para subagents
5
+ *
6
+ * Permite que subagents salvem e recuperem aprendizados entre sessões.
7
+ * Cada agent tem sua própria memória, armazenada em .claude/agent-memory/<agent-name>/MEMORY.md
8
+ *
9
+ * @see https://modelcontextprotocol.io/
10
+ */
11
+
12
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
13
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
14
+ import {
15
+ CallToolRequestSchema,
16
+ ListToolsRequestSchema,
17
+ } from "@modelcontextprotocol/sdk/types.js";
18
+ import {
19
+ readFileSync,
20
+ writeFileSync,
21
+ existsSync,
22
+ mkdirSync,
23
+ readdirSync,
24
+ } from "fs";
25
+ import { join, dirname } from "path";
26
+ import { fileURLToPath } from "url";
27
+
28
+ // ═══════════════════════════════════════════════════════════════════════════
29
+ // VERSÃO DO PROJETO
30
+ // ═══════════════════════════════════════════════════════════════════════════
31
+
32
+ /**
33
+ * Lê a versão do package.json dinamicamente
34
+ */
35
+ function getPackageVersion(): string {
36
+ try {
37
+ const __filename = fileURLToPath(import.meta.url);
38
+ const __dirname = dirname(__filename);
39
+ const packagePath = join(__dirname, "..", "package.json");
40
+ const packageJson = JSON.parse(readFileSync(packagePath, "utf-8"));
41
+ return packageJson.version || "0.1.0";
42
+ } catch {
43
+ return "0.1.0";
44
+ }
45
+ }
46
+
47
+ const PACKAGE_VERSION = getPackageVersion();
48
+
49
+ // ═══════════════════════════════════════════════════════════════════════════
50
+ // FUNÇÕES UTILITÁRIAS
51
+ // ═══════════════════════════════════════════════════════════════════════════
52
+
53
+ /**
54
+ * Normaliza nome do agent para usar como nome de pasta
55
+ * Ex: "Sentinel" → "sentinel", "QA-Tester" → "qa-tester"
56
+ */
57
+ function normalizeAgentName(agentName: string): string {
58
+ return agentName
59
+ .toLowerCase()
60
+ .replace(/[^a-z0-9]+/g, "-")
61
+ .replace(/^-|-$/g, "");
62
+ }
63
+
64
+ /**
65
+ * Retorna o caminho do diretório de memória para um agent
66
+ */
67
+ function getAgentMemoryDir(agentName: string): string {
68
+ const normalizedName = normalizeAgentName(agentName);
69
+ return join(process.cwd(), ".claude", "agent-memory", normalizedName);
70
+ }
71
+
72
+ /**
73
+ * Retorna o caminho do arquivo MEMORY.md de um agent
74
+ */
75
+ function getAgentMemoryPath(agentName: string): string {
76
+ return join(getAgentMemoryDir(agentName), "MEMORY.md");
77
+ }
78
+
79
+ /**
80
+ * Garante que o diretório existe
81
+ */
82
+ function ensureAgentMemoryDir(agentName: string): void {
83
+ const dir = getAgentMemoryDir(agentName);
84
+ if (!existsSync(dir)) {
85
+ mkdirSync(dir, { recursive: true });
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Formata timestamp para uso em entradas de memória
91
+ */
92
+ function formatTimestamp(): string {
93
+ const now = new Date();
94
+ return now.toISOString().replace("T", " ").slice(0, 19);
95
+ }
96
+
97
+ /**
98
+ * Limita memória a ~200 linhas (últimas entradas)
99
+ */
100
+ function limitMemoryLines(content: string, maxLines = 200): string {
101
+ const lines = content.split("\n");
102
+ if (lines.length <= maxLines) return content;
103
+ const keepCount = Math.floor(maxLines * 0.8); // 160 linhas
104
+ const removed = lines.length - keepCount;
105
+ return `# Memória (últimas ${keepCount} de ${lines.length} linhas)
106
+
107
+ [... ${removed} linhas anteriores removidas ...]
108
+
109
+ ${lines.slice(-keepCount).join("\n")}`;
110
+ }
111
+
112
+ /**
113
+ * Lista todos os agents que têm memória
114
+ */
115
+ function listAgentsWithMemory(): string[] {
116
+ const memoryRoot = join(process.cwd(), ".claude", "agent-memory");
117
+ if (!existsSync(memoryRoot)) return [];
118
+
119
+ try {
120
+ return readdirSync(memoryRoot).filter((name) => {
121
+ const memoryPath = join(memoryRoot, name, "MEMORY.md");
122
+ return existsSync(memoryPath);
123
+ });
124
+ } catch {
125
+ return [];
126
+ }
127
+ }
128
+
129
+ // ═══════════════════════════════════════════════════════════════════════════
130
+ // HANDLERS DAS OPERAÇÕES
131
+ // ═══════════════════════════════════════════════════════════════════════════
132
+
133
+ /**
134
+ * Handler: read - Lê a memória do agent atual
135
+ */
136
+ async function handleRead(agentName: string): Promise<string> {
137
+ const memoryPath = getAgentMemoryPath(agentName);
138
+
139
+ if (!existsSync(memoryPath)) {
140
+ return `📝 Memória vazia para agent "${agentName}".
141
+
142
+ Use \`command="append", entry="..."\` para criar a primeira entrada.`;
143
+ }
144
+
145
+ try {
146
+ const content = readFileSync(memoryPath, "utf-8");
147
+ const lines = content.split("\n").length;
148
+ return `📝 Memória de "${agentName}" (${lines} linhas):
149
+
150
+ ─────────────────────────────────────────────────────────────────
151
+
152
+ ${content}`;
153
+ } catch (error) {
154
+ return `❌ Erro ao ler memória: ${(error as Error).message}`;
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Handler: write - Substitui toda a memória
160
+ */
161
+ async function handleWrite(
162
+ agentName: string,
163
+ content: string,
164
+ backup: boolean = false
165
+ ): Promise<string> {
166
+ try {
167
+ ensureAgentMemoryDir(agentName);
168
+ const memoryPath = getAgentMemoryPath(agentName);
169
+
170
+ // Cria backup se solicitado e arquivo existir
171
+ let backupMessage = "";
172
+ if (backup && existsSync(memoryPath)) {
173
+ const backupPath = memoryPath + ".backup";
174
+ const existingContent = readFileSync(memoryPath, "utf-8");
175
+ writeFileSync(backupPath, existingContent, "utf-8");
176
+ backupMessage = `\n💾 Backup criado: MEMORY.md.backup`;
177
+ }
178
+
179
+ const limited = limitMemoryLines(content);
180
+ writeFileSync(memoryPath, limited, "utf-8");
181
+
182
+ const lines = limited.split("\n").length;
183
+ return `✅ Memória de "${agentName}" atualizada (${lines} linhas).${backupMessage}
184
+
185
+ ${lines >= 200 ? "⚠️ Memória atingiu o limite de 200 linhas." : ""}`;
186
+ } catch (error) {
187
+ return `❌ Erro ao escrever memória: ${(error as Error).message}`;
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Handler: append - Adiciona uma entrada no final
193
+ */
194
+ async function handleAppend(
195
+ agentName: string,
196
+ entry: string
197
+ ): Promise<string> {
198
+ try {
199
+ ensureAgentMemoryDir(agentName);
200
+ const memoryPath = getAgentMemoryPath(agentName);
201
+
202
+ // conteúdo existente
203
+ let existingContent = "";
204
+ if (existsSync(memoryPath)) {
205
+ existingContent = readFileSync(memoryPath, "utf-8");
206
+ }
207
+
208
+ // Adiciona nova entrada com timestamp
209
+ const timestamp = formatTimestamp();
210
+ const newEntry = `\n## [${timestamp}]\n${entry}\n`;
211
+ const newContent = existingContent + newEntry;
212
+
213
+ // Salva com limite
214
+ const limited = limitMemoryLines(newContent);
215
+ writeFileSync(memoryPath, limited, "utf-8");
216
+
217
+ return `✅ Entrada adicionada à memória de "${agentName}".
218
+
219
+ **Timestamp:** ${timestamp}
220
+ **Entry:** ${entry.slice(0, 100)}${entry.length > 100 ? "..." : ""}`;
221
+ } catch (error) {
222
+ return `❌ Erro ao adicionar entrada: ${(error as Error).message}`;
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Handler: search - Busca texto na memória
228
+ */
229
+ async function handleSearch(
230
+ agentName: string,
231
+ query: string
232
+ ): Promise<string> {
233
+ const memoryPath = getAgentMemoryPath(agentName);
234
+
235
+ if (!existsSync(memoryPath)) {
236
+ return `📝 Memória vazia para agent "${agentName}".`;
237
+ }
238
+
239
+ try {
240
+ const content = readFileSync(memoryPath, "utf-8");
241
+ const lines = content.split("\n");
242
+ const queryLower = query.toLowerCase();
243
+
244
+ const matches = lines
245
+ .map((line, idx) => ({ line, idx }))
246
+ .filter(({ line }) => line.toLowerCase().includes(queryLower));
247
+
248
+ if (matches.length === 0) {
249
+ return `📝 Nenhuma ocorrência de "${query}" encontrada na memória de "${agentName}".`;
250
+ }
251
+
252
+ const results = matches
253
+ .slice(0, 20) // Máximo 20 resultados
254
+ .map(({ line, idx }) => ` L${idx + 1}: ${line}`)
255
+ .join("\n");
256
+
257
+ const more = matches.length > 20 ? `\n... e mais ${matches.length - 20} ocorrências` : "";
258
+
259
+ return `📝 Busca por "${query}" em "${agentName}" (${matches.length} ocorrências):
260
+
261
+ ${results}${more}`;
262
+ } catch (error) {
263
+ return `❌ Erro ao buscar: ${(error as Error).message}`;
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Handler: list - Lista todos os agents com memória
269
+ */
270
+ async function handleList(): Promise<string> {
271
+ const agents = listAgentsWithMemory();
272
+
273
+ if (agents.length === 0) {
274
+ return `📝 Nenhum agent com memória neste projeto.
275
+
276
+ Diretório: .claude/agent-memory/`;
277
+ }
278
+
279
+ const agentList = agents
280
+ .map((name) => {
281
+ const path = join(".claude", "agent-memory", name, "MEMORY.md");
282
+ let lines = 0;
283
+ try {
284
+ lines = readFileSync(join(process.cwd(), path), "utf-8").split("\n").length;
285
+ } catch {}
286
+ return ` • ${name} (${lines} linhas)`;
287
+ })
288
+ .join("\n");
289
+
290
+ return `📝 Agents com memória neste projeto (${agents.length}):
291
+
292
+ ${agentList}
293
+
294
+ Use o comando "read" de cada agent para ver o conteúdo.`;
295
+ }
296
+
297
+ // ═══════════════════════════════════════════════════════════════════════════
298
+ // SERVIDOR MCP
299
+ // ═══════════════════════════════════════════════════════════════════════════
300
+
301
+ // Cria o servidor MCP
302
+ const server = new Server(
303
+ {
304
+ name: "@justmpm/memory",
305
+ version: PACKAGE_VERSION,
306
+ },
307
+ {
308
+ capabilities: {
309
+ tools: {},
310
+ },
311
+ }
312
+ );
313
+
314
+ /**
315
+ * Handler: ListTools - Lista todas as tools disponíveis
316
+ */
317
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
318
+ return {
319
+ tools: [
320
+ {
321
+ name: "memory",
322
+ description: `
323
+ Sua memória pessoal - salve e recupere seus aprendizados entre sessões.
324
+
325
+ **PARA QUE:** Guarde padrões de código, bugs encontrados, decisões tomadas e preferências do usuário. Tudo persiste entre sessões.
326
+
327
+ **ONDE:** Seu arquivo fica em .claude/agent-memory/<seu-nome>/MEMORY.md
328
+
329
+ **IMPORTANTE:** Máximo 200 linhas. Quando passar, as mais antigas são removidas (mantém últimas 160).
330
+
331
+ ═══════════════════════════════════════════════════════════════
332
+ COMANDOS DISPONÍVEIS:
333
+ ═══════════════════════════════════════════════════════════════
334
+
335
+ 1. **read** - Carrega sua memória
336
+ Uso: Ao iniciar uma sessão para recuperar seu contexto anterior
337
+ - agent: (opcional) Seu nome de agent. Se não fornecido, usa "unknown"
338
+ - Retorna: Conteúdo completo do seu MEMORY.md com contagem de linhas
339
+ - Se não existir: Mensagem de memória vazia com sugestão de usar 'append'
340
+
341
+ 2. **append** - Adiciona nova informação à sua memória
342
+ Uso: Quando aprender algo novo importante (padrão, bug, decisão)
343
+ - entry: (OBRIGATÓRIO) Texto da informação a salvar
344
+ - Formato automático: Adiciona timestamp "## [YYYY-MM-DD HH:MM:SS]"
345
+ - Retorna: Confirmação com timestamp e preview da entrada
346
+
347
+ 3. **search** - Procura informação específica na sua memória
348
+ Uso: Quando precisar encontrar algo sem ler tudo
349
+ - query: (OBRIGATÓRIO) Termo ou palavra-chave a buscar
350
+ - Busca: Case-insensitive, máximo 20 resultados
351
+ - Retorna: Ocorrências encontradas com número da linha
352
+
353
+ 4. **write** - Reescreve sua memória reorganizando tudo
354
+ Uso: Quando a memória ficar grande (perto de 200 linhas) ou precisar reestruturar
355
+ - content: (OBRIGATÓRIO) Novo conteúdo completo em markdown
356
+ - backup: (opcional) Se true, cria backup antes de sobrescrever
357
+ - Retorna: Confirmação com contagem de linhas e alerta se atingir limite
358
+
359
+ ═══════════════════════════════════════════════════════════════
360
+ WORKFLOW RECOMENDADO:
361
+ ═══════════════════════════════════════════════════════════════
362
+
363
+ 1. **INICIAR SESSÃO:** { "command": "read" }
364
+ → Carrega sua memória anterior para ter contexto
365
+
366
+ 2. **DURANTE O TRABALHO:** { "command": "append", "entry": "Padrão: ..." }
367
+ Sempre que aprender algo importante, salve imediatamente
368
+
369
+ 3. **PRECISAR DE ALGO:** { "command": "search", "query": "Zod" }
370
+ Procure na sua memória sem precisar ler tudo
371
+
372
+ 4. **MEMÓRIA GRANDE:** { "command": "write", "content": "...", "backup": true }
373
+ Reestruture e use backup para segurança
374
+
375
+ ═══════════════════════════════════════════════════════════════
376
+ EXEMPLOS PRÁTICOS:
377
+ ═══════════════════════════════════════════════════════════════
378
+
379
+ Iniciar sessão (sempre comece assim):
380
+ { "command": "read" }
381
+
382
+ Salvar um padrão de código:
383
+ { "command": "append", "entry": "Padrão: Sempre use Zod para validar inputs do usuário" }
384
+
385
+ Salvar uma decisão importante:
386
+ { "command": "append", "entry": "Decisão: Usar Zustand em vez de Redux (mais leve)" }
387
+
388
+ Salvar um bug recorrente:
389
+ { "command": "append", "entry": "Bug: Erro acontece quando array está vazio no submit" }
390
+
391
+ Buscar informação sobre Zod:
392
+ { "command": "search", "query": "Zod" }
393
+
394
+ Reorganizar memória grande com backup:
395
+ { "command": "write", "content": "# Memória Reorganizada\\n\\n## Padrões\\n- Padrão 1\\n- Padrão 2", "backup": true }
396
+
397
+ ═══════════════════════════════════════════════════════════════
398
+ O QUE SALVAR (exemplos):
399
+ ═══════════════════════════════════════════════════════════════
400
+
401
+ **Salve:** Padrões de código, bugs recorrentes, decisões arquiteturais, preferências do usuário, configurações importantes.
402
+
403
+ ✅ **Organize com markdown:**
404
+ ## Padrões - Convenções de código
405
+ ## Bugs - Problemas e workarounds
406
+ ## Decisões - Escolhas arquiteturais
407
+
408
+ **Não salve:** Coisas triviais, informações temporárias, logs de conversa, coisas óbvias.
409
+
410
+ ═══════════════════════════════════════════════════════════════
411
+ DICAS IMPORTANTES:
412
+ ═══════════════════════════════════════════════════════════════
413
+
414
+ Seu nome é normalizado: "Meu Agent" → "meu-agent"
415
+ Use search antes de salvar para evitar duplicatas
416
+ Memória é por projeto (pasta .claude/agent-memory/)
417
+ Ao atingir 200 linhas, as mais antigas são removidas automaticamente
418
+ `.trim(),
419
+ inputSchema: {
420
+ type: "object",
421
+ properties: {
422
+ command: {
423
+ type: "string",
424
+ enum: ["read", "append", "search", "write"],
425
+ description: "Comando a executar: 'read' carrega sua memória, 'append' adiciona nova informação, 'search' procura algo específico, 'write' reescreve reorganizando tudo.",
426
+ },
427
+ agent: {
428
+ type: "string",
429
+ description: 'Seu nome de agent (ex: "sentinel", "fix-worker"). Opcional - usa "unknown" se não informado. Será normalizado para minúsculas.',
430
+ },
431
+ content: {
432
+ type: "string",
433
+ description: 'Conteúdo completo em markdown para substituir a memória existente (OBRIGATÓRIO para "write"). Use para reorganizar, limpar ou reconstruir memória do zero.',
434
+ },
435
+ backup: {
436
+ type: "boolean",
437
+ description: 'Se true, cria um backup do conteúdo atual antes de sobrescrever. O backup é salvo como MEMORY.md.backup no mesmo diretório. Padrão: false.',
438
+ },
439
+ entry: {
440
+ type: "string",
441
+ description: 'Texto da informação a salvar (OBRIGATÓRIO para append). Ex: "Padrão: sempre use try/catch em funções async" ou "Bug: erro quando usuário clica 2x". Será adicionado com timestamp automaticamente.',
442
+ },
443
+ query: {
444
+ type: "string",
445
+ description: 'Palavra-chave para buscar na sua memória (OBRIGATÓRIO para search). Ex: "Zod", "bug", "Firebase". Retorna até 20 resultados com número da linha.',
446
+ },
447
+ },
448
+ required: ["command"],
449
+ },
450
+ },
451
+ ],
452
+ };
453
+ });
454
+
455
+ /**
456
+ * Handler: CallTool - Executa uma tool
457
+ */
458
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
459
+ const { name, arguments: args } = request.params;
460
+
461
+ if (name !== "memory") {
462
+ throw new Error(`Tool desconhecida: ${name}`);
463
+ }
464
+
465
+ const command = args?.command as string;
466
+ const agentName = (args?.agent as string) || "unknown";
467
+
468
+ // Valida comando
469
+ const validCommands = ["read", "append", "search", "write"];
470
+ if (!validCommands.includes(command)) {
471
+ return {
472
+ content: [
473
+ {
474
+ type: "text",
475
+ text: `❌ ERRO: Comando desconhecido "${command}"
476
+
477
+ COMANDOS DISPONÍVEIS:
478
+
479
+ 1. read - Carrega sua memória (use ao iniciar)
480
+ 2. append - Adiciona nova informação (mais usado)
481
+ 3. search - Procura algo na sua memória
482
+ 4. write - Reescreve reorganizando tudo (cuidado!)
483
+
484
+ PARA MAIS DETALHES, CONSULTE A DESCRIÇÃO DA TOOL "memory"
485
+ `,
486
+ },
487
+ ],
488
+ };
489
+ }
490
+
491
+ // Executa comando específico
492
+ let result: string;
493
+
494
+ switch (command) {
495
+ case "read": {
496
+ result = await handleRead(agentName);
497
+ break;
498
+ }
499
+
500
+ case "write": {
501
+ const content = args?.content as string;
502
+ if (!content) {
503
+ result = `❌ ERRO: O parâmetro "content" é OBRIGATÓRIO para o comando "write".
504
+
505
+ EXEMPLO DE USO CORRETO:
506
+ {
507
+ "command": "write",
508
+ "agent": "sentinel",
509
+ "content": "# Memória do Sentinel\\n\\n## Padrões\\n- Sempre use Zod para validação\\n\\n## Bugs\\n- Bug XYZ ocorre quando..."
510
+ }
511
+
512
+ DICA: Use "backup": true para criar uma cópia de segurança antes de sobrescrever:
513
+ {
514
+ "command": "write",
515
+ "agent": "sentinel",
516
+ "content": "# Nova memória...",
517
+ "backup": true
518
+ }
519
+
520
+ Use "write" para reorganizar memória grande ou reconstruir do zero.
521
+ Para adicionar uma entrada preservando o histórico, use "append".
522
+ `;
523
+ break;
524
+ }
525
+ const backup = (args?.backup as boolean) || false;
526
+ result = await handleWrite(agentName, content, backup);
527
+ break;
528
+ }
529
+
530
+ case "append": {
531
+ const entry = args?.entry as string;
532
+ if (!entry) {
533
+ result = `❌ ERRO: O parâmetro "entry" é OBRIGATÓRIO para o comando "append".
534
+
535
+ EXEMPLO DE USO CORRETO:
536
+ {
537
+ "command": "append",
538
+ "agent": "sentinel",
539
+ "entry": "Padrão descoberto: Sempre use Zod para validar inputs do usuário em todos os componentes de formulário"
540
+ }
541
+
542
+ DICA: Use "append" para salvar aprendizados incrementais. O timestamp é adicionado automaticamente no formato:
543
+ ## [2026-02-09 12:34:56]
544
+ Sua entrada aqui
545
+
546
+ Para salvar informações mais longas ou estruturadas, considere usar "write".
547
+ `;
548
+ break;
549
+ }
550
+ result = await handleAppend(agentName, entry);
551
+ break;
552
+ }
553
+
554
+ case "search": {
555
+ const query = args?.query as string;
556
+ if (!query) {
557
+ result = `❌ ERRO: O parâmetro "query" é OBRIGATÓRIO para o comando "search".
558
+
559
+ EXEMPLO DE USO CORRETO:
560
+ {
561
+ "command": "search",
562
+ "agent": "sentinel",
563
+ "query": "Zod"
564
+ }
565
+
566
+ DICA: A busca é case-insensitive e retorna até 20 ocorrências com o número da linha.
567
+ Exemplos de busca úteis: "padrão", "bug", "TypeScript", "Firebase", "erro"
568
+ `;
569
+ break;
570
+ }
571
+ result = await handleSearch(agentName, query);
572
+ break;
573
+ }
574
+
575
+ default:
576
+ result = `❌ Comando desconhecido: ${command}`;
577
+ }
578
+
579
+ return {
580
+ content: [
581
+ {
582
+ type: "text",
583
+ text: result,
584
+ },
585
+ ],
586
+ };
587
+ });
588
+
589
+ // ═══════════════════════════════════════════════════════════════════════════
590
+ // INICIALIZAÇÃO
591
+ // ═══════════════════════════════════════════════════════════════════════════
592
+
593
+ async function main() {
594
+ // Verifica argumentos de linha de comando
595
+ const args = process.argv.slice(2);
596
+
597
+ // --version
598
+ if (args.includes("--version") || args.includes("-v")) {
599
+ console.log(`@justmpm/memory v${PACKAGE_VERSION}`);
600
+ process.exit(0);
601
+ }
602
+
603
+ // --help
604
+ if (args.includes("--help") || args.includes("-h")) {
605
+ console.log(`
606
+ @justmpm/memory - MCP Server para gerenciar memória persistente de subagents
607
+
608
+ Versão: ${PACKAGE_VERSION}
609
+
610
+ USO:
611
+ memory Inicia o servidor MCP (comunicação via stdio)
612
+ memory --version Mostra a versão do pacote
613
+ memory --help Mostra esta mensagem de ajuda
614
+
615
+ COMANDOS MCP:
616
+ - read Lê a memória de um agent
617
+ - write Substitui toda a memória
618
+ - append Adiciona uma entrada ao final
619
+ - search Busca texto na memória
620
+ - list Lista todos os agents com memória
621
+
622
+ DOCUMENTAÇÃO:
623
+ Para mais detalhes, consulte:
624
+ - CLAUDE.md (Documentação para IAs)
625
+ - AGENTS.md (Guia para subagents)
626
+ - README.md (Documentação principal)
627
+
628
+ REPOSITÓRIO:
629
+ https://github.com/justmpm/memory-mcp
630
+
631
+ LICENÇA:
632
+ MIT © Koda AI Studio
633
+ `);
634
+ process.exit(0);
635
+ }
636
+
637
+ const transport = new StdioServerTransport();
638
+ await server.connect(transport);
639
+ // Não faz log aqui pois o servidor se comunica via stdio
640
+ }
641
+
642
+ main().catch((error) => {
643
+ console.error("Erro fatal ao iniciar servidor:", error);
644
+ process.exit(1);
645
+ });