@certynix/mcp 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.
- package/CHANGELOG.md +49 -0
- package/README.md +209 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +119 -0
- package/dist/prompts/audit-report.d.ts +6 -0
- package/dist/prompts/audit-report.js +117 -0
- package/dist/prompts/security-review.d.ts +6 -0
- package/dist/prompts/security-review.js +95 -0
- package/dist/resources/alerts-active.d.ts +7 -0
- package/dist/resources/alerts-active.js +43 -0
- package/dist/resources/asset.d.ts +7 -0
- package/dist/resources/asset.js +61 -0
- package/dist/resources/organization-info.d.ts +8 -0
- package/dist/resources/organization-info.js +44 -0
- package/dist/resources/trust-score.d.ts +7 -0
- package/dist/resources/trust-score.js +46 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.js +67 -0
- package/dist/tools/create-api-key.d.ts +3 -0
- package/dist/tools/create-api-key.js +37 -0
- package/dist/tools/create-webhook.d.ts +3 -0
- package/dist/tools/create-webhook.js +45 -0
- package/dist/tools/delete-asset.d.ts +3 -0
- package/dist/tools/delete-asset.js +48 -0
- package/dist/tools/get-asset.d.ts +3 -0
- package/dist/tools/get-asset.js +41 -0
- package/dist/tools/get-trust-score.d.ts +3 -0
- package/dist/tools/get-trust-score.js +43 -0
- package/dist/tools/list-alerts.d.ts +3 -0
- package/dist/tools/list-alerts.js +55 -0
- package/dist/tools/list-api-keys.d.ts +3 -0
- package/dist/tools/list-api-keys.js +51 -0
- package/dist/tools/list-assets.d.ts +3 -0
- package/dist/tools/list-assets.js +68 -0
- package/dist/tools/list-audit-logs.d.ts +3 -0
- package/dist/tools/list-audit-logs.js +62 -0
- package/dist/tools/list-webhooks.d.ts +3 -0
- package/dist/tools/list-webhooks.js +51 -0
- package/dist/tools/register-asset.d.ts +3 -0
- package/dist/tools/register-asset.js +87 -0
- package/dist/tools/revoke-api-key.d.ts +3 -0
- package/dist/tools/revoke-api-key.js +48 -0
- package/dist/tools/verify-asset.d.ts +3 -0
- package/dist/tools/verify-asset.js +53 -0
- package/dist/tools/verify-webhook-signature.d.ts +7 -0
- package/dist/tools/verify-webhook-signature.js +178 -0
- package/dist/utils/format.d.ts +10 -0
- package/dist/utils/format.js +18 -0
- package/dist/utils/mask.d.ts +14 -0
- package/dist/utils/mask.js +42 -0
- package/eslint.config.mjs +27 -0
- package/package.json +38 -0
- package/src/index.ts +149 -0
- package/src/prompts/audit-report.ts +126 -0
- package/src/prompts/security-review.ts +102 -0
- package/src/resources/alerts-active.ts +56 -0
- package/src/resources/asset.ts +79 -0
- package/src/resources/organization-info.ts +60 -0
- package/src/resources/trust-score.ts +59 -0
- package/src/server.ts +81 -0
- package/src/tools/create-api-key.ts +52 -0
- package/src/tools/create-webhook.ts +63 -0
- package/src/tools/delete-asset.ts +63 -0
- package/src/tools/get-asset.ts +53 -0
- package/src/tools/get-trust-score.ts +57 -0
- package/src/tools/list-alerts.ts +69 -0
- package/src/tools/list-api-keys.ts +63 -0
- package/src/tools/list-assets.ts +80 -0
- package/src/tools/list-audit-logs.ts +76 -0
- package/src/tools/list-webhooks.ts +63 -0
- package/src/tools/register-asset.ts +103 -0
- package/src/tools/revoke-api-key.ts +65 -0
- package/src/tools/verify-asset.ts +66 -0
- package/src/tools/verify-webhook-signature.ts +232 -0
- package/src/utils/format.ts +20 -0
- package/src/utils/mask.ts +41 -0
- package/tests/unit/tools/register-asset.test.ts +190 -0
- package/tests/unit/tools/verify-webhook-signature.test.ts +250 -0
- package/tests/unit/utils/mask.test.ts +129 -0
- package/tsconfig.json +17 -0
- package/vitest.config.ts +19 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import eslint from '@eslint/js';
|
|
3
|
+
import tseslint from 'typescript-eslint';
|
|
4
|
+
import globals from 'globals';
|
|
5
|
+
|
|
6
|
+
export default tseslint.config(
|
|
7
|
+
{ ignores: ['dist/**', 'node_modules/**', 'eslint.config.mjs'] },
|
|
8
|
+
eslint.configs.recommended,
|
|
9
|
+
...tseslint.configs.recommendedTypeChecked,
|
|
10
|
+
{
|
|
11
|
+
languageOptions: {
|
|
12
|
+
globals: { ...globals.node },
|
|
13
|
+
parserOptions: {
|
|
14
|
+
project: './tsconfig.json',
|
|
15
|
+
tsconfigRootDir: import.meta.dirname,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
rules: {
|
|
19
|
+
'@typescript-eslint/no-explicit-any': 'error',
|
|
20
|
+
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
|
21
|
+
'@typescript-eslint/no-floating-promises': 'error',
|
|
22
|
+
'@typescript-eslint/no-unsafe-assignment': 'error',
|
|
23
|
+
'@typescript-eslint/no-unsafe-member-access': 'error',
|
|
24
|
+
'@typescript-eslint/no-unsafe-return': 'error',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
);
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@certynix/mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Official Certynix MCP Server — Expose Trust Infrastructure to AI agents",
|
|
5
|
+
"bin": {
|
|
6
|
+
"certynix-mcp": "./dist/index.js"
|
|
7
|
+
},
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"test": "vitest run",
|
|
13
|
+
"lint": "eslint src",
|
|
14
|
+
"type-check": "tsc --noEmit",
|
|
15
|
+
"start": "node dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@certynix/sdk": "file:../sdk-node",
|
|
19
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"typescript": "^5.4.0",
|
|
23
|
+
"vitest": "^2.0.0",
|
|
24
|
+
"@types/node": "^20.0.0",
|
|
25
|
+
"zod": "^3.23.0",
|
|
26
|
+
"eslint": "^9.0.0",
|
|
27
|
+
"typescript-eslint": "^8.0.0",
|
|
28
|
+
"@eslint/js": "^9.0.0",
|
|
29
|
+
"globals": "^15.0.0"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18.0.0"
|
|
33
|
+
},
|
|
34
|
+
"vitest": {
|
|
35
|
+
"environment": "node",
|
|
36
|
+
"globals": false
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Certynix MCP Server — Entrypoint
|
|
5
|
+
*
|
|
6
|
+
* Expõe a Certynix Trust Infrastructure para agentes de IA via Model Context Protocol.
|
|
7
|
+
* Suporta transporte stdio (Claude Desktop) e SSE (integrações remotas).
|
|
8
|
+
*
|
|
9
|
+
* Variáveis de ambiente:
|
|
10
|
+
* CERTYNIX_API_KEY — obrigatório (cnx_live_sk_* ou cnx_test_sk_*)
|
|
11
|
+
* CERTYNIX_BASE_URL — opcional (auto-detectado via prefixo da key)
|
|
12
|
+
* CERTYNIX_TIMEOUT — opcional (ms, padrão: 30000)
|
|
13
|
+
* MCP_TRANSPORT — opcional (stdio | sse, padrão: stdio)
|
|
14
|
+
* MCP_PORT — opcional (apenas para sse, padrão: 3100)
|
|
15
|
+
* MCP_LOG_LEVEL — opcional (debug | info | warn | error, padrão: info)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
19
|
+
import { createServer } from './server.js';
|
|
20
|
+
import { maskApiKey } from './utils/mask.js';
|
|
21
|
+
|
|
22
|
+
async function main(): Promise<void> {
|
|
23
|
+
const apiKey = process.env['CERTYNIX_API_KEY'];
|
|
24
|
+
|
|
25
|
+
if (!apiKey) {
|
|
26
|
+
console.error(
|
|
27
|
+
'Error: CERTYNIX_API_KEY environment variable is required.\n' +
|
|
28
|
+
'Set it to your Certynix API Key (cnx_live_sk_... for production, cnx_test_sk_... for sandbox).',
|
|
29
|
+
);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Validar formato básico sem expor a key
|
|
34
|
+
if (!apiKey.startsWith('cnx_live_sk_') && !apiKey.startsWith('cnx_test_sk_')) {
|
|
35
|
+
console.error(
|
|
36
|
+
'Error: Invalid CERTYNIX_API_KEY format.\n' +
|
|
37
|
+
'Expected: cnx_live_sk_... (production) or cnx_test_sk_... (sandbox).',
|
|
38
|
+
);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const transport = process.env['MCP_TRANSPORT'] ?? 'stdio';
|
|
43
|
+
const baseUrl = process.env['CERTYNIX_BASE_URL'];
|
|
44
|
+
const timeoutRaw = process.env['CERTYNIX_TIMEOUT'];
|
|
45
|
+
const timeout =
|
|
46
|
+
timeoutRaw !== undefined ? parseInt(timeoutRaw, 10) : undefined;
|
|
47
|
+
const isSandbox = apiKey.startsWith('cnx_test_sk_');
|
|
48
|
+
|
|
49
|
+
let server;
|
|
50
|
+
try {
|
|
51
|
+
server = createServer(apiKey, {
|
|
52
|
+
...(baseUrl !== undefined ? { baseUrl } : {}),
|
|
53
|
+
...(timeout !== undefined ? { timeout } : {}),
|
|
54
|
+
});
|
|
55
|
+
} catch (err) {
|
|
56
|
+
// Nunca logar a API key, mesmo em erro de inicialização
|
|
57
|
+
console.error(
|
|
58
|
+
'Failed to initialize Certynix MCP Server. Check your API key format and network connectivity.',
|
|
59
|
+
);
|
|
60
|
+
if (err instanceof Error) {
|
|
61
|
+
// Sanitizar mensagem de erro para garantir que a key não vaze
|
|
62
|
+
const sanitized = err.message.replace(apiKey, maskApiKey(apiKey));
|
|
63
|
+
console.error(`Details: ${sanitized}`);
|
|
64
|
+
}
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (transport === 'stdio') {
|
|
69
|
+
const stdioTransport = new StdioServerTransport();
|
|
70
|
+
await server.connect(stdioTransport);
|
|
71
|
+
|
|
72
|
+
// Log para stderr (não interfere com stdio MCP) — API key mascarada
|
|
73
|
+
console.error(
|
|
74
|
+
`Certynix MCP Server running (stdio) — ` +
|
|
75
|
+
`API key: ${maskApiKey(apiKey)} — ` +
|
|
76
|
+
`Environment: ${isSandbox ? 'sandbox' : 'production'}`,
|
|
77
|
+
);
|
|
78
|
+
} else if (transport === 'sse') {
|
|
79
|
+
const port = parseInt(process.env['MCP_PORT'] ?? '3100', 10);
|
|
80
|
+
|
|
81
|
+
// SSE transport via HTTP server
|
|
82
|
+
// Importação dinâmica para evitar dependência quando não for necessário
|
|
83
|
+
const { createServer: createHttpServer } = await import('node:http');
|
|
84
|
+
const { SSEServerTransport } = await import(
|
|
85
|
+
'@modelcontextprotocol/sdk/server/sse.js'
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const httpServer = createHttpServer();
|
|
89
|
+
let sseTransport: InstanceType<typeof SSEServerTransport> | null = null;
|
|
90
|
+
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
92
|
+
httpServer.on('request', async (req, res) => {
|
|
93
|
+
if (req.method === 'GET' && req.url === '/sse') {
|
|
94
|
+
sseTransport = new SSEServerTransport('/message', res);
|
|
95
|
+
await server.connect(sseTransport);
|
|
96
|
+
console.error(
|
|
97
|
+
`Certynix MCP SSE client connected from ${req.socket.remoteAddress ?? 'unknown'}`,
|
|
98
|
+
);
|
|
99
|
+
} else if (req.method === 'POST' && req.url === '/message') {
|
|
100
|
+
if (!sseTransport) {
|
|
101
|
+
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
102
|
+
res.end(JSON.stringify({ error: 'SSE connection not established yet' }));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
await sseTransport.handlePostMessage(req, res);
|
|
106
|
+
} else if (req.method === 'GET' && req.url === '/health') {
|
|
107
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
108
|
+
res.end(
|
|
109
|
+
JSON.stringify({
|
|
110
|
+
status: 'ok',
|
|
111
|
+
server: 'certynix-mcp',
|
|
112
|
+
version: '1.0.0',
|
|
113
|
+
environment: isSandbox ? 'sandbox' : 'production',
|
|
114
|
+
}),
|
|
115
|
+
);
|
|
116
|
+
} else {
|
|
117
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
118
|
+
res.end(JSON.stringify({ error: 'Not found' }));
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
await new Promise<void>((resolve, reject) => {
|
|
123
|
+
httpServer.listen(port, () => {
|
|
124
|
+
console.error(
|
|
125
|
+
`Certynix MCP Server running on port ${port} (SSE) — ` +
|
|
126
|
+
`API key: ${maskApiKey(apiKey)} — ` +
|
|
127
|
+
`Environment: ${isSandbox ? 'sandbox' : 'production'}\n` +
|
|
128
|
+
` SSE endpoint: http://localhost:${port}/sse\n` +
|
|
129
|
+
` Health check: http://localhost:${port}/health`,
|
|
130
|
+
);
|
|
131
|
+
resolve();
|
|
132
|
+
});
|
|
133
|
+
httpServer.on('error', reject);
|
|
134
|
+
});
|
|
135
|
+
} else {
|
|
136
|
+
console.error(
|
|
137
|
+
`Error: Invalid MCP_TRANSPORT value: "${transport}". Expected "stdio" or "sse".`,
|
|
138
|
+
);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
main().catch((err) => {
|
|
144
|
+
console.error(
|
|
145
|
+
'Fatal error:',
|
|
146
|
+
err instanceof Error ? err.message : String(err),
|
|
147
|
+
);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Prompt: certynix_audit_report
|
|
6
|
+
* Gera um relatório de auditoria estruturado a partir dos audit logs da organização.
|
|
7
|
+
*/
|
|
8
|
+
export function registerAuditReportPrompt(server: McpServer): void {
|
|
9
|
+
server.prompt(
|
|
10
|
+
'certynix_audit_report',
|
|
11
|
+
'Gera um relatório de auditoria estruturado para um período específico, analisando ações críticas, padrões de acesso e eventos de segurança.',
|
|
12
|
+
{
|
|
13
|
+
period: z
|
|
14
|
+
.enum(['last_7_days', 'last_30_days', 'custom'])
|
|
15
|
+
.describe(
|
|
16
|
+
"Período do relatório: 'last_7_days' (últimos 7 dias), 'last_30_days' (últimos 30 dias), 'custom' (usar start_date e end_date).",
|
|
17
|
+
),
|
|
18
|
+
start_date: z
|
|
19
|
+
.string()
|
|
20
|
+
.optional()
|
|
21
|
+
.describe("Data de início no formato ISO 8601 (ex: 2026-01-01T00:00:00Z). Necessário apenas quando period='custom'."),
|
|
22
|
+
end_date: z
|
|
23
|
+
.string()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("Data de fim no formato ISO 8601 (ex: 2026-01-31T23:59:59Z). Necessário apenas quando period='custom'."),
|
|
26
|
+
},
|
|
27
|
+
(args) => {
|
|
28
|
+
const period = args['period'] ?? 'last_30_days';
|
|
29
|
+
const startDate = args['start_date'];
|
|
30
|
+
const endDate = args['end_date'];
|
|
31
|
+
|
|
32
|
+
let periodDescription: string;
|
|
33
|
+
let dateInstructions: string;
|
|
34
|
+
|
|
35
|
+
if (period === 'last_7_days') {
|
|
36
|
+
const now = new Date();
|
|
37
|
+
const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
|
38
|
+
periodDescription = 'últimos 7 dias';
|
|
39
|
+
dateInstructions = `Use created_after="${weekAgo.toISOString()}" e created_before="${now.toISOString()}" ao chamar list_audit_logs.`;
|
|
40
|
+
} else if (period === 'last_30_days') {
|
|
41
|
+
const now = new Date();
|
|
42
|
+
const monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
|
|
43
|
+
periodDescription = 'últimos 30 dias';
|
|
44
|
+
dateInstructions = `Use created_after="${monthAgo.toISOString()}" e created_before="${now.toISOString()}" ao chamar list_audit_logs.`;
|
|
45
|
+
} else if (period === 'custom' && startDate && endDate) {
|
|
46
|
+
periodDescription = `${startDate} até ${endDate}`;
|
|
47
|
+
dateInstructions = `Use created_after="${startDate}" e created_before="${endDate}" ao chamar list_audit_logs.`;
|
|
48
|
+
} else {
|
|
49
|
+
periodDescription = 'período especificado';
|
|
50
|
+
dateInstructions =
|
|
51
|
+
"Peça ao usuário para especificar start_date e end_date quando period='custom'.";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
messages: [
|
|
56
|
+
{
|
|
57
|
+
role: 'user' as const,
|
|
58
|
+
content: {
|
|
59
|
+
type: 'text' as const,
|
|
60
|
+
text: `Gere um Relatório de Auditoria Certynix completo para o período: ${periodDescription}.
|
|
61
|
+
|
|
62
|
+
## Instruções de execução
|
|
63
|
+
|
|
64
|
+
1. **Chame a tool list_audit_logs** com os seguintes parâmetros:
|
|
65
|
+
- ${dateInstructions}
|
|
66
|
+
- limit: 100
|
|
67
|
+
- Se houver has_more=true na resposta, continue paginando com o next_cursor até obter todos os registros do período.
|
|
68
|
+
|
|
69
|
+
2. **Chame a tool get_trust_score** para incluir o Trust Score atual no relatório.
|
|
70
|
+
|
|
71
|
+
3. **Chame a tool list_alerts** com resolved=false para incluir alertas ativos.
|
|
72
|
+
|
|
73
|
+
## Estrutura do relatório a gerar
|
|
74
|
+
|
|
75
|
+
Após coletar os dados, gere um relatório em Markdown com esta estrutura:
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
# Relatório de Auditoria Certynix
|
|
80
|
+
**Período:** ${periodDescription}
|
|
81
|
+
**Gerado em:** [data atual]
|
|
82
|
+
|
|
83
|
+
## Resumo Executivo
|
|
84
|
+
- Total de ações no período: [N]
|
|
85
|
+
- Trust Score atual: [score]/100
|
|
86
|
+
- Alertas ativos: [N]
|
|
87
|
+
- Eventos críticos: [N]
|
|
88
|
+
|
|
89
|
+
## Trust Score
|
|
90
|
+
- Score: [N]/100 ([label])
|
|
91
|
+
- Identidade: [N]/35
|
|
92
|
+
- Segurança: [N]/25
|
|
93
|
+
- Comportamento: [N]/20
|
|
94
|
+
- Assets: [N]/20
|
|
95
|
+
- Penalidades ativas: [lista ou "Nenhuma"]
|
|
96
|
+
|
|
97
|
+
## Atividade por Tipo de Ação
|
|
98
|
+
| Ação | Quantidade |
|
|
99
|
+
|------|-----------|
|
|
100
|
+
| asset.created | N |
|
|
101
|
+
| api_key.created | N |
|
|
102
|
+
| ... | ... |
|
|
103
|
+
|
|
104
|
+
## Eventos Críticos de Segurança
|
|
105
|
+
[Lista de eventos relacionados a api_key.created, api_key.revoked, member.invited, plan.changed, etc.]
|
|
106
|
+
|
|
107
|
+
## Assets Registrados no Período
|
|
108
|
+
[Sumário de assets registrados: total, status breakdown, first_registrant count]
|
|
109
|
+
|
|
110
|
+
## Alertas de Exposição Ativos
|
|
111
|
+
[Lista de alertas não resolvidos com severidade]
|
|
112
|
+
|
|
113
|
+
## Recomendações
|
|
114
|
+
[Com base nos dados coletados, liste 3-5 recomendações priorizadas]
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
**IMPORTANTE:** Nunca inclua valores de API Keys, tokens, ou outros dados sensíveis no relatório.
|
|
119
|
+
`,
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
);
|
|
126
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Prompt: certynix_security_review
|
|
5
|
+
* Analisa o estado de segurança da Organization e gera recomendações.
|
|
6
|
+
*/
|
|
7
|
+
export function registerSecurityReviewPrompt(server: McpServer): void {
|
|
8
|
+
server.prompt(
|
|
9
|
+
'certynix_security_review',
|
|
10
|
+
'Gera uma análise de segurança completa da Organization: Trust Score, penalidades ativas, status de API Keys, alertas abertos e recomendações priorizadas.',
|
|
11
|
+
{},
|
|
12
|
+
(_args) => {
|
|
13
|
+
return {
|
|
14
|
+
messages: [
|
|
15
|
+
{
|
|
16
|
+
role: 'user' as const,
|
|
17
|
+
content: {
|
|
18
|
+
type: 'text' as const,
|
|
19
|
+
text: `Realize uma Revisão de Segurança completa da organização Certynix.
|
|
20
|
+
|
|
21
|
+
## Instruções de execução
|
|
22
|
+
|
|
23
|
+
Execute as seguintes tools em ordem para coletar dados:
|
|
24
|
+
|
|
25
|
+
1. **get_trust_score** — Trust Score atual com componentes e penalidades
|
|
26
|
+
2. **list_alerts** com resolved=false, limit=50 — Alertas de exposição ativos
|
|
27
|
+
3. **list_api_keys** com limit=100 — Inventário de API Keys ativas
|
|
28
|
+
4. **list_audit_logs** com limit=50, action="api_key.created" — Criações recentes de API Keys
|
|
29
|
+
5. **list_audit_logs** com limit=20, action="member.invited" — Membros adicionados recentemente
|
|
30
|
+
6. **list_assets** com limit=20, status="failed" — Assets com falha de verificação
|
|
31
|
+
|
|
32
|
+
## Estrutura da análise a gerar
|
|
33
|
+
|
|
34
|
+
Após coletar todos os dados, gere uma Revisão de Segurança em Markdown:
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
# Revisão de Segurança Certynix
|
|
39
|
+
**Data:** [data atual]
|
|
40
|
+
**Status Geral:** [Critico / Atenção / Bom / Excelente com base no score]
|
|
41
|
+
|
|
42
|
+
## Trust Score: [N]/100
|
|
43
|
+
|
|
44
|
+
### Componentes
|
|
45
|
+
| Pilar | Pontuação | Máximo | Status |
|
|
46
|
+
|-------|-----------|--------|--------|
|
|
47
|
+
| Identidade | N | 35 | [OK/Atenção/Crítico] |
|
|
48
|
+
| Segurança | N | 25 | [OK/Atenção/Crítico] |
|
|
49
|
+
| Comportamento | N | 20 | [OK/Atenção/Crítico] |
|
|
50
|
+
| Assets | N | 20 | [OK/Atenção/Crítico] |
|
|
51
|
+
|
|
52
|
+
### Penalidades Ativas
|
|
53
|
+
[Lista de penalidades ou "Nenhuma penalidade ativa"]
|
|
54
|
+
|
|
55
|
+
## Alertas de Exposição ([N] ativos)
|
|
56
|
+
|
|
57
|
+
### Alta Severidade ([N])
|
|
58
|
+
[Lista de alertas high com descrição]
|
|
59
|
+
|
|
60
|
+
### Média Severidade ([N])
|
|
61
|
+
[Lista de alertas medium]
|
|
62
|
+
|
|
63
|
+
### Baixa Severidade ([N])
|
|
64
|
+
[Lista de alertas low]
|
|
65
|
+
|
|
66
|
+
## API Keys ([N] ativas)
|
|
67
|
+
[Listar todas as API Keys com nome e data de criação]
|
|
68
|
+
[Identificar keys não utilizadas recentemente]
|
|
69
|
+
[Identificar keys criadas há mais de 90 dias sem rotação]
|
|
70
|
+
|
|
71
|
+
## Observações de Acesso
|
|
72
|
+
[Membros adicionados recentemente]
|
|
73
|
+
[API Keys criadas recentemente]
|
|
74
|
+
|
|
75
|
+
## Assets com Falha ([N])
|
|
76
|
+
[Lista de assets com status=failed]
|
|
77
|
+
|
|
78
|
+
## Recomendações Priorizadas
|
|
79
|
+
|
|
80
|
+
### Críticas (ação imediata)
|
|
81
|
+
1. [recomendação baseada nos dados]
|
|
82
|
+
|
|
83
|
+
### Altas (próxima semana)
|
|
84
|
+
1. [recomendação baseada nos dados]
|
|
85
|
+
|
|
86
|
+
### Médias (próximo mês)
|
|
87
|
+
1. [recomendação baseada nos dados]
|
|
88
|
+
|
|
89
|
+
## Score de Maturidade de Segurança
|
|
90
|
+
[Avaliação qualitativa de 1-5 com justificativa baseada nos dados coletados]
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
**IMPORTANTE:** Nunca inclua valores de API Keys, tokens, signing secrets ou outros dados sensíveis na análise.
|
|
95
|
+
`,
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
};
|
|
100
|
+
},
|
|
101
|
+
);
|
|
102
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { CertynixClient } from '@certynix/sdk';
|
|
3
|
+
import { formatError } from '../utils/format.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resource: certynix://alerts/active
|
|
7
|
+
* Lista todos os Exposure Alerts ativos não resolvidos.
|
|
8
|
+
*/
|
|
9
|
+
export function registerActiveAlertsResource(server: McpServer, client: CertynixClient): void {
|
|
10
|
+
server.resource(
|
|
11
|
+
'alerts-active',
|
|
12
|
+
'certynix://alerts/active',
|
|
13
|
+
{
|
|
14
|
+
description:
|
|
15
|
+
'Lista de Exposure Alerts ativos não resolvidos: anomalias de integridade detectadas automaticamente (surtos de verificação, versões conflitantes, assets não verificados).',
|
|
16
|
+
mimeType: 'application/json',
|
|
17
|
+
},
|
|
18
|
+
async (_uri) => {
|
|
19
|
+
try {
|
|
20
|
+
const page = await client.alerts.listPage({ resolved: false, limit: 50 });
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
contents: [
|
|
24
|
+
{
|
|
25
|
+
uri: 'certynix://alerts/active',
|
|
26
|
+
mimeType: 'application/json',
|
|
27
|
+
text: JSON.stringify(
|
|
28
|
+
{
|
|
29
|
+
data: page.data,
|
|
30
|
+
total_active: page.data.length,
|
|
31
|
+
has_more: page.pagination.has_more,
|
|
32
|
+
next_cursor: page.pagination.next_cursor,
|
|
33
|
+
note: page.pagination.has_more
|
|
34
|
+
? 'Use a tool list_alerts com cursor para ver mais alertas.'
|
|
35
|
+
: undefined,
|
|
36
|
+
},
|
|
37
|
+
null,
|
|
38
|
+
2,
|
|
39
|
+
),
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
};
|
|
43
|
+
} catch (err) {
|
|
44
|
+
return {
|
|
45
|
+
contents: [
|
|
46
|
+
{
|
|
47
|
+
uri: 'certynix://alerts/active',
|
|
48
|
+
mimeType: 'application/json',
|
|
49
|
+
text: JSON.stringify({ error: formatError(err) }, null, 2),
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { CertynixClient } from '@certynix/sdk';
|
|
3
|
+
import { formatError } from '../utils/format.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resource template: certynix://assets/{asset_id}
|
|
7
|
+
* Retorna metadados completos de um asset específico.
|
|
8
|
+
*/
|
|
9
|
+
export function registerAssetResource(server: McpServer, client: CertynixClient): void {
|
|
10
|
+
server.resource(
|
|
11
|
+
'asset',
|
|
12
|
+
new ResourceTemplate('certynix://assets/{asset_id}', { list: undefined }),
|
|
13
|
+
{
|
|
14
|
+
description:
|
|
15
|
+
'Metadados completos de um asset específico: hash, status, Trust Score individual, contagem de verificações públicas e histórico de eventos.',
|
|
16
|
+
mimeType: 'application/json',
|
|
17
|
+
},
|
|
18
|
+
async (uri, variables) => {
|
|
19
|
+
const assetId = variables['asset_id'];
|
|
20
|
+
|
|
21
|
+
if (typeof assetId !== 'string' || !assetId) {
|
|
22
|
+
return {
|
|
23
|
+
contents: [
|
|
24
|
+
{
|
|
25
|
+
uri: uri.href,
|
|
26
|
+
mimeType: 'application/json',
|
|
27
|
+
text: JSON.stringify(
|
|
28
|
+
{ error: 'asset_id is required in the URI: certynix://assets/{asset_id}' },
|
|
29
|
+
null,
|
|
30
|
+
2,
|
|
31
|
+
),
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const asset = await client.assets.get(assetId);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
contents: [
|
|
42
|
+
{
|
|
43
|
+
uri: uri.href,
|
|
44
|
+
mimeType: 'application/json',
|
|
45
|
+
text: JSON.stringify(
|
|
46
|
+
{
|
|
47
|
+
id: asset.id,
|
|
48
|
+
hash: asset.hash,
|
|
49
|
+
status: asset.status,
|
|
50
|
+
trust_score: asset.trustScore,
|
|
51
|
+
source_type: asset.sourceType,
|
|
52
|
+
source_reference: asset.sourceReference,
|
|
53
|
+
public_verification_count: asset.publicVerificationCount,
|
|
54
|
+
is_first_registrant: asset.isFirstRegistrant,
|
|
55
|
+
is_sample: asset.isSample,
|
|
56
|
+
created_at: asset.createdAt,
|
|
57
|
+
events: asset.events,
|
|
58
|
+
verification_url: `https://certynix.com/verify/${asset.id}`,
|
|
59
|
+
},
|
|
60
|
+
null,
|
|
61
|
+
2,
|
|
62
|
+
),
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
};
|
|
66
|
+
} catch (err) {
|
|
67
|
+
return {
|
|
68
|
+
contents: [
|
|
69
|
+
{
|
|
70
|
+
uri: uri.href,
|
|
71
|
+
mimeType: 'application/json',
|
|
72
|
+
text: JSON.stringify({ error: formatError(err) }, null, 2),
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { CertynixClient } from '@certynix/sdk';
|
|
3
|
+
import { formatError } from '../utils/format.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resource: certynix://organization/info
|
|
7
|
+
* Retorna informações da Organization autenticada via API Key.
|
|
8
|
+
* Usa o Trust Score endpoint para obter dados da organização.
|
|
9
|
+
*/
|
|
10
|
+
export function registerOrganizationInfoResource(
|
|
11
|
+
server: McpServer,
|
|
12
|
+
client: CertynixClient,
|
|
13
|
+
): void {
|
|
14
|
+
server.resource(
|
|
15
|
+
'organization-info',
|
|
16
|
+
'certynix://organization/info',
|
|
17
|
+
{
|
|
18
|
+
description:
|
|
19
|
+
'Informações da Organization autenticada: Trust Score atual, componentes e penalidades ativas.',
|
|
20
|
+
mimeType: 'application/json',
|
|
21
|
+
},
|
|
22
|
+
async (_uri) => {
|
|
23
|
+
try {
|
|
24
|
+
const trustScore = await client.trustScore.get();
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
contents: [
|
|
28
|
+
{
|
|
29
|
+
uri: 'certynix://organization/info',
|
|
30
|
+
mimeType: 'application/json',
|
|
31
|
+
text: JSON.stringify(
|
|
32
|
+
{
|
|
33
|
+
trust_score: {
|
|
34
|
+
score: trustScore.score,
|
|
35
|
+
algorithm_version: trustScore.algorithmVersion,
|
|
36
|
+
components: trustScore.components,
|
|
37
|
+
penalties: trustScore.penalties,
|
|
38
|
+
updated_at: trustScore.updatedAt,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
null,
|
|
42
|
+
2,
|
|
43
|
+
),
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
} catch (err) {
|
|
48
|
+
return {
|
|
49
|
+
contents: [
|
|
50
|
+
{
|
|
51
|
+
uri: 'certynix://organization/info',
|
|
52
|
+
mimeType: 'application/json',
|
|
53
|
+
text: JSON.stringify({ error: formatError(err) }, null, 2),
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
);
|
|
60
|
+
}
|