@arthysis/mcp-server 1.0.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 (3) hide show
  1. package/README.md +91 -0
  2. package/dist/index.js +580 -0
  3. package/package.json +52 -0
package/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # ArthySis MCP Server
2
+
3
+ Servidor MCP (Model Context Protocol) para integração do [ArthySis](https://tasks.arthysis.com.br) com Claude Desktop.
4
+
5
+ ## Instalação
6
+
7
+ ```bash
8
+ npm install -g @arthysis/mcp-server
9
+ ```
10
+
11
+ ## Configuração
12
+
13
+ ### 1. Obtenha sua API Key
14
+
15
+ Acesse o painel do ArthySis em **Configurações > Integrações > API Keys** e gere uma nova chave.
16
+
17
+ ### 2. Configure o Claude Desktop
18
+
19
+ Edite o arquivo de configuração do Claude Desktop:
20
+
21
+ - **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
22
+ - **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
23
+
24
+ Adicione a configuração:
25
+
26
+ ```json
27
+ {
28
+ "mcpServers": {
29
+ "arthysis": {
30
+ "command": "arthysis-mcp",
31
+ "env": {
32
+ "ARTHYSIS_API_KEY": "sua_api_key_aqui"
33
+ }
34
+ }
35
+ }
36
+ }
37
+ ```
38
+
39
+ ### 3. Reinicie o Claude Desktop
40
+
41
+ Feche e abra novamente o Claude Desktop para carregar o MCP Server.
42
+
43
+ ## Ferramentas Disponíveis
44
+
45
+ O MCP Server disponibiliza as seguintes ferramentas para o Claude:
46
+
47
+ ### Clientes
48
+ - `list_clients` - Lista todos os clientes
49
+ - `get_client` - Busca um cliente por ID ou nome
50
+ - `create_client` - Cria um novo cliente
51
+
52
+ ### Projetos
53
+ - `list_projects` - Lista todos os projetos
54
+ - `get_project` - Busca um projeto por ID ou nome
55
+ - `create_project` - Cria um novo projeto
56
+
57
+ ### Tarefas
58
+ - `list_tasks` - Lista tarefas de um projeto
59
+ - `get_task` - Busca uma tarefa por ID
60
+ - `create_task` - Cria uma nova tarefa
61
+ - `create_tasks_batch` - Cria múltiplas tarefas de uma vez
62
+ - `update_task` - Atualiza uma tarefa
63
+ - `batch_update_status` - Atualiza status de múltiplas tarefas
64
+
65
+ ### Estatísticas
66
+ - `get_stats` - Retorna estatísticas gerais do sistema
67
+
68
+ ## Exemplos de Uso no Claude
69
+
70
+ Após configurar, você pode pedir ao Claude:
71
+
72
+ - "Liste meus clientes no ArthySis"
73
+ - "Crie um projeto chamado 'Website Novo' para o cliente João"
74
+ - "Adicione 5 tarefas de desenvolvimento no projeto Website Novo"
75
+ - "Mostre as estatísticas do meu sistema"
76
+
77
+ ## Variáveis de Ambiente
78
+
79
+ | Variável | Descrição | Obrigatório |
80
+ |----------|-----------|-------------|
81
+ | `ARTHYSIS_API_KEY` | Sua chave de API do ArthySis | Sim |
82
+ | `ARTHYSIS_API_URL` | URL da API (padrão: https://tasks.arthysis.com.br/api/v1/external) | Não |
83
+
84
+ ## Suporte
85
+
86
+ - Site: https://tasks.arthysis.com.br
87
+ - Email: suporte@arthysis.com.br
88
+
89
+ ## Licença
90
+
91
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,580 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
+ // Configuração - será sobrescrita por variáveis de ambiente
6
+ const API_BASE_URL = process.env.ARTHYSIS_API_URL || 'https://tasks.arthysis.com.br/api/v1/external';
7
+ const API_KEY = process.env.ARTHYSIS_API_KEY || '';
8
+ // Helper para fazer requisições à API
9
+ async function apiRequest(endpoint, options = {}) {
10
+ const url = `${API_BASE_URL}${endpoint}`;
11
+ const response = await fetch(url, {
12
+ ...options,
13
+ headers: {
14
+ 'Content-Type': 'application/json',
15
+ 'X-API-Key': API_KEY,
16
+ ...options.headers,
17
+ },
18
+ });
19
+ if (!response.ok) {
20
+ const error = await response.json().catch(() => ({ error: response.statusText }));
21
+ throw new Error(`API Error: ${error.error || response.statusText}`);
22
+ }
23
+ return response.json();
24
+ }
25
+ // Criar servidor MCP
26
+ const server = new Server({
27
+ name: 'arthysis-mcp',
28
+ version: '1.0.0',
29
+ }, {
30
+ capabilities: {
31
+ tools: {},
32
+ },
33
+ });
34
+ // Listar ferramentas disponíveis
35
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
36
+ return {
37
+ tools: [
38
+ // ========== CLIENTES ==========
39
+ {
40
+ name: 'list_clients',
41
+ description: 'Lista todos os clientes do sistema ArthySis. Retorna nome, email, telefone e quantidade de projetos.',
42
+ inputSchema: {
43
+ type: 'object',
44
+ properties: {
45
+ search: {
46
+ type: 'string',
47
+ description: 'Termo de busca para filtrar clientes por nome',
48
+ },
49
+ page: {
50
+ type: 'number',
51
+ description: 'Número da página (padrão: 1)',
52
+ },
53
+ limit: {
54
+ type: 'number',
55
+ description: 'Quantidade de resultados por página (padrão: 50)',
56
+ },
57
+ },
58
+ },
59
+ },
60
+ {
61
+ name: 'get_client',
62
+ description: 'Busca um cliente específico pelo ID ou pelo nome exato.',
63
+ inputSchema: {
64
+ type: 'object',
65
+ properties: {
66
+ id: {
67
+ type: 'string',
68
+ description: 'ID do cliente',
69
+ },
70
+ name: {
71
+ type: 'string',
72
+ description: 'Nome exato do cliente (alternativa ao ID)',
73
+ },
74
+ },
75
+ },
76
+ },
77
+ {
78
+ name: 'create_client',
79
+ description: 'Cria um novo cliente no sistema ArthySis.',
80
+ inputSchema: {
81
+ type: 'object',
82
+ properties: {
83
+ name: {
84
+ type: 'string',
85
+ description: 'Nome do cliente (obrigatório)',
86
+ },
87
+ email: {
88
+ type: 'string',
89
+ description: 'Email do cliente',
90
+ },
91
+ phone: {
92
+ type: 'string',
93
+ description: 'Telefone do cliente',
94
+ },
95
+ },
96
+ required: ['name'],
97
+ },
98
+ },
99
+ // ========== PROJETOS ==========
100
+ {
101
+ name: 'list_projects',
102
+ description: 'Lista todos os projetos do sistema ArthySis. Pode filtrar por cliente.',
103
+ inputSchema: {
104
+ type: 'object',
105
+ properties: {
106
+ clientId: {
107
+ type: 'string',
108
+ description: 'ID do cliente para filtrar projetos',
109
+ },
110
+ status: {
111
+ type: 'string',
112
+ description: 'Status do projeto para filtrar',
113
+ },
114
+ search: {
115
+ type: 'string',
116
+ description: 'Termo de busca para filtrar projetos por nome',
117
+ },
118
+ page: {
119
+ type: 'number',
120
+ description: 'Número da página (padrão: 1)',
121
+ },
122
+ limit: {
123
+ type: 'number',
124
+ description: 'Quantidade de resultados por página (padrão: 50)',
125
+ },
126
+ },
127
+ },
128
+ },
129
+ {
130
+ name: 'get_project',
131
+ description: 'Busca um projeto específico pelo ID ou pelo nome.',
132
+ inputSchema: {
133
+ type: 'object',
134
+ properties: {
135
+ id: {
136
+ type: 'string',
137
+ description: 'ID do projeto',
138
+ },
139
+ name: {
140
+ type: 'string',
141
+ description: 'Nome do projeto (alternativa ao ID)',
142
+ },
143
+ clientId: {
144
+ type: 'string',
145
+ description: 'ID do cliente (ajuda a filtrar quando busca por nome)',
146
+ },
147
+ },
148
+ },
149
+ },
150
+ {
151
+ name: 'create_project',
152
+ description: 'Cria um novo projeto no sistema ArthySis.',
153
+ inputSchema: {
154
+ type: 'object',
155
+ properties: {
156
+ clientId: {
157
+ type: 'string',
158
+ description: 'ID do cliente (obrigatório)',
159
+ },
160
+ name: {
161
+ type: 'string',
162
+ description: 'Nome do projeto (obrigatório)',
163
+ },
164
+ description: {
165
+ type: 'string',
166
+ description: 'Descrição do projeto',
167
+ },
168
+ },
169
+ required: ['clientId', 'name'],
170
+ },
171
+ },
172
+ // ========== TAREFAS ==========
173
+ {
174
+ name: 'list_tasks',
175
+ description: 'Lista todas as tarefas de um projeto específico. Inclui campos de fase e horas.',
176
+ inputSchema: {
177
+ type: 'object',
178
+ properties: {
179
+ projectId: {
180
+ type: 'string',
181
+ description: 'ID do projeto (obrigatório)',
182
+ },
183
+ statusId: {
184
+ type: 'string',
185
+ description: 'ID do status para filtrar',
186
+ },
187
+ phase: {
188
+ type: 'string',
189
+ description: 'Filtrar por fase (ex: "Fase 1", "Design")',
190
+ },
191
+ page: {
192
+ type: 'number',
193
+ description: 'Número da página (padrão: 1)',
194
+ },
195
+ limit: {
196
+ type: 'number',
197
+ description: 'Quantidade de resultados por página (padrão: 100)',
198
+ },
199
+ },
200
+ required: ['projectId'],
201
+ },
202
+ },
203
+ {
204
+ name: 'get_task',
205
+ description: 'Busca uma tarefa específica pelo ID.',
206
+ inputSchema: {
207
+ type: 'object',
208
+ properties: {
209
+ id: {
210
+ type: 'string',
211
+ description: 'ID da tarefa (obrigatório)',
212
+ },
213
+ },
214
+ required: ['id'],
215
+ },
216
+ },
217
+ {
218
+ name: 'create_task',
219
+ description: 'Cria uma nova tarefa em um projeto. Suporta campos de fase e horas para organização e controle de tempo.',
220
+ inputSchema: {
221
+ type: 'object',
222
+ properties: {
223
+ projectId: {
224
+ type: 'string',
225
+ description: 'ID do projeto (obrigatório)',
226
+ },
227
+ title: {
228
+ type: 'string',
229
+ description: 'Título da tarefa (obrigatório)',
230
+ },
231
+ description: {
232
+ type: 'string',
233
+ description: 'Descrição detalhada da tarefa',
234
+ },
235
+ statusId: {
236
+ type: 'string',
237
+ description: 'ID do status inicial',
238
+ },
239
+ dueDate: {
240
+ type: 'string',
241
+ description: 'Data de vencimento (formato ISO: 2024-12-31)',
242
+ },
243
+ phase: {
244
+ type: 'string',
245
+ description: 'Nome da fase para agrupamento (ex: "Fase 1", "Design", "Desenvolvimento")',
246
+ },
247
+ phaseOrder: {
248
+ type: 'number',
249
+ description: 'Ordem dentro da fase (ex: 1, 2, 3)',
250
+ },
251
+ estimatedHours: {
252
+ type: 'number',
253
+ description: 'Horas estimadas para a tarefa',
254
+ },
255
+ actualHours: {
256
+ type: 'number',
257
+ description: 'Horas já realizadas na tarefa',
258
+ },
259
+ },
260
+ required: ['projectId', 'title'],
261
+ },
262
+ },
263
+ {
264
+ name: 'create_tasks_batch',
265
+ description: 'Cria múltiplas tarefas de uma vez em um projeto. Ideal para criar várias tarefas rapidamente. Suporta campos de fase e horas.',
266
+ inputSchema: {
267
+ type: 'object',
268
+ properties: {
269
+ projectId: {
270
+ type: 'string',
271
+ description: 'ID do projeto (obrigatório)',
272
+ },
273
+ tasks: {
274
+ type: 'array',
275
+ description: 'Array de tarefas para criar',
276
+ items: {
277
+ type: 'object',
278
+ properties: {
279
+ title: {
280
+ type: 'string',
281
+ description: 'Título da tarefa (obrigatório)',
282
+ },
283
+ description: {
284
+ type: 'string',
285
+ description: 'Descrição da tarefa',
286
+ },
287
+ statusId: {
288
+ type: 'string',
289
+ description: 'ID do status',
290
+ },
291
+ dueDate: {
292
+ type: 'string',
293
+ description: 'Data de vencimento',
294
+ },
295
+ phase: {
296
+ type: 'string',
297
+ description: 'Nome da fase para agrupamento',
298
+ },
299
+ phaseOrder: {
300
+ type: 'number',
301
+ description: 'Ordem dentro da fase',
302
+ },
303
+ estimatedHours: {
304
+ type: 'number',
305
+ description: 'Horas estimadas',
306
+ },
307
+ actualHours: {
308
+ type: 'number',
309
+ description: 'Horas realizadas',
310
+ },
311
+ },
312
+ required: ['title'],
313
+ },
314
+ },
315
+ },
316
+ required: ['projectId', 'tasks'],
317
+ },
318
+ },
319
+ {
320
+ name: 'update_task',
321
+ description: 'Atualiza uma tarefa existente. Suporta campos de fase e horas.',
322
+ inputSchema: {
323
+ type: 'object',
324
+ properties: {
325
+ id: {
326
+ type: 'string',
327
+ description: 'ID da tarefa (obrigatório)',
328
+ },
329
+ title: {
330
+ type: 'string',
331
+ description: 'Novo título',
332
+ },
333
+ description: {
334
+ type: 'string',
335
+ description: 'Nova descrição',
336
+ },
337
+ statusId: {
338
+ type: 'string',
339
+ description: 'Novo status',
340
+ },
341
+ dueDate: {
342
+ type: 'string',
343
+ description: 'Nova data de vencimento (ou null para remover)',
344
+ },
345
+ phase: {
346
+ type: 'string',
347
+ description: 'Nome da fase (ou null para remover)',
348
+ },
349
+ phaseOrder: {
350
+ type: 'number',
351
+ description: 'Ordem dentro da fase (ou null para remover)',
352
+ },
353
+ estimatedHours: {
354
+ type: 'number',
355
+ description: 'Horas estimadas (ou null para remover)',
356
+ },
357
+ actualHours: {
358
+ type: 'number',
359
+ description: 'Horas realizadas (ou null para remover)',
360
+ },
361
+ },
362
+ required: ['id'],
363
+ },
364
+ },
365
+ {
366
+ name: 'batch_update_status',
367
+ description: 'Atualiza o status de múltiplas tarefas de uma vez.',
368
+ inputSchema: {
369
+ type: 'object',
370
+ properties: {
371
+ taskIds: {
372
+ type: 'array',
373
+ items: { type: 'string' },
374
+ description: 'Array de IDs das tarefas (obrigatório)',
375
+ },
376
+ statusId: {
377
+ type: 'string',
378
+ description: 'ID do novo status (obrigatório)',
379
+ },
380
+ },
381
+ required: ['taskIds', 'statusId'],
382
+ },
383
+ },
384
+ // ========== ESTATÍSTICAS ==========
385
+ {
386
+ name: 'get_stats',
387
+ description: 'Retorna estatísticas gerais do sistema: total de clientes, projetos e tarefas.',
388
+ inputSchema: {
389
+ type: 'object',
390
+ properties: {},
391
+ },
392
+ },
393
+ ],
394
+ };
395
+ });
396
+ // Handler para executar ferramentas
397
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
398
+ const { name, arguments: args } = request.params;
399
+ try {
400
+ let result;
401
+ switch (name) {
402
+ // ========== CLIENTES ==========
403
+ case 'list_clients': {
404
+ const params = new URLSearchParams();
405
+ if (args?.search)
406
+ params.append('search', String(args.search));
407
+ if (args?.page)
408
+ params.append('page', String(args.page));
409
+ if (args?.limit)
410
+ params.append('limit', String(args.limit));
411
+ result = await apiRequest(`/clients?${params}`);
412
+ break;
413
+ }
414
+ case 'get_client': {
415
+ if (args?.id) {
416
+ result = await apiRequest(`/clients/${args.id}`);
417
+ }
418
+ else if (args?.name) {
419
+ result = await apiRequest(`/clients/search?name=${encodeURIComponent(String(args.name))}`);
420
+ }
421
+ else {
422
+ throw new Error('Informe o id ou name do cliente');
423
+ }
424
+ break;
425
+ }
426
+ case 'create_client': {
427
+ result = await apiRequest('/clients', {
428
+ method: 'POST',
429
+ body: JSON.stringify({
430
+ name: args?.name,
431
+ email: args?.email,
432
+ phone: args?.phone,
433
+ }),
434
+ });
435
+ break;
436
+ }
437
+ // ========== PROJETOS ==========
438
+ case 'list_projects': {
439
+ const params = new URLSearchParams();
440
+ if (args?.clientId)
441
+ params.append('clientId', String(args.clientId));
442
+ if (args?.status)
443
+ params.append('status', String(args.status));
444
+ if (args?.search)
445
+ params.append('search', String(args.search));
446
+ if (args?.page)
447
+ params.append('page', String(args.page));
448
+ if (args?.limit)
449
+ params.append('limit', String(args.limit));
450
+ result = await apiRequest(`/projects?${params}`);
451
+ break;
452
+ }
453
+ case 'get_project': {
454
+ if (args?.id) {
455
+ result = await apiRequest(`/projects/${args.id}`);
456
+ }
457
+ else if (args?.name) {
458
+ const params = new URLSearchParams({ name: String(args.name) });
459
+ if (args?.clientId)
460
+ params.append('clientId', String(args.clientId));
461
+ result = await apiRequest(`/projects/search?${params}`);
462
+ }
463
+ else {
464
+ throw new Error('Informe o id ou name do projeto');
465
+ }
466
+ break;
467
+ }
468
+ case 'create_project': {
469
+ result = await apiRequest('/projects', {
470
+ method: 'POST',
471
+ body: JSON.stringify({
472
+ clientId: args?.clientId,
473
+ name: args?.name,
474
+ description: args?.description,
475
+ }),
476
+ });
477
+ break;
478
+ }
479
+ // ========== TAREFAS ==========
480
+ case 'list_tasks': {
481
+ const params = new URLSearchParams();
482
+ if (args?.statusId)
483
+ params.append('statusId', String(args.statusId));
484
+ if (args?.phase)
485
+ params.append('phase', String(args.phase));
486
+ if (args?.page)
487
+ params.append('page', String(args.page));
488
+ if (args?.limit)
489
+ params.append('limit', String(args.limit));
490
+ result = await apiRequest(`/projects/${args?.projectId}/tasks?${params}`);
491
+ break;
492
+ }
493
+ case 'get_task': {
494
+ result = await apiRequest(`/tasks/${args?.id}`);
495
+ break;
496
+ }
497
+ case 'create_task': {
498
+ result = await apiRequest(`/projects/${args?.projectId}/tasks`, {
499
+ method: 'POST',
500
+ body: JSON.stringify({
501
+ title: args?.title,
502
+ description: args?.description,
503
+ statusId: args?.statusId,
504
+ dueDate: args?.dueDate,
505
+ phase: args?.phase,
506
+ phaseOrder: args?.phaseOrder,
507
+ estimatedHours: args?.estimatedHours,
508
+ actualHours: args?.actualHours,
509
+ }),
510
+ });
511
+ break;
512
+ }
513
+ case 'create_tasks_batch': {
514
+ result = await apiRequest(`/projects/${args?.projectId}/tasks/batch`, {
515
+ method: 'POST',
516
+ body: JSON.stringify({
517
+ tasks: args?.tasks,
518
+ }),
519
+ });
520
+ break;
521
+ }
522
+ case 'update_task': {
523
+ const { id, ...data } = args;
524
+ result = await apiRequest(`/tasks/${id}`, {
525
+ method: 'PATCH',
526
+ body: JSON.stringify(data),
527
+ });
528
+ break;
529
+ }
530
+ case 'batch_update_status': {
531
+ result = await apiRequest('/tasks/batch-status', {
532
+ method: 'PATCH',
533
+ body: JSON.stringify({
534
+ taskIds: args?.taskIds,
535
+ statusId: args?.statusId,
536
+ }),
537
+ });
538
+ break;
539
+ }
540
+ // ========== ESTATÍSTICAS ==========
541
+ case 'get_stats': {
542
+ result = await apiRequest('/stats');
543
+ break;
544
+ }
545
+ default:
546
+ throw new Error(`Ferramenta desconhecida: ${name}`);
547
+ }
548
+ return {
549
+ content: [
550
+ {
551
+ type: 'text',
552
+ text: JSON.stringify(result, null, 2),
553
+ },
554
+ ],
555
+ };
556
+ }
557
+ catch (error) {
558
+ const errorMessage = error instanceof Error ? error.message : 'Erro desconhecido';
559
+ return {
560
+ content: [
561
+ {
562
+ type: 'text',
563
+ text: `Erro: ${errorMessage}`,
564
+ },
565
+ ],
566
+ isError: true,
567
+ };
568
+ }
569
+ });
570
+ // Iniciar servidor
571
+ async function main() {
572
+ if (!API_KEY) {
573
+ console.error('ERRO: Variável de ambiente ARTHYSIS_API_KEY não configurada');
574
+ process.exit(1);
575
+ }
576
+ const transport = new StdioServerTransport();
577
+ await server.connect(transport);
578
+ console.error('ArthySis MCP Server iniciado');
579
+ }
580
+ main().catch(console.error);
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@arthysis/mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "MCP Server para integração do ArthySis com Claude Desktop",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "arthysis-mcp": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist/index.js",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "prepublishOnly": "npm run build",
17
+ "start": "node dist/index.js",
18
+ "start:http": "node dist/http-server.js",
19
+ "dev": "tsx src/index.ts",
20
+ "dev:http": "tsx src/http-server.ts"
21
+ },
22
+ "keywords": [
23
+ "mcp",
24
+ "claude",
25
+ "arthysis",
26
+ "model-context-protocol",
27
+ "ai",
28
+ "tasks",
29
+ "project-management"
30
+ ],
31
+ "author": "ArthySis",
32
+ "license": "MIT",
33
+ "homepage": "https://tasks.arthysis.com.br",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "https://github.com/arthysis/mcp-server"
37
+ },
38
+ "engines": {
39
+ "node": ">=18.0.0"
40
+ },
41
+ "dependencies": {
42
+ "@modelcontextprotocol/sdk": "^1.0.0"
43
+ },
44
+ "devDependencies": {
45
+ "@types/express": "^4.17.21",
46
+ "@types/node": "^20.10.0",
47
+ "express": "^4.18.2",
48
+ "node-fetch": "^3.3.2",
49
+ "tsx": "^4.7.0",
50
+ "typescript": "^5.3.0"
51
+ }
52
+ }