fg-tracker-mcp 0.10.0 → 0.11.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.
- package/CHANGELOG.md +23 -0
- package/README.md +10 -3
- package/dist/cli.js +10 -6
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +39 -3
- package/dist/client.js +78 -9
- package/dist/client.js.map +1 -1
- package/dist/diagnostics.d.ts +18 -0
- package/dist/diagnostics.js +467 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/format.d.ts +25 -2
- package/dist/format.js +33 -6
- package/dist/format.js.map +1 -1
- package/dist/guide.js +10 -6
- package/dist/guide.js.map +1 -1
- package/dist/infra/tools.js +21 -8
- package/dist/infra/tools.js.map +1 -1
- package/dist/inputs.d.ts +40 -0
- package/dist/inputs.js +30 -0
- package/dist/inputs.js.map +1 -0
- package/dist/journeys.d.ts +71 -0
- package/dist/journeys.js +130 -0
- package/dist/journeys.js.map +1 -0
- package/dist/links.d.ts +87 -0
- package/dist/links.js +124 -0
- package/dist/links.js.map +1 -0
- package/dist/loadtest/tools.js +14 -5
- package/dist/loadtest/tools.js.map +1 -1
- package/dist/schemas.d.ts +621 -0
- package/dist/schemas.js +277 -0
- package/dist/schemas.js.map +1 -0
- package/dist/server.d.ts +11 -2
- package/dist/server.js +379 -112
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
package/dist/inputs.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Parâmetros de entrada comuns às ferramentas tracker_* (o mesmo texto em todas). */
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { RANGE_KEYS } from './client.js';
|
|
4
|
+
export const rangeSchema = z.enum(RANGE_KEYS).default('24h').describe('Janela: 15m|1h|6h|24h|7d|30d (default 24h)');
|
|
5
|
+
export const projectIdSchema = z.string().uuid().describe('ID (uuid) do projeto — veja tracker_list_projects');
|
|
6
|
+
export const rootKindSchema = z
|
|
7
|
+
.enum(['http', 'custom', 'browser', 'all'])
|
|
8
|
+
.optional()
|
|
9
|
+
.describe('Tipo da raiz: http = requisições (default), custom = jobs/tarefas (tracker.span("custom", ...) sem pai), browser = raízes do SDK de navegador, all = qualquer raiz');
|
|
10
|
+
export const serviceFilterSchema = z.string().min(1).max(120).optional().describe('Só execuções deste serviço (serviceName da raiz, igualdade exata; valores em `services` de tracker_get_overview)');
|
|
11
|
+
export const envFilterSchema = z.string().min(1).max(64).optional().describe('Só execuções deste ambiente (environment da raiz, igualdade exata; valores em tracker_list_environments)');
|
|
12
|
+
/** Instante ISO 8601 com fuso (`2026-10-15T09:00:00Z`, `2026-10-15T06:00:00-03:00`). */
|
|
13
|
+
export const isoInstantSchema = z.string().datetime({ offset: true });
|
|
14
|
+
/** `from`/`to` absolutos das leituras de métricas (a API aceita até 31 dias; `range` vale quando falta `from`). */
|
|
15
|
+
export const fromSchema = isoInstantSchema.optional().describe('Início absoluto ISO 8601 (ex.: 2026-10-15T09:00:00Z); com from/to o range só vale quando falta from. Até 31 dias');
|
|
16
|
+
export const toSchema = isoInstantSchema.optional().describe('Fim absoluto ISO 8601 (default agora)');
|
|
17
|
+
/** Descreve os filtros de raiz aplicados, para as mensagens de resumo. */
|
|
18
|
+
export function filterNote(filters) {
|
|
19
|
+
const parts = [
|
|
20
|
+
filters.rootKind && filters.rootKind !== 'http' ? `rootKind ${filters.rootKind}` : '',
|
|
21
|
+
filters.service ? `service ${filters.service}` : '',
|
|
22
|
+
filters.env ? `env ${filters.env}` : '',
|
|
23
|
+
].filter(Boolean);
|
|
24
|
+
return parts.length ? ` (${parts.join(', ')})` : '';
|
|
25
|
+
}
|
|
26
|
+
/** Filtros de raiz informados (sem as chaves ausentes), para o `structuredContent`. */
|
|
27
|
+
export function definedFilters(filters) {
|
|
28
|
+
return Object.fromEntries(Object.entries(filters).filter(([, value]) => value !== undefined));
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=inputs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inputs.js","sourceRoot":"","sources":["../src/inputs.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,UAAU,EAAiB,MAAM,aAAa,CAAC;AAExD,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC,IAAI,CAAC,UAAuC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,4CAA4C,CAAC,CAAC;AACjJ,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,mDAAmD,CAAC,CAAC;AAC/G,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC;KAC5B,IAAI,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;KAC1C,QAAQ,EAAE;KACV,QAAQ,CAAC,oKAAoK,CAAC,CAAC;AAClL,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kHAAkH,CAAC,CAAC;AACtM,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,0GAA0G,CAAC,CAAC;AAEzL,wFAAwF;AACxF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AAEtE,mHAAmH;AACnH,MAAM,CAAC,MAAM,UAAU,GAAG,gBAAgB,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kHAAkH,CAAC,CAAC;AACnL,MAAM,CAAC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uCAAuC,CAAC,CAAC;AAEtG,0EAA0E;AAC1E,MAAM,UAAU,UAAU,CAAC,OAA8D;IACvF,MAAM,KAAK,GAAG;QACZ,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE;QACrF,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE;QACnD,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE;KACxC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAClB,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;AACtD,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,cAAc,CAAC,OAA8D;IAC3F,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAA0D,CAAC;AACzJ,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompts das cinco jornadas de diagnóstico (docs/mcp.md, "Jornadas de diagnóstico"): cada um
|
|
3
|
+
* dá a sequência de ferramentas (até 6 chamadas), o critério de parada e como citar a evidência.
|
|
4
|
+
* Só usam ferramentas de leitura: valem com token pessoal `fgt_…` e com FG_TRACKER_READ_ONLY=1.
|
|
5
|
+
*/
|
|
6
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
export interface JourneyArgs {
|
|
9
|
+
projectId?: string;
|
|
10
|
+
endpoint?: string;
|
|
11
|
+
release?: string;
|
|
12
|
+
env?: string;
|
|
13
|
+
query?: string;
|
|
14
|
+
monitor?: string;
|
|
15
|
+
deployedAt?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function regressionPrompt(args: JourneyArgs): string;
|
|
18
|
+
export declare function slowQueryPrompt(args: JourneyArgs): string;
|
|
19
|
+
export declare function lateJobPrompt(args: JourneyArgs): string;
|
|
20
|
+
export declare function distributedErrorPrompt(args: JourneyArgs): string;
|
|
21
|
+
export declare function confirmFixPrompt(args: JourneyArgs): string;
|
|
22
|
+
export declare const JOURNEYS: readonly [{
|
|
23
|
+
readonly name: "investigar-regressao-p95";
|
|
24
|
+
readonly title: "Investigar regressão de p95 após um deploy";
|
|
25
|
+
readonly build: typeof regressionPrompt;
|
|
26
|
+
readonly args: {
|
|
27
|
+
readonly projectId: z.ZodOptional<z.ZodString>;
|
|
28
|
+
readonly endpoint: z.ZodOptional<z.ZodString>;
|
|
29
|
+
readonly release: z.ZodOptional<z.ZodString>;
|
|
30
|
+
readonly env: z.ZodOptional<z.ZodString>;
|
|
31
|
+
};
|
|
32
|
+
}, {
|
|
33
|
+
readonly name: "investigar-query-lenta";
|
|
34
|
+
readonly title: "Investigar query lenta ou N+1";
|
|
35
|
+
readonly build: typeof slowQueryPrompt;
|
|
36
|
+
readonly args: {
|
|
37
|
+
readonly projectId: z.ZodOptional<z.ZodString>;
|
|
38
|
+
readonly query: z.ZodOptional<z.ZodString>;
|
|
39
|
+
readonly env: z.ZodOptional<z.ZodString>;
|
|
40
|
+
};
|
|
41
|
+
}, {
|
|
42
|
+
readonly name: "investigar-job-atrasado";
|
|
43
|
+
readonly title: "Investigar job atrasado ou que não rodou";
|
|
44
|
+
readonly build: typeof lateJobPrompt;
|
|
45
|
+
readonly args: {
|
|
46
|
+
readonly projectId: z.ZodOptional<z.ZodString>;
|
|
47
|
+
readonly monitor: z.ZodOptional<z.ZodString>;
|
|
48
|
+
readonly env: z.ZodOptional<z.ZodString>;
|
|
49
|
+
};
|
|
50
|
+
}, {
|
|
51
|
+
readonly name: "investigar-erro-distribuido";
|
|
52
|
+
readonly title: "Investigar erro distribuído (API → serviços)";
|
|
53
|
+
readonly build: typeof distributedErrorPrompt;
|
|
54
|
+
readonly args: {
|
|
55
|
+
readonly projectId: z.ZodOptional<z.ZodString>;
|
|
56
|
+
readonly endpoint: z.ZodOptional<z.ZodString>;
|
|
57
|
+
readonly env: z.ZodOptional<z.ZodString>;
|
|
58
|
+
};
|
|
59
|
+
}, {
|
|
60
|
+
readonly name: "confirmar-correcao";
|
|
61
|
+
readonly title: "Confirmar que uma correção funcionou";
|
|
62
|
+
readonly build: typeof confirmFixPrompt;
|
|
63
|
+
readonly args: {
|
|
64
|
+
readonly projectId: z.ZodOptional<z.ZodString>;
|
|
65
|
+
readonly endpoint: z.ZodOptional<z.ZodString>;
|
|
66
|
+
readonly release: z.ZodOptional<z.ZodString>;
|
|
67
|
+
readonly deployedAt: z.ZodOptional<z.ZodString>;
|
|
68
|
+
readonly env: z.ZodOptional<z.ZodString>;
|
|
69
|
+
};
|
|
70
|
+
}];
|
|
71
|
+
export declare function registerJourneyPrompts(server: McpServer): void;
|
package/dist/journeys.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const COMMON_RULES = [
|
|
3
|
+
'Regras:',
|
|
4
|
+
'- No máximo 6 chamadas de ferramenta. Não abra o painel: tudo sai das ferramentas tracker_*.',
|
|
5
|
+
'- Toda conclusão cita pelo menos um item de `evidence` (traceId, spanId quando houver, métrica, valor e janela) devolvido por uma ferramenta nesta conversa. Sem evidência, diga que não há dado suficiente, não chute.',
|
|
6
|
+
'- Se o projeto tem mais de um ambiente, filtre por env (tracker_list_environments diz quais existem). Quando uma resposta trouxer `filtersApplied: false`, avise que os números misturam ambientes.',
|
|
7
|
+
'- Percentis de janelas longas (tracker_endpoint_history, releases) podem ser aproximados nesta API; prefira os traces e tracker_get_endpoint para a janela recente.',
|
|
8
|
+
];
|
|
9
|
+
function header(title, args) {
|
|
10
|
+
const known = [
|
|
11
|
+
args.projectId ? `projectId ${args.projectId}` : 'projectId: descubra com tracker_list_projects (não conta como passo se o usuário já informou)',
|
|
12
|
+
args.env ? `ambiente ${args.env}` : '',
|
|
13
|
+
args.endpoint ? `endpoint/job "${args.endpoint}"` : '',
|
|
14
|
+
args.release ? `release ${args.release}` : '',
|
|
15
|
+
args.query ? `query contendo "${args.query}"` : '',
|
|
16
|
+
args.monitor ? `monitor ${args.monitor}` : '',
|
|
17
|
+
args.deployedAt ? `deploy em ${args.deployedAt}` : '',
|
|
18
|
+
].filter(Boolean);
|
|
19
|
+
return [`# ${title}`, '', `Dados: ${known.join(' · ')}.`, ''];
|
|
20
|
+
}
|
|
21
|
+
const p = (args) => (args.projectId ? `"${args.projectId}"` : '<projectId>');
|
|
22
|
+
const envArg = (args, key = 'env') => (args.env ? `, ${key}: "${args.env}"` : '');
|
|
23
|
+
export function regressionPrompt(args) {
|
|
24
|
+
const name = args.endpoint ? `"${args.endpoint}"` : '<rota com maior delta>';
|
|
25
|
+
return [
|
|
26
|
+
...header('Investigar regressão de p95 depois de um deploy', args),
|
|
27
|
+
'Objetivo: dizer qual rota piorou, em qual release, e onde o tempo a mais está (banco, chamada externa, código sem span), com traces de prova.',
|
|
28
|
+
'',
|
|
29
|
+
'Sequência:',
|
|
30
|
+
`1. tracker_list_releases { projectId: ${p(args)}${envArg(args, 'environment')} } — ${args.release ? `a release ${args.release} e a anterior a ela` : 'a release com maior delta de p95 e a anterior a ela'}.`,
|
|
31
|
+
`2. tracker_compare { projectId: ${p(args)}, a: { release: <anterior> }, b: { release: ${args.release ? `"${args.release}"` : '<nova>'} }${args.endpoint ? `, endpoint: "${args.endpoint}"` : ''}${envArg(args)} } — rotas que mais pioraram, onde o tempo cresceu por tipo de span (banco, chamadas externas, custom, sem span) e os spans que mais cresceram, com evidence.`,
|
|
32
|
+
'3. tracker_get_trace { projectId, traceId: <evidence do span que mais cresceu>, name: <nome do span> } — confirme no waterfall.',
|
|
33
|
+
`4. (se a decomposição vier null: um lado sem spans brutos) tracker_get_endpoint { projectId: ${p(args)}, name: ${name}, from: <deploy>${envArg(args)} } e tracker_get_trace do mais lento.`,
|
|
34
|
+
`5. (se precisar) tracker_endpoint_history { projectId: ${p(args)}, name: ${name}, days: 7${envArg(args)} } — a piora começou no deploy (markers) e continua no ponto mais recente?`,
|
|
35
|
+
'',
|
|
36
|
+
'Pare quando: tracker_compare mostrar o tipo de span ou o span que explica a maior parte do aumento de p95 e um trace da evidence confirmar; ou quando as 6 chamadas acabarem — então liste o que falta.',
|
|
37
|
+
'Cuidado: com coverage "none" em um lado, não há decomposição — não conclua regressão por tipo de span sem cobertura.',
|
|
38
|
+
'',
|
|
39
|
+
...COMMON_RULES,
|
|
40
|
+
].join('\n');
|
|
41
|
+
}
|
|
42
|
+
export function slowQueryPrompt(args) {
|
|
43
|
+
return [
|
|
44
|
+
...header('Investigar query lenta ou N+1', args),
|
|
45
|
+
'Objetivo: achar a query que mais custa, dizer se é uma execução lenta (p99/máximo), repetição (N+1) ou espera de lock, e quem a chama.',
|
|
46
|
+
'',
|
|
47
|
+
'Sequência:',
|
|
48
|
+
args.env ? '' : `1. tracker_list_environments { projectId: ${p(args)} } — escolha o ambiente (produção) se houver mais de um.`,
|
|
49
|
+
`${args.env ? '1' : '2'}. tracker_list_queries { projectId: ${p(args)}, range: "24h"${envArg(args)}${args.query ? `, search: "${args.query}"` : ''} } — top por tempo total, p95/p99/máximo, chamadores e evidence (trace e span da execução mais lenta).`,
|
|
50
|
+
`${args.env ? '2' : '3'}. tracker_get_trace { projectId, traceId: <evidence[0].traceId>, kind: "db.query" } — veja se a query aparece repetida (summary.repeated, evidence repeated.count) ou uma vez lenta.`,
|
|
51
|
+
`${args.env ? '3' : '4'}. (se repetida) tracker_list_issues { projectId, type: ["N_PLUS_ONE", "LOOP"], q: <trecho da query> } — a issue já detectada, com contagem e commits suspeitos via tracker_get_issue.`,
|
|
52
|
+
'',
|
|
53
|
+
'Classifique: nome com pg_advisory_lock/FOR UPDATE/LOCK = espera de lock (o tempo é de outra transação); repeated.count alto = N+1 (agrupe numa consulta); p99/máximo muito acima do p95 = outliers (procure o parâmetro no trace).',
|
|
54
|
+
'Pare quando: a query, o chamador e um trace com o span (traceId + spanId) estiverem identificados e classificados.',
|
|
55
|
+
'Limite atual: o SDK corta o SQL em 200 caracteres; duas queries com o mesmo início aparecem juntas.',
|
|
56
|
+
'',
|
|
57
|
+
...COMMON_RULES,
|
|
58
|
+
]
|
|
59
|
+
.filter((line) => line !== '')
|
|
60
|
+
.join('\n');
|
|
61
|
+
}
|
|
62
|
+
export function lateJobPrompt(args) {
|
|
63
|
+
const monitor = args.monitor ? `slug: "${args.monitor}"` : 'monitorId: <id do monitor missed/down>';
|
|
64
|
+
return [
|
|
65
|
+
...header('Investigar job atrasado ou que não rodou', args),
|
|
66
|
+
'Objetivo: dizer se o job rodou, quando devia ter rodado, quanto atrasou, por que falhou e se alguém foi alertado.',
|
|
67
|
+
'',
|
|
68
|
+
'Sequência:',
|
|
69
|
+
`1. tracker_list_monitors { projectId: ${p(args)}${envArg(args, 'environment')} } — status (missed/down), último check e próximo prazo.`,
|
|
70
|
+
`2. tracker_get_monitor { projectId: ${p(args)}, ${monitor}${envArg(args, 'environment')} } — checks com traceId, horário esperado e atraso de cada execução (delayMs), schedule.overdueMs (quanto já passou do prazo) e runningSince (execução ainda aberta). evidence traz os checks que falharam.`,
|
|
71
|
+
'3. tracker_get_trace { projectId, traceId: <evidence[0].traceId> } — onde a execução falhou ou demorou.',
|
|
72
|
+
'4. tracker_search_logs { projectId, traceId: <o mesmo> } — erros da execução.',
|
|
73
|
+
`5. tracker_list_alert_events { projectId: ${p(args)}, type: ["monitor_down"] } — se e quando o alerta disparou e se a entrega saiu.`,
|
|
74
|
+
'',
|
|
75
|
+
'Pare quando: houver o check que falhou ou faltou (com o trace, se o job reporta trace), a causa no trace/log e o estado do alerta.',
|
|
76
|
+
'Cuidado: um job longo ainda em execução pode aparecer como missed; confira runningSince antes de concluir que não rodou.',
|
|
77
|
+
'',
|
|
78
|
+
...COMMON_RULES,
|
|
79
|
+
].join('\n');
|
|
80
|
+
}
|
|
81
|
+
export function distributedErrorPrompt(args) {
|
|
82
|
+
return [
|
|
83
|
+
...header('Investigar erro distribuído (API → serviços)', args),
|
|
84
|
+
'Objetivo: a partir de requisições com erro, achar o serviço e o span onde a falha começou e o log ou a issue que a explica.',
|
|
85
|
+
'',
|
|
86
|
+
'Sequência:',
|
|
87
|
+
`1. tracker_list_traces { projectId: ${p(args)}, rootKind: "all", hasSpan: { status: "error" }, sort: "duration", range: "24h"${envArg(args)}${args.endpoint ? `, endpoint: "${args.endpoint}"` : ''} } — traces com algum span com erro (inclusive filhos e outros serviços), mais lentos primeiro. Use hasSpan.service para ficar num serviço.`,
|
|
88
|
+
'2. tracker_get_trace { projectId, traceId: <um deles> } — `services` mostra os serviços do trace; evidence traz os spans que falharam (statusCode/error) com o serviço.',
|
|
89
|
+
'3. tracker_search_logs { projectId, traceId: <o mesmo> } — logs de todos os serviços do trace.',
|
|
90
|
+
'4. tracker_list_issues { projectId, type: ["ERROR"], q: <mensagem ou nome do span> } e, se preciso, tracker_get_issue — impacto, releases e commits suspeitos.',
|
|
91
|
+
'',
|
|
92
|
+
'Pare quando: o primeiro span com erro (o mais profundo/antigo) estiver identificado com serviço, mensagem e um log ou issue que confirme.',
|
|
93
|
+
'Limite atual: erros do navegador ainda não se ligam ao trace da API (o SDK de navegador cria um trace próprio); esta jornada cobre API → serviços.',
|
|
94
|
+
'',
|
|
95
|
+
...COMMON_RULES,
|
|
96
|
+
].join('\n');
|
|
97
|
+
}
|
|
98
|
+
export function confirmFixPrompt(args) {
|
|
99
|
+
const name = args.endpoint ? `"${args.endpoint}"` : '<rota ou job corrigido>';
|
|
100
|
+
const since = args.deployedAt ? `"${args.deployedAt}"` : '<instante do deploy, ISO>';
|
|
101
|
+
return [
|
|
102
|
+
...header('Confirmar que uma correção funcionou', args),
|
|
103
|
+
'Objetivo: comparar antes e depois do deploy da correção e dizer, com evidência, se o problema parou.',
|
|
104
|
+
'',
|
|
105
|
+
'Sequência:',
|
|
106
|
+
`1. tracker_list_releases { projectId: ${p(args)}${envArg(args, 'environment')}${args.release ? `, version: "${args.release}"` : ''} } — saúde da release nova contra a anterior (p95, erro, issues novas, regressões).`,
|
|
107
|
+
`2. tracker_compare { projectId: ${p(args)}, a: { from: <deploy − mesma duração>, to: ${since} }, b: { from: ${since} }${args.endpoint ? `, endpoint: "${args.endpoint}"` : ''}${envArg(args)} } — antes × depois: p95, erro e onde o tempo mudou, com traces de prova (ou tracker_get_endpoint { name: ${name}, from: ${since} } para só a rota).`,
|
|
108
|
+
`3. tracker_log_stats { projectId: ${p(args)}, from: <deploy − mesma duração>, to: <agora>, level: "error"${envArg(args)} } — quando os erros pararam (último bucket com erro antes ou depois de ${since}).`,
|
|
109
|
+
`4. tracker_list_alert_events { projectId: ${p(args)}, from: ${since} } — nenhum alerta novo do tipo, ou o episódio resolvido.`,
|
|
110
|
+
'5. (se havia issue) tracker_get_issue { projectId, issueId } — a última ocorrência é anterior ao deploy?',
|
|
111
|
+
'',
|
|
112
|
+
'Pare quando: as janelas depois do deploy mostram o alvo atingido (p95/erro), sem alerta novo, com evidência de um trace recente bom; ou quando uma delas mostrar que o problema continua (aí cite o trace ruim recente).',
|
|
113
|
+
'',
|
|
114
|
+
...COMMON_RULES,
|
|
115
|
+
].join('\n');
|
|
116
|
+
}
|
|
117
|
+
const optional = (description) => z.string().max(512).optional().describe(description);
|
|
118
|
+
export const JOURNEYS = [
|
|
119
|
+
{ name: 'investigar-regressao-p95', title: 'Investigar regressão de p95 após um deploy', build: regressionPrompt, args: { projectId: optional('ID do projeto'), endpoint: optional('Rota suspeita (opcional)'), release: optional('Versão do deploy (opcional)'), env: optional('Ambiente, ex.: production') } },
|
|
120
|
+
{ name: 'investigar-query-lenta', title: 'Investigar query lenta ou N+1', build: slowQueryPrompt, args: { projectId: optional('ID do projeto'), query: optional('Trecho do SQL (opcional)'), env: optional('Ambiente, ex.: production') } },
|
|
121
|
+
{ name: 'investigar-job-atrasado', title: 'Investigar job atrasado ou que não rodou', build: lateJobPrompt, args: { projectId: optional('ID do projeto'), monitor: optional('Slug do monitor (opcional)'), env: optional('Ambiente, ex.: production') } },
|
|
122
|
+
{ name: 'investigar-erro-distribuido', title: 'Investigar erro distribuído (API → serviços)', build: distributedErrorPrompt, args: { projectId: optional('ID do projeto'), endpoint: optional('Rota ou job com erro (opcional)'), env: optional('Ambiente, ex.: production') } },
|
|
123
|
+
{ name: 'confirmar-correcao', title: 'Confirmar que uma correção funcionou', build: confirmFixPrompt, args: { projectId: optional('ID do projeto'), endpoint: optional('Rota ou job corrigido'), release: optional('Versão com a correção'), deployedAt: optional('Instante do deploy (ISO 8601)'), env: optional('Ambiente, ex.: production') } },
|
|
124
|
+
];
|
|
125
|
+
export function registerJourneyPrompts(server) {
|
|
126
|
+
for (const journey of JOURNEYS) {
|
|
127
|
+
server.registerPrompt(journey.name, { title: journey.title, description: `${journey.title}: sequência de ferramentas (até 6 chamadas), critério de parada e evidência exigida.`, argsSchema: journey.args }, (args) => ({ messages: [{ role: 'user', content: { type: 'text', text: journey.build(args) } }] }));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=journeys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journeys.js","sourceRoot":"","sources":["../src/journeys.ts"],"names":[],"mappings":"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"}
|
package/dist/links.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Janelas, evidências e links das ferramentas de diagnóstico (docs/mcp.md, "Evidências e links").
|
|
3
|
+
*
|
|
4
|
+
* - `evidence[]` aponta só para traces que a API devolveu nesta chamada: o agente pode abrir
|
|
5
|
+
* cada um (tracker_get_trace ou o recurso fg-tracker://trace/{traceId}) e conferir o número.
|
|
6
|
+
* - Links do painel só saem quando a URL do painel é conhecida (FG_TRACKER_WEB_URL ou derivada
|
|
7
|
+
* da API); senão `dashboard: null`.
|
|
8
|
+
*/
|
|
9
|
+
import { type QueryValue, type RangeKey, type TrackClient } from './client.js';
|
|
10
|
+
import { type ResourceLink } from './format.js';
|
|
11
|
+
import type { Evidence } from './schemas.js';
|
|
12
|
+
export interface Window {
|
|
13
|
+
from: string;
|
|
14
|
+
to: string;
|
|
15
|
+
}
|
|
16
|
+
export interface Links {
|
|
17
|
+
dashboard: string | null;
|
|
18
|
+
resource?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Templates dos recursos. `?projectId=` é um sufixo opcional (os links que o servidor gera
|
|
22
|
+
* sempre o trazem); sem ele, a leitura procura o id nos projetos acessíveis.
|
|
23
|
+
*/
|
|
24
|
+
export declare const TRACE_URI_TEMPLATE = "fg-tracker://trace/{traceId}";
|
|
25
|
+
export declare const ISSUE_URI_TEMPLATE = "fg-tracker://issue/{issueId}";
|
|
26
|
+
/** Id e `projectId` opcional de `fg-tracker://<tipo>/<id>[?projectId=…]`. */
|
|
27
|
+
export declare function parseResourceUri(uri: URL): {
|
|
28
|
+
id: string;
|
|
29
|
+
projectId: string | undefined;
|
|
30
|
+
};
|
|
31
|
+
export declare function traceUri(projectId: string, traceId: string): string;
|
|
32
|
+
export declare function issueUri(projectId: string, issueId: string): string;
|
|
33
|
+
/** Janela `[agora − range, agora]` como a API calcula. */
|
|
34
|
+
export declare function rangeWindow(range: RangeKey, now?: number): Window;
|
|
35
|
+
/** Maior janela absoluta aceita pela API de logs (`logs-query.service.ts`, 31 dias). */
|
|
36
|
+
export declare const LOGS_MAX_WINDOW_DAYS = 31;
|
|
37
|
+
/**
|
|
38
|
+
* Janela de uma consulta com `from`/`to` opcionais, como a API de logs calcula: sem os dois,
|
|
39
|
+
* `[agora − range, agora]`; só `to`, a janela `range` que termina em `to`; só `from`, de `from`
|
|
40
|
+
* até agora. Validada aqui para o erro sair antes da chamada.
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolveWindow(range: RangeKey, from?: string, to?: string, maxDays?: number | null, now?: number): Window;
|
|
43
|
+
export interface WindowedResult<T> {
|
|
44
|
+
body: T;
|
|
45
|
+
window: Window;
|
|
46
|
+
/** from/to pedidos e a API não confirmou (API anterior à janela absoluta): os números são do `range`. */
|
|
47
|
+
windowIgnored: boolean;
|
|
48
|
+
/** Parâmetros extras que a API declarou ter aplicado (`x-fg-tracker-capabilities`). */
|
|
49
|
+
capabilities: Set<string>;
|
|
50
|
+
}
|
|
51
|
+
export declare const WINDOW_IGNORED_NOTE = "ATEN\u00C7\u00C3O: esta API ignorou from/to (vers\u00E3o anterior \u00E0 janela absoluta); os n\u00FAmeros s\u00E3o do range relativo.";
|
|
52
|
+
/**
|
|
53
|
+
* GET de uma leitura de métricas com `range` ou `from`/`to` (validados aqui, até 31 dias). A
|
|
54
|
+
* janela devolvida é a que a API confirmou no cabeçalho; sem a confirmação, `windowIgnored`.
|
|
55
|
+
*/
|
|
56
|
+
export declare function getWithWindow<T>(client: TrackClient, path: string, query: Record<string, QueryValue>, range: RangeKey, from?: string, to?: string): Promise<WindowedResult<T>>;
|
|
57
|
+
/** Intervalo de um span ou trace: `[startedAt, startedAt + durationMs]`. */
|
|
58
|
+
export declare function spanWindow(startedAt: string, durationMs: number): Window;
|
|
59
|
+
/** Página do painel (`/trace/?project=…&trace=…`), ou `null` sem URL do painel. */
|
|
60
|
+
export declare function dashboardUrl(client: TrackClient, page: string, params: Record<string, string>): string | null;
|
|
61
|
+
export declare function traceLinks(client: TrackClient, projectId: string, traceId: string): {
|
|
62
|
+
links: Links;
|
|
63
|
+
resourceLinks: ResourceLink[];
|
|
64
|
+
};
|
|
65
|
+
export declare function issueLinks(client: TrackClient, projectId: string, issueId: string): {
|
|
66
|
+
links: Links;
|
|
67
|
+
resourceLinks: ResourceLink[];
|
|
68
|
+
};
|
|
69
|
+
export declare function pageLinks(client: TrackClient, page: string, params: Record<string, string>, name: string): {
|
|
70
|
+
links: Links;
|
|
71
|
+
resourceLinks: ResourceLink[];
|
|
72
|
+
};
|
|
73
|
+
/** Links `fg-tracker://trace/…` das evidências (sem repetir trace), até `max`. */
|
|
74
|
+
export declare function evidenceResourceLinks(projectId: string, evidence: Evidence[], max?: number): ResourceLink[];
|
|
75
|
+
interface TraceLike {
|
|
76
|
+
traceId: string;
|
|
77
|
+
startedAt: string;
|
|
78
|
+
durationMs: number;
|
|
79
|
+
name?: string;
|
|
80
|
+
statusCode?: number | null;
|
|
81
|
+
}
|
|
82
|
+
export declare function traceEvidence(item: TraceLike, metric?: string, value?: number | string, note?: string): Evidence;
|
|
83
|
+
/** Os `max` traces mais lentos de uma lista, como evidência de duração. */
|
|
84
|
+
export declare function tracesEvidence(items: TraceLike[], max?: number): Evidence[];
|
|
85
|
+
/** Resumo em texto das evidências, para clientes que só mostram o `content`. */
|
|
86
|
+
export declare function evidenceLines(evidence: Evidence[], max?: number): string;
|
|
87
|
+
export {};
|
package/dist/links.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Janelas, evidências e links das ferramentas de diagnóstico (docs/mcp.md, "Evidências e links").
|
|
3
|
+
*
|
|
4
|
+
* - `evidence[]` aponta só para traces que a API devolveu nesta chamada: o agente pode abrir
|
|
5
|
+
* cada um (tracker_get_trace ou o recurso fg-tracker://trace/{traceId}) e conferir o número.
|
|
6
|
+
* - Links do painel só saem quando a URL do painel é conhecida (FG_TRACKER_WEB_URL ou derivada
|
|
7
|
+
* da API); senão `dashboard: null`.
|
|
8
|
+
*/
|
|
9
|
+
import { RANGE_SECONDS } from './client.js';
|
|
10
|
+
import { ms } from './format.js';
|
|
11
|
+
/**
|
|
12
|
+
* Templates dos recursos. `?projectId=` é um sufixo opcional (os links que o servidor gera
|
|
13
|
+
* sempre o trazem); sem ele, a leitura procura o id nos projetos acessíveis.
|
|
14
|
+
*/
|
|
15
|
+
export const TRACE_URI_TEMPLATE = 'fg-tracker://trace/{traceId}';
|
|
16
|
+
export const ISSUE_URI_TEMPLATE = 'fg-tracker://issue/{issueId}';
|
|
17
|
+
/** Id e `projectId` opcional de `fg-tracker://<tipo>/<id>[?projectId=…]`. */
|
|
18
|
+
export function parseResourceUri(uri) {
|
|
19
|
+
return { id: decodeURIComponent(uri.pathname.replace(/^\/+/, '')), projectId: uri.searchParams.get('projectId') ?? undefined };
|
|
20
|
+
}
|
|
21
|
+
export function traceUri(projectId, traceId) {
|
|
22
|
+
return `fg-tracker://trace/${encodeURIComponent(traceId)}?projectId=${encodeURIComponent(projectId)}`;
|
|
23
|
+
}
|
|
24
|
+
export function issueUri(projectId, issueId) {
|
|
25
|
+
return `fg-tracker://issue/${encodeURIComponent(issueId)}?projectId=${encodeURIComponent(projectId)}`;
|
|
26
|
+
}
|
|
27
|
+
/** Janela `[agora − range, agora]` como a API calcula. */
|
|
28
|
+
export function rangeWindow(range, now = Date.now()) {
|
|
29
|
+
return { from: new Date(now - RANGE_SECONDS[range] * 1000).toISOString(), to: new Date(now).toISOString() };
|
|
30
|
+
}
|
|
31
|
+
/** Maior janela absoluta aceita pela API de logs (`logs-query.service.ts`, 31 dias). */
|
|
32
|
+
export const LOGS_MAX_WINDOW_DAYS = 31;
|
|
33
|
+
/**
|
|
34
|
+
* Janela de uma consulta com `from`/`to` opcionais, como a API de logs calcula: sem os dois,
|
|
35
|
+
* `[agora − range, agora]`; só `to`, a janela `range` que termina em `to`; só `from`, de `from`
|
|
36
|
+
* até agora. Validada aqui para o erro sair antes da chamada.
|
|
37
|
+
*/
|
|
38
|
+
export function resolveWindow(range, from, to, maxDays = LOGS_MAX_WINDOW_DAYS, now = Date.now()) {
|
|
39
|
+
if (!from && !to)
|
|
40
|
+
return rangeWindow(range, now);
|
|
41
|
+
const end = to ? Date.parse(to) : now;
|
|
42
|
+
const start = from ? Date.parse(from) : end - RANGE_SECONDS[range] * 1000;
|
|
43
|
+
if (end <= start)
|
|
44
|
+
throw new Error(`\`to\` (${to ?? 'agora'}) precisa ser depois de \`from\` (${from}).`);
|
|
45
|
+
if (maxDays !== null && end - start > maxDays * 86_400_000)
|
|
46
|
+
throw new Error(`Janela de ${Math.ceil((end - start) / 86_400_000)} dias: o máximo é ${maxDays}. Encurte from/to.`);
|
|
47
|
+
return { from: new Date(start).toISOString(), to: new Date(end).toISOString() };
|
|
48
|
+
}
|
|
49
|
+
export const WINDOW_IGNORED_NOTE = 'ATENÇÃO: esta API ignorou from/to (versão anterior à janela absoluta); os números são do range relativo.';
|
|
50
|
+
/**
|
|
51
|
+
* GET de uma leitura de métricas com `range` ou `from`/`to` (validados aqui, até 31 dias). A
|
|
52
|
+
* janela devolvida é a que a API confirmou no cabeçalho; sem a confirmação, `windowIgnored`.
|
|
53
|
+
*/
|
|
54
|
+
export async function getWithWindow(client, path, query, range, from, to) {
|
|
55
|
+
const absolute = Boolean(from || to);
|
|
56
|
+
const requested = resolveWindow(range, from, to);
|
|
57
|
+
const res = await client.getWindowed(path, { ...query, range, ...(absolute ? requested : {}) });
|
|
58
|
+
if (!absolute)
|
|
59
|
+
return { body: res.body, window: requested, windowIgnored: false, capabilities: res.capabilities };
|
|
60
|
+
return { body: res.body, window: res.window ?? rangeWindow(range), windowIgnored: res.window === null, capabilities: res.capabilities };
|
|
61
|
+
}
|
|
62
|
+
/** Intervalo de um span ou trace: `[startedAt, startedAt + durationMs]`. */
|
|
63
|
+
export function spanWindow(startedAt, durationMs) {
|
|
64
|
+
const start = Date.parse(startedAt);
|
|
65
|
+
if (Number.isNaN(start))
|
|
66
|
+
return { from: startedAt, to: startedAt };
|
|
67
|
+
return { from: new Date(start).toISOString(), to: new Date(start + Math.max(0, durationMs)).toISOString() };
|
|
68
|
+
}
|
|
69
|
+
/** Página do painel (`/trace/?project=…&trace=…`), ou `null` sem URL do painel. */
|
|
70
|
+
export function dashboardUrl(client, page, params) {
|
|
71
|
+
const web = client.webUrl;
|
|
72
|
+
if (!web)
|
|
73
|
+
return null;
|
|
74
|
+
const query = new URLSearchParams(params).toString();
|
|
75
|
+
return `${web}/${page}/${query ? `?${query}` : ''}`;
|
|
76
|
+
}
|
|
77
|
+
function resourceLinks(links, name, description) {
|
|
78
|
+
const out = [];
|
|
79
|
+
if (links.dashboard)
|
|
80
|
+
out.push({ type: 'resource_link', uri: links.dashboard, name: `${name} (painel)`, description: `${description} no painel do FG Tracker`, mimeType: 'text/html' });
|
|
81
|
+
if (links.resource)
|
|
82
|
+
out.push({ type: 'resource_link', uri: links.resource, name, description: `${description} (JSON, resources/read)`, mimeType: 'application/json' });
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
export function traceLinks(client, projectId, traceId) {
|
|
86
|
+
const links = { dashboard: dashboardUrl(client, 'trace', { project: projectId, trace: traceId }), resource: traceUri(projectId, traceId) };
|
|
87
|
+
return { links, resourceLinks: resourceLinks(links, `trace ${traceId}`, 'Waterfall do trace') };
|
|
88
|
+
}
|
|
89
|
+
export function issueLinks(client, projectId, issueId) {
|
|
90
|
+
const links = { dashboard: dashboardUrl(client, 'issue', { project: projectId, issue: issueId }), resource: issueUri(projectId, issueId) };
|
|
91
|
+
return { links, resourceLinks: resourceLinks(links, `issue ${issueId}`, 'Issue') };
|
|
92
|
+
}
|
|
93
|
+
export function pageLinks(client, page, params, name) {
|
|
94
|
+
const links = { dashboard: dashboardUrl(client, page, params) };
|
|
95
|
+
return { links, resourceLinks: resourceLinks(links, name, name) };
|
|
96
|
+
}
|
|
97
|
+
/** Links `fg-tracker://trace/…` das evidências (sem repetir trace), até `max`. */
|
|
98
|
+
export function evidenceResourceLinks(projectId, evidence, max = 3) {
|
|
99
|
+
const seen = new Set();
|
|
100
|
+
const out = [];
|
|
101
|
+
for (const item of evidence) {
|
|
102
|
+
if (seen.has(item.traceId) || out.length >= max)
|
|
103
|
+
continue;
|
|
104
|
+
seen.add(item.traceId);
|
|
105
|
+
out.push({ type: 'resource_link', uri: traceUri(projectId, item.traceId), name: `trace ${item.traceId}`, description: `Evidência: ${item.metric} = ${item.value}`, mimeType: 'application/json' });
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
export function traceEvidence(item, metric = 'durationMs', value = item.durationMs, note) {
|
|
110
|
+
const label = note ?? item.name;
|
|
111
|
+
return { traceId: item.traceId, metric, value, window: spanWindow(item.startedAt, item.durationMs), ...(label ? { note: label } : {}) };
|
|
112
|
+
}
|
|
113
|
+
/** Os `max` traces mais lentos de uma lista, como evidência de duração. */
|
|
114
|
+
export function tracesEvidence(items, max = 5) {
|
|
115
|
+
return [...items].sort((a, b) => b.durationMs - a.durationMs).slice(0, max).map((item) => traceEvidence(item));
|
|
116
|
+
}
|
|
117
|
+
/** Resumo em texto das evidências, para clientes que só mostram o `content`. */
|
|
118
|
+
export function evidenceLines(evidence, max = 5) {
|
|
119
|
+
if (!evidence.length)
|
|
120
|
+
return '';
|
|
121
|
+
const fmt = (e) => (typeof e.value === 'number' && /ms$/i.test(e.metric) ? ms(e.value) : String(e.value));
|
|
122
|
+
return `\nEvidência: ${evidence.slice(0, max).map((e) => `trace ${e.traceId}${e.spanId ? ` span ${e.spanId}` : ''} (${e.metric} ${fmt(e)})`).join('; ')}${evidence.length > max ? ` (+${evidence.length - max})` : ''}`;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"links.js","sourceRoot":"","sources":["../src/links.ts"],"names":[],"mappings":"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"}
|
package/dist/loadtest/tools.js
CHANGED
|
@@ -6,7 +6,8 @@ import { stat } from 'node:fs/promises';
|
|
|
6
6
|
import { resolve } from 'node:path';
|
|
7
7
|
import { z } from 'zod';
|
|
8
8
|
import { TrackApiError } from '../client.js';
|
|
9
|
-
import { guard, json, text } from '../format.js';
|
|
9
|
+
import { guard, json, structured, text } from '../format.js';
|
|
10
|
+
import { planOutput, routesOutput, scenariosOutput } from '../schemas.js';
|
|
10
11
|
import { formatRouteScan, scanRoutes } from './routes.js';
|
|
11
12
|
import { PRESETS, describePlan, planWarnings, presetPlan, validatePlan, validateScenario, } from './scenario.js';
|
|
12
13
|
const projectIdSchema = z.string().uuid().describe('ID (uuid) do projeto — veja tracker_list_projects');
|
|
@@ -210,10 +211,14 @@ export function registerLoadtestTools(server, client) {
|
|
|
210
211
|
path: z.string().optional().describe('Diretório do app/serviço (default: diretório atual)'),
|
|
211
212
|
format: z.enum(['text', 'json']).default('text').describe('text (compacto) ou json (estrutura completa)'),
|
|
212
213
|
},
|
|
214
|
+
outputSchema: routesOutput,
|
|
213
215
|
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
214
216
|
}, async ({ path, format }) => guard(async () => {
|
|
215
217
|
const scan = await scanRoutes(await resolveRoot(path));
|
|
216
|
-
|
|
218
|
+
if (format === 'json')
|
|
219
|
+
return { ...json(scan), structuredContent: { format, scan } };
|
|
220
|
+
const report = formatRouteScan(scan);
|
|
221
|
+
return structured({ format, report }, report, { body: null });
|
|
217
222
|
}));
|
|
218
223
|
server.registerTool('loadtest_create_scenario', {
|
|
219
224
|
title: 'Criar roteiro de simulação (jornada)',
|
|
@@ -269,16 +274,19 @@ export function registerLoadtestTools(server, client) {
|
|
|
269
274
|
title: 'Listar roteiros de simulação',
|
|
270
275
|
description: 'GET /v1/projects/:id/loadtest/scenarios: id, nome, origem (mcp|editor), passos e atualização.',
|
|
271
276
|
inputSchema: { projectId: projectIdSchema },
|
|
277
|
+
outputSchema: scenariosOutput,
|
|
272
278
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
273
279
|
}, async ({ projectId }) => guard(async () => {
|
|
274
280
|
const list = asList(await client.get(`${base(projectId)}/scenarios`));
|
|
275
281
|
if (list.length === 0)
|
|
276
|
-
return
|
|
282
|
+
return structured({ total: 0, items: [] }, 'Nenhum roteiro. Crie um com o prompt create-load-test ou `loadtest_create_scenario`.', { body: null });
|
|
277
283
|
const lines = list.map((s) => {
|
|
278
284
|
const doc = s.document;
|
|
279
285
|
return `- ${String(s.name ?? '?')} · id ${String(s.id ?? '?')} · ${String(s.source ?? '?')}${doc?.steps ? ` · ${doc.setup?.length ?? 0} setup + ${doc.steps.length} passos` : ''}${s.updatedAt ? ` · atualizado ${String(s.updatedAt)}` : ''}`;
|
|
280
286
|
});
|
|
281
|
-
|
|
287
|
+
// O documento inteiro fica de fora: o resumo de passos basta para escolher o roteiro.
|
|
288
|
+
const items = list.map(({ document, ...rest }) => ({ ...rest, steps: document?.steps?.length ?? null }));
|
|
289
|
+
return structured({ total: list.length, items }, `${list.length} roteiro(s):\n${lines.join('\n')}`, { body: null });
|
|
282
290
|
}));
|
|
283
291
|
server.registerTool('loadtest_suggest_plan', {
|
|
284
292
|
title: 'Sugerir plano de carga (preset k6)',
|
|
@@ -289,12 +297,13 @@ export function registerLoadtestTools(server, client) {
|
|
|
289
297
|
targetRps: z.number().positive().optional().describe('Alvo de iterações por segundo (usa arrival-rate)'),
|
|
290
298
|
duration: z.string().optional().describe('Duração total no formato k6 ("10m", "2h")'),
|
|
291
299
|
},
|
|
300
|
+
outputSchema: planOutput,
|
|
292
301
|
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
293
302
|
}, async ({ preset, vus, targetRps, duration }) => guard(async () => {
|
|
294
303
|
const plan = presetPlan(preset, { vus, targetRps, duration });
|
|
295
304
|
const shape = describePlan(plan);
|
|
296
305
|
const warnings = planWarnings(plan);
|
|
297
|
-
return
|
|
306
|
+
return structured({ preset, plan: plan, warnings }, [`Plano ${preset}: ${shape.executor} · pico ${shape.peakVus} VUs${shape.peakIterationsPerSecond !== null ? ` / ${shape.peakIterationsPerSecond} it/s` : ''} · ${shape.duration}.`, ...warnings.map((w) => `Atenção: ${w}`), 'Ajuste thresholds ao SLO do produto. Na UI, "Avaliar com IA" confere o plano contra os limites da conta.'].join('\n'), { body: plan });
|
|
298
307
|
}));
|
|
299
308
|
server.registerTool('loadtest_get_run', {
|
|
300
309
|
title: 'Resultado de uma simulação',
|