epa-testeprojetoia 0.1.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.
Files changed (100) hide show
  1. package/.idea/epa_mcp.iml +8 -0
  2. package/.idea/modules.xml +8 -0
  3. package/.idea/php.xml +19 -0
  4. package/.idea/vcs.xml +6 -0
  5. package/AGENTS.md +10 -0
  6. package/README.md +339 -0
  7. package/dist/agent/agentHelpers.js +21 -0
  8. package/dist/agent/openaiAgent.js +170 -0
  9. package/dist/agent/slidingWindow.js +13 -0
  10. package/dist/api/epaApiClient.js +59 -0
  11. package/dist/cli/index.js +47 -0
  12. package/dist/config/credentialStore.js +92 -0
  13. package/dist/config/ensureCliAuth.js +127 -0
  14. package/dist/config/loadConfig.js +40 -0
  15. package/dist/config/setup.js +23 -0
  16. package/dist/core/createReferenceTool.js +12 -0
  17. package/dist/core/createTool.js +32 -0
  18. package/dist/mocks/requestMocks.js +47 -0
  19. package/dist/server/server.js +41 -0
  20. package/dist/server/stdioServer.js +3 -0
  21. package/dist/services/request/requestFilters.js +1 -0
  22. package/dist/services/requestService.js +81 -0
  23. package/dist/services/teamService.js +18 -0
  24. package/dist/sql/createSqlConnection.js +13 -0
  25. package/dist/sql/fetchSchemaSummary.js +38 -0
  26. package/dist/sql/generateSqlFromQuestion.js +83 -0
  27. package/dist/sql/generateSqlPlan.js +111 -0
  28. package/dist/sql/getSqlAgentErrorMessage.js +15 -0
  29. package/dist/sql/loadSqlAgentConfig.js +24 -0
  30. package/dist/sql/loadSqlConfig.js +43 -0
  31. package/dist/sql/parseSqlQuestionHints.js +29 -0
  32. package/dist/sql/runSqlAgentCli.js +163 -0
  33. package/dist/sql/runSqlCli.js +34 -0
  34. package/dist/sql/selectRelevantTables.js +136 -0
  35. package/dist/sql/sqlGuard.js +21 -0
  36. package/dist/sql/sqlPlan.js +16 -0
  37. package/dist/tests/requestService.test.js +110 -0
  38. package/dist/tools/analytics/teamReport.draft.js +16 -0
  39. package/dist/tools/loadTools.js +40 -0
  40. package/dist/tools/requests/assignees.js +50 -0
  41. package/dist/tools/requests/clients.draft.js +10 -0
  42. package/dist/tools/requests/create.draft.js +51 -0
  43. package/dist/tools/requests/list.js +50 -0
  44. package/dist/tools/requests/priorities.js +2 -0
  45. package/dist/tools/requests/services.draft.js +20 -0
  46. package/dist/tools/requests/types.js +16 -0
  47. package/dist/tools/requests/units.draft.js +10 -0
  48. package/dist/tools/requests/view.draft.js +20 -0
  49. package/dist/utils/buildDateRange.js +18 -0
  50. package/dist/utils/findIdByDescription.js +4 -0
  51. package/dist/utils/resolveAssigneeId.js +14 -0
  52. package/dist/utils/toolNameMaps.js +23 -0
  53. package/package.json +31 -0
  54. package/src/agent/agentHelpers.ts +25 -0
  55. package/src/agent/openaiAgent.ts +205 -0
  56. package/src/agent/slidingWindow.ts +17 -0
  57. package/src/api/epaApiClient.ts +82 -0
  58. package/src/cli/index.ts +61 -0
  59. package/src/config/credentialStore.ts +130 -0
  60. package/src/config/ensureCliAuth.ts +152 -0
  61. package/src/config/loadConfig.ts +62 -0
  62. package/src/config/setup.ts +35 -0
  63. package/src/core/createReferenceTool.ts +17 -0
  64. package/src/core/createTool.ts +51 -0
  65. package/src/mocks/requestMocks.ts +52 -0
  66. package/src/server/server.ts +61 -0
  67. package/src/server/stdioServer.ts +5 -0
  68. package/src/services/request/requestFilters.ts +12 -0
  69. package/src/services/requestService.ts +126 -0
  70. package/src/services/teamService.ts +27 -0
  71. package/src/sql/createSqlConnection.ts +15 -0
  72. package/src/sql/fetchSchemaSummary.ts +64 -0
  73. package/src/sql/generateSqlFromQuestion.ts +105 -0
  74. package/src/sql/generateSqlPlan.ts +133 -0
  75. package/src/sql/getSqlAgentErrorMessage.ts +24 -0
  76. package/src/sql/loadSqlAgentConfig.ts +33 -0
  77. package/src/sql/loadSqlConfig.ts +75 -0
  78. package/src/sql/parseSqlQuestionHints.ts +46 -0
  79. package/src/sql/runSqlAgentCli.ts +204 -0
  80. package/src/sql/runSqlCli.ts +40 -0
  81. package/src/sql/selectRelevantTables.ts +184 -0
  82. package/src/sql/sqlGuard.ts +28 -0
  83. package/src/sql/sqlPlan.ts +28 -0
  84. package/src/tests/requestService.test.ts +152 -0
  85. package/src/tools/analytics/teamReport.draft.ts +25 -0
  86. package/src/tools/loadTools.ts +59 -0
  87. package/src/tools/requests/assignees.ts +59 -0
  88. package/src/tools/requests/clients.draft.ts +18 -0
  89. package/src/tools/requests/create.draft.ts +59 -0
  90. package/src/tools/requests/list.ts +57 -0
  91. package/src/tools/requests/priorities.ts +6 -0
  92. package/src/tools/requests/services.draft.ts +24 -0
  93. package/src/tools/requests/types.ts +18 -0
  94. package/src/tools/requests/units.draft.ts +18 -0
  95. package/src/tools/requests/view.draft.ts +27 -0
  96. package/src/utils/buildDateRange.ts +22 -0
  97. package/src/utils/findIdByDescription.ts +10 -0
  98. package/src/utils/resolveAssigneeId.ts +24 -0
  99. package/src/utils/toolNameMaps.ts +33 -0
  100. package/tsconfig.json +11 -0
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="WEB_MODULE" version="4">
3
+ <component name="NewModuleRootManager">
4
+ <content url="file://$MODULE_DIR$" />
5
+ <orderEntry type="inheritedJdk" />
6
+ <orderEntry type="sourceFolder" forTests="false" />
7
+ </component>
8
+ </module>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/epa_mcp.iml" filepath="$PROJECT_DIR$/.idea/epa_mcp.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
package/.idea/php.xml ADDED
@@ -0,0 +1,19 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="MessDetectorOptionsConfiguration">
4
+ <option name="transferred" value="true" />
5
+ </component>
6
+ <component name="PHPCSFixerOptionsConfiguration">
7
+ <option name="transferred" value="true" />
8
+ </component>
9
+ <component name="PHPCodeSnifferOptionsConfiguration">
10
+ <option name="highlightLevel" value="WARNING" />
11
+ <option name="transferred" value="true" />
12
+ </component>
13
+ <component name="PhpStanOptionsConfiguration">
14
+ <option name="transferred" value="true" />
15
+ </component>
16
+ <component name="PsalmOptionsConfiguration">
17
+ <option name="transferred" value="true" />
18
+ </component>
19
+ </project>
package/.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="" vcs="Git" />
5
+ </component>
6
+ </project>
package/AGENTS.md ADDED
@@ -0,0 +1,10 @@
1
+ # AGENTS.md
2
+
3
+ ## Documentation policy
4
+
5
+ - Sempre que houver mudanca importante no projeto (novas tools, renomeacao de contratos publicos, mudanca de comandos, fluxo de setup/execucao/teste), atualize o `README.md` no mesmo trabalho.
6
+ - Nao finalize alteracoes que impactem uso do projeto sem revisar consistencia entre codigo e README.
7
+ - Priorize manter no README:
8
+ - comandos reais do `package.json`
9
+ - nomes atuais das tools MCP
10
+ - pre-requisitos de configuracao
package/README.md ADDED
@@ -0,0 +1,339 @@
1
+ # EPA MCP
2
+
3
+ Servidor MCP (Model Context Protocol) para integracao do sistema EPA com modelos de linguagem (LLMs).
4
+
5
+ Este projeto permite que assistentes de IA interajam com o EPA por meio de ferramentas estruturadas.
6
+ O objetivo e permitir que usuarios utilizem linguagem natural para operar o sistema EPA.
7
+
8
+ ## Arquitetura
9
+
10
+ Fluxo principal:
11
+
12
+ ```text
13
+ Tool -> Service -> API Client -> EPA API
14
+ ```
15
+
16
+ Camadas:
17
+
18
+ - CLI: interface de execucao local
19
+ - Server: servidor MCP
20
+ - Tools: ferramentas expostas ao agente
21
+ - Services: regras de negocio
22
+ - API Client: comunicacao com a API do EPA
23
+
24
+ ## Estrutura do projeto
25
+
26
+ ```text
27
+ src/
28
+ agent/
29
+ api/
30
+ cli/
31
+ config/
32
+ core/
33
+ services/
34
+ tools/
35
+ tests/
36
+ utils/
37
+ ```
38
+
39
+ ## Tools ativas
40
+
41
+ Solicitacoes:
42
+
43
+ - `requests.list`
44
+
45
+ Dados auxiliares:
46
+
47
+ - `requests.types`
48
+ - `requests.priorities`
49
+ - `requests.assignees`
50
+
51
+ ## Endpoints reais configurados
52
+
53
+ - `requests.types`
54
+ - `GET /epa_os/ajax.php`
55
+ - Query params: `action=get`, `controller=TipoSolicitacao`
56
+ - `requests.priorities`
57
+ - `GET /api/api/prioridade`
58
+ - `requests.assignees`
59
+ - `GET /api/api/usuarios/search`
60
+ - Query params obrigatorios: `term`, `_type=query`, `q`
61
+ - Filtro opcional preparado: `filters[unidade_gerencial][type]=pertenco` e `filters[unidade_gerencial][value]=<id>`
62
+
63
+ ### requests.list (filtros recomendados)
64
+
65
+ - `assignee_id` (opcional): ID do responsavel
66
+ - `assignee_name` (opcional): nome/login (ex: `suporte.simeon`)
67
+ - `period` (opcional): `current_year` (padrao) ou `last_12_months`
68
+
69
+ Exemplo para "listar as solicitacoes do usuario suporte.simeon do ultimo ano":
70
+
71
+ ```json
72
+ {
73
+ "assignee_name": "suporte.simeon",
74
+ "period": "last_12_months"
75
+ }
76
+ ```
77
+
78
+ ## Instalacao
79
+
80
+ ```bash
81
+ npm install
82
+ ```
83
+
84
+ ## Configuracao
85
+
86
+ Para clientes MCP (ex: Claude Desktop), configure as variaveis de ambiente:
87
+
88
+ - `epaApiUrl` (ou `EPA_API_URL`)
89
+ - `epaApiToken` (ou `EPA_API_TOKEN`)
90
+ - `epaApiTimeoutMs` (ou `EPA_API_TIMEOUT_MS`, opcional)
91
+
92
+ Opcionalmente, voce pode usar arquivo local `config/config.json`.
93
+
94
+ Exemplo:
95
+
96
+ ```json
97
+ {
98
+ "epaApiUrl": "https://dev.sysepa.com.br/epa",
99
+ "epaApiToken": "SEU_TOKEN",
100
+ "epaApiTimeoutMs": 15000
101
+ }
102
+ ```
103
+
104
+ Para uso da CLI, voce tambem pode executar o setup:
105
+
106
+ ```bash
107
+ npm run dev setup
108
+ ```
109
+
110
+ Esse comando configura a URL da API. Para login/token EPA da CLI:
111
+
112
+ ```bash
113
+ npm run dev login
114
+ ```
115
+
116
+ Os dados sao salvos em `.env` (se existir) ou `config/config.json`.
117
+
118
+ Arquivos de configuracao usados:
119
+
120
+ ```text
121
+ config/config.json
122
+ ```
123
+
124
+ ## Execucao
125
+
126
+ Iniciar MCP Server em desenvolvimento:
127
+
128
+ ```bash
129
+ npm run dev server
130
+ ```
131
+
132
+ Iniciar agente OpenAI conectado ao MCP (CLI):
133
+
134
+ ```bash
135
+ npm run dev agent
136
+ ```
137
+
138
+ Ao iniciar o agent, a CLI valida se URL/token EPA estao configurados e se o token ainda e valido.
139
+ Se estiver ausente/invalido, ela solicita login e senha para gerar novo token automaticamente.
140
+
141
+ No Windows (PowerShell com policy restritiva), use:
142
+
143
+ ```bash
144
+ npm.cmd run dev agent
145
+ ```
146
+
147
+ Se o ambiente bloquear `spawn` (erro `EPERM`, comum em ambientes restritos), use:
148
+
149
+ ```bash
150
+ npm run build
151
+ node dist/cli/index.js agent
152
+ ```
153
+
154
+ Build de producao:
155
+
156
+ ```bash
157
+ npm run build
158
+ ```
159
+
160
+ Executar CLI compilada:
161
+
162
+ ```bash
163
+ npm run start -- server
164
+ ```
165
+
166
+ ## Testes
167
+
168
+ ```bash
169
+ npm test
170
+ ```
171
+
172
+ ## SQL tecnico via CLI
173
+
174
+ Para validar consultas tecnicas em MySQL/MariaDB de forma isolada do MCP padrao, a CLI suporta um modo read-only.
175
+
176
+ Variaveis necessarias:
177
+
178
+ - `DB_HOST`
179
+ - `DB_PORT` (opcional, padrao `3306`)
180
+ - `DB_NAME`
181
+ - `DB_USER`
182
+ - `DB_PASSWORD`
183
+ - `DB_SSL` (opcional, `true` ou `false`)
184
+
185
+ Alternativamente, essas credenciais podem ficar em `config/config.json`:
186
+
187
+ ```json
188
+ {
189
+ "epaApiUrl": "https://dev.sysepa.com.br/epa",
190
+ "epaApiToken": "SEU_TOKEN",
191
+ "epaApiTimeoutMs": 15000,
192
+ "sql": {
193
+ "host": "127.0.0.1",
194
+ "port": 3306,
195
+ "database": "nome_do_banco",
196
+ "user": "usuario",
197
+ "password": "senha",
198
+ "ssl": false
199
+ }
200
+ }
201
+ ```
202
+
203
+ Exemplo:
204
+
205
+ ```bash
206
+ npm run dev sql "SELECT * FROM sua_tabela LIMIT 10"
207
+ ```
208
+
209
+ Para validar a ideia de linguagem natural com apoio da LLM:
210
+
211
+ ```bash
212
+ npm run dev sql-agent "quero listar os usuarios"
213
+ ```
214
+
215
+ Voce tambem pode orientar melhor a geracao com pistas explicitas, por exemplo:
216
+
217
+ ```bash
218
+ npm run dev sql-agent "quero listar os planos de acoes tabela: iniciativa5w2h e trazer nome do usuario que e o campo: quem e deve vincular com tabela clientes"
219
+ ```
220
+
221
+ Tambem e possivel informar filtros, ordenacao e limite explicitamente:
222
+
223
+ ```bash
224
+ npm run dev sql-agent "listar usuarios tabela: clientes filtro: ativo = 1 ordenar por: nome asc limite: 20"
225
+ ```
226
+
227
+ Nesse modo, a CLI:
228
+
229
+ - le um resumo do schema do banco
230
+ - tenta identificar as tabelas mais provaveis para a pergunta
231
+ - respeita pistas explicitas como `tabela:`, `campo:` e `vincular com tabela`
232
+ - respeita pistas explicitas como `filtro:`, `ordenar por:` e `limite:`
233
+ - quando houver ambiguidade, pergunta qual tabela deve ser usada
234
+ - se a tabela correta nao aparecer na lista, permite informar o nome manualmente
235
+ - pede para a LLM montar um plano antes da SQL
236
+ - permite aprovar, corrigir ou cancelar esse plano
237
+ - so depois gera a SQL final
238
+ - valida a SQL (somente `SELECT`)
239
+ - executa a consulta
240
+ - mostra a pergunta, a SQL gerada e o resultado
241
+
242
+ Se a consulta nao for informada no comando, a CLI pede interativamente.
243
+
244
+ Regras atuais desse modo:
245
+
246
+ - aceita somente `SELECT`
247
+ - bloqueia comandos de escrita/DDL
248
+ - aceita apenas uma consulta por vez
249
+
250
+ ## Uso do Agent CLI
251
+
252
+ 1. Instale dependencias:
253
+
254
+ ```bash
255
+ npm install
256
+ ```
257
+
258
+ 2. Configure credenciais:
259
+
260
+ ```bash
261
+ npm run dev setup
262
+ npm run dev login
263
+ ```
264
+
265
+ No fluxo de login da CLI, informe:
266
+
267
+ - login EPA
268
+ - senha EPA
269
+ - OpenAI API KEY (somente para CLI/agent, se ainda nao estiver definida)
270
+
271
+ 3. Inicie o agent:
272
+
273
+ ```bash
274
+ npm run dev agent
275
+ ```
276
+
277
+ 4. Interaja no terminal com linguagem natural. Exemplos:
278
+
279
+ - `liste minhas solicitacoes`
280
+ - `quais sao os tipos disponiveis?`
281
+ - `crie uma solicitacao para ...`
282
+
283
+ O agent escolhe e executa automaticamente as tools MCP (`requests.list`, `requests.types`, `requests.create`, etc.) conforme o contexto da conversa.
284
+
285
+ Observacao de contexto:
286
+
287
+ - O agent usa janela deslizante para limitar o historico enviado ao modelo.
288
+ - Valor padrao: `30` mensagens nao-system.
289
+ - Para ajustar: defina a variavel `AGENT_MAX_CONTEXT_MESSAGES`.
290
+
291
+ ## Uso com Claude Desktop
292
+
293
+ ### Local (sem publicar no npm)
294
+
295
+ 1. Gere o build:
296
+
297
+ ```bash
298
+ npm run build
299
+ ```
300
+
301
+ 2. Configure o `claude_desktop_config.json` para executar o entrypoint MCP direto:
302
+
303
+ ```json
304
+ {
305
+ "mcpServers": {
306
+ "epa-local": {
307
+ "command": "node",
308
+ "args": [
309
+ "C:\\Users\\renat\\OneDrive\\Documentos\\projetos\\epa_mcp\\dist\\server\\stdioServer.js"
310
+ ],
311
+ "env": {
312
+ "epaApiUrl": "https://dev.sysepa.com.br/epa",
313
+ "epaApiToken": "SEU_TOKEN",
314
+ "epaApiTimeoutMs": "15000"
315
+ }
316
+ }
317
+ }
318
+ }
319
+ ```
320
+
321
+ 3. Reinicie o Claude Desktop.
322
+
323
+ ### Publicado no npm
324
+
325
+ ```bash
326
+ npx epa-mcp
327
+ ```
328
+
329
+ CLI como extra (opcional):
330
+
331
+ ```bash
332
+ npx epa-mcp-cli setup
333
+ npx epa-mcp-cli agent
334
+ ```
335
+
336
+ ## Observacoes
337
+
338
+ - Os textos de prompt e mensagens do projeto permanecem em portugues.
339
+ - Os identificadores tecnicos (arquivos, diretorios, classes e nomes de tools) seguem padrao em ingles.
@@ -0,0 +1,21 @@
1
+ import { buildToolNameMaps as buildBaseToolNameMaps } from "../utils/toolNameMaps.js";
2
+ export function buildToolNameMaps(toolNames) {
3
+ const { internalToExternal, externalToInternal } = buildBaseToolNameMaps(toolNames);
4
+ return {
5
+ mcpToOpenAi: internalToExternal,
6
+ openAiToMcp: externalToInternal
7
+ };
8
+ }
9
+ export function isSpawnPermissionError(error) {
10
+ if (!(error instanceof Error)) {
11
+ return false;
12
+ }
13
+ const maybeCode = error.code;
14
+ return maybeCode === "EPERM" && error.message.includes("spawn");
15
+ }
16
+ export function getErrorMessage(error) {
17
+ if (error instanceof Error) {
18
+ return error.message;
19
+ }
20
+ return String(error);
21
+ }
@@ -0,0 +1,170 @@
1
+ import OpenAI from "openai";
2
+ import readline from "readline";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
6
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
7
+ import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
8
+ import { loadConfig } from "../config/loadConfig.js";
9
+ import { ensureCliAuth } from "../config/ensureCliAuth.js";
10
+ import { createMcpServer } from "../server/server.js";
11
+ import { applySlidingWindow } from "./slidingWindow.js";
12
+ import { buildToolNameMaps, getErrorMessage, isSpawnPermissionError } from "./agentHelpers.js";
13
+ const DEFAULT_MAX_NON_SYSTEM_MESSAGES = 30;
14
+ export async function startAgent() {
15
+ await ensureCliAuth();
16
+ const config = loadConfig();
17
+ const apiKey = process.env.OPENAI_API_KEY || config.openaiApiKey;
18
+ if (!apiKey) {
19
+ throw new Error("OpenAI API key nao encontrada. Defina OPENAI_API_KEY ou configure via setup.");
20
+ }
21
+ const openai = new OpenAI({ apiKey });
22
+ const currentFilePath = fileURLToPath(import.meta.url);
23
+ const isDevRuntime = currentFilePath.endsWith(".ts");
24
+ const cliEntry = path.resolve(path.dirname(currentFilePath), "..", "cli", isDevRuntime ? "index.ts" : "index.js");
25
+ const client = new Client({
26
+ name: "epa-agent",
27
+ version: "0.1.0"
28
+ }, { capabilities: {} });
29
+ try {
30
+ const transport = new StdioClientTransport(isDevRuntime
31
+ ? {
32
+ command: process.execPath,
33
+ args: ["--import", "tsx", cliEntry, "server"]
34
+ }
35
+ : {
36
+ command: process.execPath,
37
+ args: [cliEntry, "server"]
38
+ });
39
+ await client.connect(transport);
40
+ }
41
+ catch (error) {
42
+ if (!isSpawnPermissionError(error)) {
43
+ throw error;
44
+ }
45
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
46
+ const server = await createMcpServer();
47
+ await server.connect(serverTransport);
48
+ await client.connect(clientTransport);
49
+ console.log("Aviso: modo in-memory ativado por restricao de spawn no ambiente.");
50
+ }
51
+ const toolsResponse = await client.listTools();
52
+ const toolNames = toolsResponse.tools.map((tool) => tool.name);
53
+ const { mcpToOpenAi, openAiToMcp } = buildToolNameMaps(toolNames);
54
+ const tools = toolsResponse.tools.map((tool) => ({
55
+ type: "function",
56
+ function: {
57
+ name: mcpToOpenAi.get(tool.name) ?? tool.name,
58
+ description: tool.description,
59
+ parameters: tool.inputSchema
60
+ }
61
+ }));
62
+ const rl = readline.createInterface({
63
+ input: process.stdin,
64
+ output: process.stdout
65
+ });
66
+ console.log("EPA Agent iniciado");
67
+ const messages = [
68
+ {
69
+ role: "system",
70
+ content: `
71
+ Voce e um assistente que opera o sistema EPA.
72
+
73
+ Sempre utilize as ferramentas disponiveis quando necessario.
74
+
75
+ Fluxo recomendado para criar solicitacoes:
76
+
77
+ 1. consultar requests.types
78
+ 2. consultar requests.services
79
+ 3. consultar requests.priorities/requests.assignees/requests.clients
80
+ 4. executar requests.create
81
+
82
+ Para listar solicitacoes por usuario e periodo, prefira requests.list com:
83
+ - assignee_name (ex: suporte.simeon)
84
+ - period=last_12_months para "ultimo ano"
85
+
86
+ Regra para analise:
87
+ - Se voce ja recebeu uma lista de solicitacoes no contexto atual, faca a analise diretamente desses dados.
88
+ - Nao chame analytics.teamReport para analisar dados que ja estao no contexto.
89
+ - So use analytics.teamReport quando o usuario pedir explicitamente para buscar analise de um usuario especifico ou da equipe (ex: "analise da equipe", "relatorio do usuario X").
90
+ - Se analytics.teamReport nao for necessario, responda sem chamar tools.
91
+ `
92
+ }
93
+ ];
94
+ const configuredMax = Number(process.env.AGENT_MAX_CONTEXT_MESSAGES);
95
+ const maxNonSystemMessages = Number.isFinite(configuredMax) && configuredMax > 0
96
+ ? configuredMax
97
+ : DEFAULT_MAX_NON_SYSTEM_MESSAGES;
98
+ rl.on("line", async (input) => {
99
+ try {
100
+ messages.push({
101
+ role: "user",
102
+ content: input
103
+ });
104
+ applySlidingWindow(messages, maxNonSystemMessages);
105
+ let running = true;
106
+ while (running) {
107
+ const completion = await openai.chat.completions.create({
108
+ model: "gpt-4o-mini",
109
+ messages,
110
+ tools,
111
+ tool_choice: "auto"
112
+ });
113
+ const message = completion.choices[0].message;
114
+ messages.push(message);
115
+ applySlidingWindow(messages, maxNonSystemMessages);
116
+ if (!message.tool_calls) {
117
+ console.log("\n[agent]", message.content, "\n");
118
+ running = false;
119
+ break;
120
+ }
121
+ for (const toolCall of message.tool_calls) {
122
+ const openAiToolName = toolCall.function.name;
123
+ const toolName = openAiToMcp.get(openAiToolName) ?? openAiToolName;
124
+ let args;
125
+ try {
126
+ args = JSON.parse(toolCall.function.arguments || "{}");
127
+ }
128
+ catch (error) {
129
+ const errorMessage = getErrorMessage(error);
130
+ console.error(`\n[erro] Argumentos invalidos para ${toolName}: ${errorMessage}\n`);
131
+ messages.push({
132
+ role: "tool",
133
+ tool_call_id: toolCall.id,
134
+ content: JSON.stringify([{ type: "text", text: `Erro: argumentos invalidos (${errorMessage})` }])
135
+ });
136
+ applySlidingWindow(messages, maxNonSystemMessages);
137
+ continue;
138
+ }
139
+ console.log(`\n[tool] Executando: ${toolName}`);
140
+ try {
141
+ const result = await client.callTool({
142
+ name: toolName,
143
+ arguments: args
144
+ });
145
+ messages.push({
146
+ role: "tool",
147
+ tool_call_id: toolCall.id,
148
+ content: JSON.stringify(result.content)
149
+ });
150
+ applySlidingWindow(messages, maxNonSystemMessages);
151
+ }
152
+ catch (error) {
153
+ const errorMessage = getErrorMessage(error);
154
+ console.error(`\n[erro] Falha ao executar ${toolName}: ${errorMessage}\n`);
155
+ messages.push({
156
+ role: "tool",
157
+ tool_call_id: toolCall.id,
158
+ content: JSON.stringify([{ type: "text", text: `Erro ao executar tool: ${errorMessage}` }])
159
+ });
160
+ applySlidingWindow(messages, maxNonSystemMessages);
161
+ }
162
+ }
163
+ }
164
+ }
165
+ catch (error) {
166
+ const errorMessage = getErrorMessage(error);
167
+ console.error(`\n[erro] Falha no processamento da mensagem: ${errorMessage}\n`);
168
+ }
169
+ });
170
+ }
@@ -0,0 +1,13 @@
1
+ export function applySlidingWindow(messages, maxNonSystemMessages) {
2
+ if (!Number.isFinite(maxNonSystemMessages) || maxNonSystemMessages < 1) {
3
+ return;
4
+ }
5
+ const systemMessage = messages.find((message) => message?.role === "system");
6
+ const nonSystemMessages = messages.filter((message) => message?.role !== "system");
7
+ const trimmedNonSystem = nonSystemMessages.slice(-maxNonSystemMessages);
8
+ messages.length = 0;
9
+ if (systemMessage) {
10
+ messages.push(systemMessage);
11
+ }
12
+ messages.push(...trimmedNonSystem);
13
+ }
@@ -0,0 +1,59 @@
1
+ import axios from "axios";
2
+ import { loadConfig } from "../config/loadConfig.js";
3
+ export class EpaApiClient {
4
+ client;
5
+ constructor() {
6
+ const config = loadConfig();
7
+ const timeoutMs = Number(config.epaApiTimeoutMs ??
8
+ process.env.EPA_API_TIMEOUT_MS ??
9
+ 15000);
10
+ this.client = axios.create({
11
+ baseURL: config.epaApiUrl,
12
+ timeout: Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : 15000,
13
+ headers: {
14
+ Authorization: `Bearer ${config.epaApiToken}`
15
+ }
16
+ });
17
+ }
18
+ async get(endpoint, params) {
19
+ try {
20
+ const response = await this.client.get(endpoint, { params });
21
+ return response.data;
22
+ }
23
+ catch (error) {
24
+ throw this.formatError(error, "GET", endpoint);
25
+ }
26
+ }
27
+ async post(endpoint, body = {}, params) {
28
+ try {
29
+ const response = await this.client.post(endpoint, body, { params });
30
+ return response.data;
31
+ }
32
+ catch (error) {
33
+ throw this.formatError(error, "POST", endpoint);
34
+ }
35
+ }
36
+ formatError(error, method, endpoint) {
37
+ if (axios.isAxiosError(error)) {
38
+ const status = error.response?.status;
39
+ const apiMessage = this.extractApiMessage(error.response?.data);
40
+ const suffix = status ? `status ${status}` : "sem status HTTP";
41
+ return new Error(`[EPA API] ${method} ${endpoint} falhou (${suffix}): ${apiMessage}`);
42
+ }
43
+ const message = error instanceof Error ? error.message : String(error);
44
+ return new Error(`[EPA API] ${method} ${endpoint} falhou: ${message}`);
45
+ }
46
+ extractApiMessage(data) {
47
+ if (typeof data === "string" && data.trim()) {
48
+ return data;
49
+ }
50
+ if (data && typeof data === "object") {
51
+ const maybeMessage = data.message ?? data.error;
52
+ if (typeof maybeMessage === "string" && maybeMessage.trim()) {
53
+ return maybeMessage;
54
+ }
55
+ return JSON.stringify(data);
56
+ }
57
+ return "erro sem detalhes retornados pela API";
58
+ }
59
+ }