@justmpm/ai-tool 0.8.1 → 0.9.0

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.
@@ -0,0 +1,649 @@
1
+ import {
2
+ VERSION,
3
+ area,
4
+ areaContext,
5
+ areas,
6
+ areasInit,
7
+ context,
8
+ dead,
9
+ find,
10
+ findSimilar,
11
+ formatOutput,
12
+ functions,
13
+ impact,
14
+ map,
15
+ parseCommandOptions,
16
+ readConfig,
17
+ suggest
18
+ } from "./chunk-NVJXCSJF.js";
19
+
20
+ // src/mcp/server.ts
21
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
22
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
23
+
24
+ // src/mcp/tools.ts
25
+ import { z } from "zod";
26
+
27
+ // src/commands/describe.ts
28
+ async function describe(query, options = {}) {
29
+ const { cwd, format } = parseCommandOptions(options);
30
+ if (!query || query.trim().length === 0) {
31
+ throw new Error("Query \xE9 obrigat\xF3ria. Exemplo: ai-tool describe 'autentica\xE7\xE3o'");
32
+ }
33
+ try {
34
+ const config = readConfig(cwd);
35
+ const normalizedQuery = query.toLowerCase().trim();
36
+ const candidates = Object.entries(config.areas).map(([id, area2]) => ({
37
+ id,
38
+ name: area2.name,
39
+ description: area2.description || ""
40
+ }));
41
+ const matches = findAreaMatches(normalizedQuery, candidates, config);
42
+ const suggestions = [];
43
+ if (matches.length === 0) {
44
+ const similarAreaIds = findSimilar(
45
+ normalizedQuery,
46
+ candidates.map((c) => c.id),
47
+ { maxDistance: 2, limit: 3 }
48
+ );
49
+ const similarNames = findSimilar(
50
+ normalizedQuery,
51
+ candidates.map((c) => c.name),
52
+ { maxDistance: 2, limit: 3 }
53
+ );
54
+ suggestions.push(
55
+ ...similarAreaIds.map((id) => `\u2192 ai-tool describe ${id}`),
56
+ ...similarNames.map((name) => `\u2192 ai-tool describe "${name}"`)
57
+ );
58
+ }
59
+ const result = {
60
+ version: "1.0.0",
61
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
62
+ query,
63
+ areas: matches,
64
+ suggestions: suggestions.length > 0 ? suggestions : void 0
65
+ };
66
+ return formatOutput(result, format, formatDescribeText);
67
+ } catch (error) {
68
+ const message = error instanceof Error ? error.message : String(error);
69
+ throw new Error(`Erro ao executar describe: ${message}`);
70
+ }
71
+ }
72
+ function findAreaMatches(normalizedQuery, candidates, config) {
73
+ const matches = [];
74
+ for (const candidate of candidates) {
75
+ const searchableText = `${candidate.id} ${candidate.name} ${candidate.description}`.toLowerCase();
76
+ const hasDirectMatch = searchableText.includes(normalizedQuery);
77
+ const queryWords = normalizedQuery.split(/\s+/);
78
+ const allWordsMatch = queryWords.every((word) => searchableText.includes(word));
79
+ if (hasDirectMatch || allWordsMatch) {
80
+ const areaFiles = getAreaFiles(candidate.id, config);
81
+ const score = calculateRelevanceScore(normalizedQuery, searchableText);
82
+ matches.push({
83
+ id: candidate.id,
84
+ name: candidate.name,
85
+ description: candidate.description || "Sem descri\xE7\xE3o",
86
+ files: areaFiles,
87
+ fileCount: areaFiles.length,
88
+ score
89
+ });
90
+ }
91
+ }
92
+ return matches.sort((a, b) => a.score - b.score);
93
+ }
94
+ function getAreaFiles(areaId, config) {
95
+ const files = [];
96
+ const areaConfig = config.areas[areaId];
97
+ if (areaConfig?.patterns) {
98
+ files.push(`[Use 'ai-tool area ${areaId}' para ver arquivos completos]`);
99
+ }
100
+ return files;
101
+ }
102
+ function calculateRelevanceScore(query, text) {
103
+ if (text.includes(query)) {
104
+ return 0;
105
+ }
106
+ const queryWords = query.split(/\s+/).filter(Boolean);
107
+ const allWordsPresent = queryWords.every((word) => text.includes(word));
108
+ if (allWordsPresent) {
109
+ return 1;
110
+ }
111
+ return 10;
112
+ }
113
+ function formatDescribeText(result) {
114
+ let out = "";
115
+ if (result.areas.length === 0) {
116
+ out += `\u274C Nenhuma \xE1rea encontrada para: "${result.query}"
117
+
118
+ `;
119
+ if (result.suggestions && result.suggestions.length > 0) {
120
+ out += `\u{1F4A1} Voc\xEA quis dizer?
121
+ `;
122
+ for (const suggestion of result.suggestions) {
123
+ out += ` ${suggestion}
124
+ `;
125
+ }
126
+ out += `
127
+ `;
128
+ }
129
+ out += `\u{1F4D6} Dica: Use 'ai-tool areas' para listar todas as \xE1reas dispon\xEDveis`;
130
+ return out;
131
+ }
132
+ out += `\u{1F50D} Busca: "${result.query}"
133
+
134
+ `;
135
+ for (const area2 of result.areas) {
136
+ out += `## ${area2.name} (${area2.id})
137
+ `;
138
+ out += `${area2.description}
139
+ `;
140
+ out += `\u{1F4C1} ${area2.fileCount} arquivo(s)
141
+
142
+ `;
143
+ if (area2.files.length > 0) {
144
+ out += `Arquivos:
145
+ `;
146
+ for (const file of area2.files) {
147
+ out += ` \u2022 ${file}
148
+ `;
149
+ }
150
+ out += "\n";
151
+ }
152
+ }
153
+ out += `\u{1F4D6} Pr\xF3ximos passos:
154
+ `;
155
+ out += ` \u2192 ai-tool area <id> - ver detalhes de uma \xE1rea
156
+ `;
157
+ out += ` \u2192 ai-tool context --area=<id> - contexto completo de uma \xE1rea
158
+ `;
159
+ return out;
160
+ }
161
+
162
+ // src/mcp/tools.ts
163
+ function registerAllTools(server2) {
164
+ server2.registerTool(
165
+ "aitool_project_map",
166
+ {
167
+ title: "Project Map",
168
+ description: `Mapeia projeto e retorna resumo: contagens por categoria, areas detectadas, alertas.
169
+ Use no inicio da sessao. Para detalhes: area_detail, file_context, suggest_reads.
170
+
171
+ Parametros:
172
+ - format: text (legivel) ou json (estruturado)
173
+ - cwd: Diretorio do projeto a analisar`,
174
+ inputSchema: {
175
+ format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
176
+ cwd: z.string().optional().describe("Diretorio do projeto a analisar")
177
+ },
178
+ annotations: {
179
+ title: "Project Map",
180
+ readOnlyHint: true,
181
+ destructiveHint: false,
182
+ idempotentHint: true,
183
+ openWorldHint: false
184
+ }
185
+ },
186
+ async (params) => {
187
+ try {
188
+ const result = await map({
189
+ format: params.format,
190
+ cwd: params.cwd,
191
+ full: false
192
+ });
193
+ return { content: [{ type: "text", text: result }] };
194
+ } catch (error) {
195
+ return {
196
+ content: [{ type: "text", text: `Erro ao executar map: ${error instanceof Error ? error.message : String(error)}` }],
197
+ isError: true
198
+ };
199
+ }
200
+ }
201
+ );
202
+ server2.registerTool(
203
+ "aitool_dead_code",
204
+ {
205
+ title: "Dead Code Detector",
206
+ description: `Detecta codigo morto: arquivos orfaos, exports nao usados, deps npm mortas.
207
+ Use antes de refatoracoes ou periodicamente para limpeza.
208
+
209
+ Parametros:
210
+ - format: text (legivel) ou json (estruturado)
211
+ - cwd: Diretorio do projeto a analisar`,
212
+ inputSchema: {
213
+ format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
214
+ cwd: z.string().optional().describe("Diretorio do projeto a analisar")
215
+ },
216
+ annotations: {
217
+ title: "Dead Code Detector",
218
+ readOnlyHint: true,
219
+ destructiveHint: false,
220
+ idempotentHint: true,
221
+ openWorldHint: false
222
+ }
223
+ },
224
+ async (params) => {
225
+ try {
226
+ const result = await dead({
227
+ format: params.format,
228
+ cwd: params.cwd
229
+ });
230
+ return { content: [{ type: "text", text: result }] };
231
+ } catch (error) {
232
+ return {
233
+ content: [{ type: "text", text: `Erro ao executar dead: ${error instanceof Error ? error.message : String(error)}` }],
234
+ isError: true
235
+ };
236
+ }
237
+ }
238
+ );
239
+ server2.registerTool(
240
+ "aitool_impact_analysis",
241
+ {
242
+ title: "Impact Analysis",
243
+ description: `Analisa impacto de modificar um arquivo: upstream (quem importa), downstream (o que importa), riscos.
244
+ Use ANTES de editar arquivos para planejar mudancas seguras.
245
+
246
+ Parametros:
247
+ - target: Arquivo a analisar (caminho completo, parcial ou nome)
248
+ - format: text (legivel) ou json (estruturado)
249
+ - cwd: Diretorio do projeto a analisar`,
250
+ inputSchema: {
251
+ target: z.string().min(1).describe("Arquivo a analisar: caminho completo, parcial ou nome do arquivo"),
252
+ format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
253
+ cwd: z.string().optional().describe("Diretorio do projeto a analisar")
254
+ },
255
+ annotations: {
256
+ title: "Impact Analysis",
257
+ readOnlyHint: true,
258
+ destructiveHint: false,
259
+ idempotentHint: true,
260
+ openWorldHint: false
261
+ }
262
+ },
263
+ async (params) => {
264
+ try {
265
+ const result = await impact(params.target, {
266
+ format: params.format,
267
+ cwd: params.cwd
268
+ });
269
+ return { content: [{ type: "text", text: result }] };
270
+ } catch (error) {
271
+ return {
272
+ content: [{ type: "text", text: `Erro ao executar impact: ${error instanceof Error ? error.message : String(error)}` }],
273
+ isError: true
274
+ };
275
+ }
276
+ }
277
+ );
278
+ server2.registerTool(
279
+ "aitool_suggest_reads",
280
+ {
281
+ title: "Suggest Files to Read",
282
+ description: `Sugere arquivos para ler ANTES de modificar um arquivo.
283
+ Prioriza: tipos usados, dependencias diretas, upstream, testes.
284
+
285
+ Parametros:
286
+ - target: Arquivo que sera modificado (caminho completo, parcial ou nome)
287
+ - limit: Numero maximo de sugestoes (default: 10, max: 50)`,
288
+ inputSchema: {
289
+ target: z.string().min(1).describe("Arquivo que sera modificado: caminho completo, parcial ou nome"),
290
+ limit: z.number().int().min(1).max(50).default(10).describe("Numero maximo de sugestoes (default: 10, max: 50)"),
291
+ cwd: z.string().optional().describe("Diretorio do projeto a analisar")
292
+ },
293
+ annotations: {
294
+ title: "Suggest Files to Read",
295
+ readOnlyHint: true,
296
+ destructiveHint: false,
297
+ idempotentHint: true,
298
+ openWorldHint: false
299
+ }
300
+ },
301
+ async (params) => {
302
+ try {
303
+ const result = await suggest(params.target, {
304
+ limit: params.limit,
305
+ cwd: params.cwd,
306
+ format: "text"
307
+ });
308
+ return { content: [{ type: "text", text: result }] };
309
+ } catch (error) {
310
+ return {
311
+ content: [{ type: "text", text: `Erro ao executar suggest: ${error instanceof Error ? error.message : String(error)}` }],
312
+ isError: true
313
+ };
314
+ }
315
+ }
316
+ );
317
+ server2.registerTool(
318
+ "aitool_file_context",
319
+ {
320
+ title: "Extract File Context",
321
+ description: `Extrai assinaturas de funcoes e tipos de um arquivo (sem implementacao).
322
+ Use para entender a API publica antes de usar ou modificar.
323
+
324
+ Parametros:
325
+ - target: Arquivo para extrair contexto (caminho completo, parcial ou nome)
326
+ - cwd: Diretorio do projeto a analisar`,
327
+ inputSchema: {
328
+ target: z.string().min(1).describe("Arquivo para extrair contexto: caminho completo, parcial ou nome"),
329
+ cwd: z.string().optional().describe("Diretorio do projeto a analisar")
330
+ },
331
+ annotations: {
332
+ title: "Extract File Context",
333
+ readOnlyHint: true,
334
+ destructiveHint: false,
335
+ idempotentHint: true,
336
+ openWorldHint: false
337
+ }
338
+ },
339
+ async (params) => {
340
+ try {
341
+ const result = await context(params.target, {
342
+ cwd: params.cwd,
343
+ format: "text"
344
+ });
345
+ return { content: [{ type: "text", text: result }] };
346
+ } catch (error) {
347
+ return {
348
+ content: [{ type: "text", text: `Erro ao executar context: ${error instanceof Error ? error.message : String(error)}` }],
349
+ isError: true
350
+ };
351
+ }
352
+ }
353
+ );
354
+ server2.registerTool(
355
+ "aitool_list_areas",
356
+ {
357
+ title: "List Project Areas",
358
+ description: `Lista areas/dominios funcionais do projeto (auth, pets, stripe...).
359
+ Diferente de categorias (hook, component). Use area_detail para ver arquivos.
360
+
361
+ Parametros:
362
+ - format: text (legivel) ou json (estruturado)
363
+ - cwd: Diretorio do projeto a analisar`,
364
+ inputSchema: {
365
+ format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
366
+ cwd: z.string().optional().describe("Diretorio do projeto a analisar")
367
+ },
368
+ annotations: {
369
+ title: "List Project Areas",
370
+ readOnlyHint: true,
371
+ destructiveHint: false,
372
+ idempotentHint: true,
373
+ openWorldHint: false
374
+ }
375
+ },
376
+ async (params) => {
377
+ try {
378
+ const result = await areas({
379
+ format: params.format,
380
+ cwd: params.cwd
381
+ });
382
+ return { content: [{ type: "text", text: result }] };
383
+ } catch (error) {
384
+ return {
385
+ content: [{ type: "text", text: `Erro ao executar areas: ${error instanceof Error ? error.message : String(error)}` }],
386
+ isError: true
387
+ };
388
+ }
389
+ }
390
+ );
391
+ server2.registerTool(
392
+ "aitool_area_detail",
393
+ {
394
+ title: "Area Detail",
395
+ description: `Mostra arquivos de uma area especifica, agrupados por categoria.
396
+ Use ID (ex: auth) ou Name (ex: Autentica\xE7\xE3o) para identificar a area.
397
+
398
+ Parametros:
399
+ - target: Nome da area (ex: auth, dashboard, billing)
400
+ - type: Filtrar por categoria (page, component, hook, service, etc)
401
+ - full: Mostrar todos os arquivos (default: resumido)
402
+ - cwd: Diretorio do projeto a analisar`,
403
+ inputSchema: {
404
+ target: z.string().min(1).describe("Nome da area: auth, dashboard, billing, etc"),
405
+ type: z.enum(["page", "layout", "route", "component", "hook", "service", "store", "util", "type", "config", "test", "other"]).optional().describe("Filtrar por categoria especifica"),
406
+ full: z.boolean().default(false).describe("Mostrar todos os arquivos (default: resumido)"),
407
+ cwd: z.string().optional().describe("Diretorio do projeto a analisar")
408
+ },
409
+ annotations: {
410
+ title: "Area Detail",
411
+ readOnlyHint: true,
412
+ destructiveHint: false,
413
+ idempotentHint: true,
414
+ openWorldHint: false
415
+ }
416
+ },
417
+ async (params) => {
418
+ try {
419
+ const result = await area(params.target, {
420
+ type: params.type,
421
+ full: params.full,
422
+ cwd: params.cwd,
423
+ format: "text"
424
+ });
425
+ return { content: [{ type: "text", text: result }] };
426
+ } catch (error) {
427
+ return {
428
+ content: [{ type: "text", text: `Erro ao executar area: ${error instanceof Error ? error.message : String(error)}` }],
429
+ isError: true
430
+ };
431
+ }
432
+ }
433
+ );
434
+ server2.registerTool(
435
+ "aitool_areas_init",
436
+ {
437
+ title: "Initialize Areas Config",
438
+ description: `Gera .analyze/areas.config.json para customizar deteccao de areas.
439
+ Use quando houver arquivos sem area ou precisar ajustar deteccao.
440
+
441
+ Parametros:
442
+ - force: Sobrescrever config existente
443
+ - cwd: Diretorio do projeto`,
444
+ inputSchema: {
445
+ force: z.boolean().default(false).describe("Sobrescrever config existente"),
446
+ cwd: z.string().optional().describe("Diretorio do projeto")
447
+ },
448
+ annotations: {
449
+ title: "Initialize Areas Config",
450
+ readOnlyHint: false,
451
+ destructiveHint: false,
452
+ idempotentHint: false,
453
+ openWorldHint: false
454
+ }
455
+ },
456
+ async (params) => {
457
+ try {
458
+ const result = await areasInit({
459
+ force: params.force,
460
+ cwd: params.cwd
461
+ });
462
+ return { content: [{ type: "text", text: result }] };
463
+ } catch (error) {
464
+ return {
465
+ content: [{ type: "text", text: `Erro ao executar areas init: ${error instanceof Error ? error.message : String(error)}` }],
466
+ isError: true
467
+ };
468
+ }
469
+ }
470
+ );
471
+ server2.registerTool(
472
+ "aitool_find",
473
+ {
474
+ title: "Find Symbol",
475
+ description: `Busca simbolos no codigo: funcoes, tipos, componentes, hooks, constantes.
476
+ Retorna definicao + referencias/usos. Diferente de grep, entende o AST do TypeScript.
477
+
478
+ Parametros:
479
+ - query: Termo a buscar (ex: useAuth, User, login)
480
+ - type: Filtrar por tipo (function, type, const, component, hook, trigger, all)
481
+ - area: Buscar apenas em uma area especifica (ex: auth, dashboard)
482
+ - def: Mostrar apenas definicoes (onde e declarado)
483
+ - refs: Mostrar apenas referencias/usos`,
484
+ inputSchema: {
485
+ query: z.string().min(1).describe("Termo a buscar (nome de funcao, tipo, componente, etc)"),
486
+ type: z.enum(["function", "type", "const", "component", "hook", "trigger", "all"]).default("all").describe("Filtrar por tipo de simbolo (use trigger para Cloud Functions)"),
487
+ area: z.string().optional().describe("Filtrar busca por area especifica (ex: auth, dashboard)"),
488
+ def: z.boolean().default(false).describe("Mostrar apenas definicoes (onde e declarado)"),
489
+ refs: z.boolean().default(false).describe("Mostrar apenas referencias (onde e usado)"),
490
+ cwd: z.string().optional().describe("Diretorio do projeto a analisar")
491
+ },
492
+ annotations: {
493
+ title: "Find Symbol",
494
+ readOnlyHint: true,
495
+ destructiveHint: false,
496
+ idempotentHint: true,
497
+ openWorldHint: false
498
+ }
499
+ },
500
+ async (params) => {
501
+ try {
502
+ const result = await find(params.query, {
503
+ type: params.type,
504
+ area: params.area,
505
+ def: params.def,
506
+ refs: params.refs,
507
+ cwd: params.cwd,
508
+ format: "text"
509
+ });
510
+ return { content: [{ type: "text", text: result }] };
511
+ } catch (error) {
512
+ return {
513
+ content: [{ type: "text", text: `Erro ao executar find: ${error instanceof Error ? error.message : String(error)}` }],
514
+ isError: true
515
+ };
516
+ }
517
+ }
518
+ );
519
+ server2.registerTool(
520
+ "aitool_area_context",
521
+ {
522
+ title: "Area Context",
523
+ description: `Contexto consolidado de toda uma area: tipos, hooks, funcoes, componentes, services, stores.
524
+ Uma chamada = entender toda a feature. Muito mais eficiente que chamar context em cada arquivo.
525
+
526
+ Parametros:
527
+ - area: Nome da area (ex: auth, dashboard, payments)
528
+ - cwd: Diretorio do projeto a analisar`,
529
+ inputSchema: {
530
+ area: z.string().min(1).describe("Nome da area: auth, dashboard, payments, etc"),
531
+ cwd: z.string().optional().describe("Diretorio do projeto a analisar")
532
+ },
533
+ annotations: {
534
+ title: "Area Context",
535
+ readOnlyHint: true,
536
+ destructiveHint: false,
537
+ idempotentHint: true,
538
+ openWorldHint: false
539
+ }
540
+ },
541
+ async (params) => {
542
+ try {
543
+ const result = await areaContext(params.area, {
544
+ cwd: params.cwd,
545
+ format: "text"
546
+ });
547
+ return { content: [{ type: "text", text: result }] };
548
+ } catch (error) {
549
+ return {
550
+ content: [{ type: "text", text: `Erro ao executar area context: ${error instanceof Error ? error.message : String(error)}` }],
551
+ isError: true
552
+ };
553
+ }
554
+ }
555
+ );
556
+ server2.registerTool(
557
+ "aitool_list_functions",
558
+ {
559
+ title: "List Cloud Functions",
560
+ description: `Lista todas as Cloud Functions Firebase do projeto.
561
+ Agrupa por tipo de trigger (onCall, onDocumentCreated, onSchedule, etc).
562
+ Use para entender a arquitetura serverless antes de modificar triggers.
563
+
564
+ Parametros:
565
+ - trigger: Filtrar por tipo de trigger (ex: onCall, onDocumentCreated, onSchedule)
566
+ - format: text (legivel) ou json (estruturado)`,
567
+ inputSchema: {
568
+ trigger: z.string().optional().describe("Filtrar por tipo de trigger (ex: onCall, onDocumentCreated, onSchedule)"),
569
+ format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
570
+ cwd: z.string().optional().describe("Diretorio do projeto a analisar")
571
+ },
572
+ annotations: {
573
+ title: "List Cloud Functions",
574
+ readOnlyHint: true,
575
+ destructiveHint: false,
576
+ idempotentHint: true,
577
+ openWorldHint: false
578
+ }
579
+ },
580
+ async (params) => {
581
+ try {
582
+ const result = await functions({
583
+ format: params.format,
584
+ trigger: params.trigger,
585
+ cwd: params.cwd
586
+ });
587
+ return { content: [{ type: "text", text: result }] };
588
+ } catch (error) {
589
+ return {
590
+ content: [{ type: "text", text: `Erro ao executar functions: ${error instanceof Error ? error.message : String(error)}` }],
591
+ isError: true
592
+ };
593
+ }
594
+ }
595
+ );
596
+ server2.registerTool(
597
+ "aitool_describe",
598
+ {
599
+ title: "Search by Description",
600
+ description: `Busca \xE1reas por descri\xE7\xE3o em linguagem natural.
601
+ Ex: "Onde implementou login?" \u2192 encontra \xE1rea de autentica\xE7\xE3o.
602
+
603
+ Parametros:
604
+ - query: Descri\xE7\xE3o ou keyword
605
+ - cwd: Diretorio do projeto`,
606
+ inputSchema: {
607
+ query: z.string().min(1).describe("Descri\xE7\xE3o ou keyword para buscar \xE1reas"),
608
+ format: z.enum(["text", "json"]).default("text").describe("Formato de saida: text (legivel) ou json (estruturado)"),
609
+ cwd: z.string().optional().describe("Diretorio do projeto a analisar")
610
+ },
611
+ annotations: {
612
+ title: "Search by Description",
613
+ readOnlyHint: true,
614
+ destructiveHint: false,
615
+ idempotentHint: true,
616
+ openWorldHint: false
617
+ }
618
+ },
619
+ async (params) => {
620
+ try {
621
+ const result = await describe(params.query, {
622
+ format: params.format,
623
+ cwd: params.cwd
624
+ });
625
+ return { content: [{ type: "text", text: result }] };
626
+ } catch (error) {
627
+ return {
628
+ content: [{ type: "text", text: `Erro ao executar describe: ${error instanceof Error ? error.message : String(error)}` }],
629
+ isError: true
630
+ };
631
+ }
632
+ }
633
+ );
634
+ }
635
+
636
+ // src/mcp/server.ts
637
+ var server = new McpServer({
638
+ name: "ai-tool-mcp-server",
639
+ version: VERSION
640
+ });
641
+ registerAllTools(server);
642
+ async function startMcpServer() {
643
+ const transport = new StdioServerTransport();
644
+ await server.connect(transport);
645
+ console.error(`[ai-tool] MCP server v${VERSION} running via stdio`);
646
+ }
647
+ export {
648
+ startMcpServer
649
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@justmpm/ai-tool",
3
- "version": "0.8.1",
4
- "description": "Ferramenta de análise de dependências e impacto para projetos TypeScript/JavaScript. Usa Skott + Knip internamente.",
3
+ "version": "0.9.0",
4
+ "description": "Ferramenta de análise de dependências e impacto para projetos TypeScript/JavaScript. Usa Skott + Knip internamente. Inclui busca por descrição, integração Git e testes inteligentes.",
5
5
  "keywords": [
6
6
  "dependency-analysis",
7
7
  "impact-analysis",