@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,61 @@
|
|
|
1
|
+
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { formatError } from '../utils/format.js';
|
|
3
|
+
/**
|
|
4
|
+
* Resource template: certynix://assets/{asset_id}
|
|
5
|
+
* Retorna metadados completos de um asset específico.
|
|
6
|
+
*/
|
|
7
|
+
export function registerAssetResource(server, client) {
|
|
8
|
+
server.resource('asset', new ResourceTemplate('certynix://assets/{asset_id}', { list: undefined }), {
|
|
9
|
+
description: 'Metadados completos de um asset específico: hash, status, Trust Score individual, contagem de verificações públicas e histórico de eventos.',
|
|
10
|
+
mimeType: 'application/json',
|
|
11
|
+
}, async (uri, variables) => {
|
|
12
|
+
const assetId = variables['asset_id'];
|
|
13
|
+
if (typeof assetId !== 'string' || !assetId) {
|
|
14
|
+
return {
|
|
15
|
+
contents: [
|
|
16
|
+
{
|
|
17
|
+
uri: uri.href,
|
|
18
|
+
mimeType: 'application/json',
|
|
19
|
+
text: JSON.stringify({ error: 'asset_id is required in the URI: certynix://assets/{asset_id}' }, null, 2),
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const asset = await client.assets.get(assetId);
|
|
26
|
+
return {
|
|
27
|
+
contents: [
|
|
28
|
+
{
|
|
29
|
+
uri: uri.href,
|
|
30
|
+
mimeType: 'application/json',
|
|
31
|
+
text: JSON.stringify({
|
|
32
|
+
id: asset.id,
|
|
33
|
+
hash: asset.hash,
|
|
34
|
+
status: asset.status,
|
|
35
|
+
trust_score: asset.trustScore,
|
|
36
|
+
source_type: asset.sourceType,
|
|
37
|
+
source_reference: asset.sourceReference,
|
|
38
|
+
public_verification_count: asset.publicVerificationCount,
|
|
39
|
+
is_first_registrant: asset.isFirstRegistrant,
|
|
40
|
+
is_sample: asset.isSample,
|
|
41
|
+
created_at: asset.createdAt,
|
|
42
|
+
events: asset.events,
|
|
43
|
+
verification_url: `https://certynix.com/verify/${asset.id}`,
|
|
44
|
+
}, null, 2),
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
return {
|
|
51
|
+
contents: [
|
|
52
|
+
{
|
|
53
|
+
uri: uri.href,
|
|
54
|
+
mimeType: 'application/json',
|
|
55
|
+
text: JSON.stringify({ error: formatError(err) }, null, 2),
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { CertynixClient } from '@certynix/sdk';
|
|
3
|
+
/**
|
|
4
|
+
* Resource: certynix://organization/info
|
|
5
|
+
* Retorna informações da Organization autenticada via API Key.
|
|
6
|
+
* Usa o Trust Score endpoint para obter dados da organização.
|
|
7
|
+
*/
|
|
8
|
+
export declare function registerOrganizationInfoResource(server: McpServer, client: CertynixClient): void;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { formatError } from '../utils/format.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resource: certynix://organization/info
|
|
4
|
+
* Retorna informações da Organization autenticada via API Key.
|
|
5
|
+
* Usa o Trust Score endpoint para obter dados da organização.
|
|
6
|
+
*/
|
|
7
|
+
export function registerOrganizationInfoResource(server, client) {
|
|
8
|
+
server.resource('organization-info', 'certynix://organization/info', {
|
|
9
|
+
description: 'Informações da Organization autenticada: Trust Score atual, componentes e penalidades ativas.',
|
|
10
|
+
mimeType: 'application/json',
|
|
11
|
+
}, async (_uri) => {
|
|
12
|
+
try {
|
|
13
|
+
const trustScore = await client.trustScore.get();
|
|
14
|
+
return {
|
|
15
|
+
contents: [
|
|
16
|
+
{
|
|
17
|
+
uri: 'certynix://organization/info',
|
|
18
|
+
mimeType: 'application/json',
|
|
19
|
+
text: JSON.stringify({
|
|
20
|
+
trust_score: {
|
|
21
|
+
score: trustScore.score,
|
|
22
|
+
algorithm_version: trustScore.algorithmVersion,
|
|
23
|
+
components: trustScore.components,
|
|
24
|
+
penalties: trustScore.penalties,
|
|
25
|
+
updated_at: trustScore.updatedAt,
|
|
26
|
+
},
|
|
27
|
+
}, null, 2),
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
return {
|
|
34
|
+
contents: [
|
|
35
|
+
{
|
|
36
|
+
uri: 'certynix://organization/info',
|
|
37
|
+
mimeType: 'application/json',
|
|
38
|
+
text: JSON.stringify({ error: formatError(err) }, null, 2),
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { CertynixClient } from '@certynix/sdk';
|
|
3
|
+
/**
|
|
4
|
+
* Resource: certynix://organization/trust-score
|
|
5
|
+
* Retorna o Trust Score V2 detalhado com componentes e penalidades ativas.
|
|
6
|
+
*/
|
|
7
|
+
export declare function registerTrustScoreResource(server: McpServer, client: CertynixClient): void;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { formatError } from '../utils/format.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resource: certynix://organization/trust-score
|
|
4
|
+
* Retorna o Trust Score V2 detalhado com componentes e penalidades ativas.
|
|
5
|
+
*/
|
|
6
|
+
export function registerTrustScoreResource(server, client) {
|
|
7
|
+
server.resource('trust-score', 'certynix://organization/trust-score', {
|
|
8
|
+
description: 'Trust Score V2 detalhado da organização: pontuação 0-100, componentes por pilar (Identidade, Segurança, Comportamento, Assets) e penalidades ativas.',
|
|
9
|
+
mimeType: 'application/json',
|
|
10
|
+
}, async (_uri) => {
|
|
11
|
+
try {
|
|
12
|
+
const trustScore = await client.trustScore.get();
|
|
13
|
+
return {
|
|
14
|
+
contents: [
|
|
15
|
+
{
|
|
16
|
+
uri: 'certynix://organization/trust-score',
|
|
17
|
+
mimeType: 'application/json',
|
|
18
|
+
text: JSON.stringify({
|
|
19
|
+
score: trustScore.score,
|
|
20
|
+
components: {
|
|
21
|
+
identity: trustScore.components.identity,
|
|
22
|
+
security: trustScore.components.security,
|
|
23
|
+
behavior: trustScore.components.behavior,
|
|
24
|
+
assets: trustScore.components.assets,
|
|
25
|
+
},
|
|
26
|
+
penalties: trustScore.penalties,
|
|
27
|
+
algorithm_version: trustScore.algorithmVersion,
|
|
28
|
+
updated_at: trustScore.updatedAt,
|
|
29
|
+
}, null, 2),
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
return {
|
|
36
|
+
contents: [
|
|
37
|
+
{
|
|
38
|
+
uri: 'certynix://organization/trust-score',
|
|
39
|
+
mimeType: 'application/json',
|
|
40
|
+
text: JSON.stringify({ error: formatError(err) }, null, 2),
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
export interface ServerOptions {
|
|
3
|
+
baseUrl?: string;
|
|
4
|
+
timeout?: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Cria e configura o MCP Server Certynix.
|
|
8
|
+
*
|
|
9
|
+
* @param apiKey - API Key Certynix (cnx_live_sk_* ou cnx_test_sk_*)
|
|
10
|
+
* @param options - Opções de configuração do cliente
|
|
11
|
+
* @returns McpServer configurado e pronto para conectar
|
|
12
|
+
*/
|
|
13
|
+
export declare function createServer(apiKey: string, options?: ServerOptions): McpServer;
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { CertynixClient } from '@certynix/sdk';
|
|
3
|
+
// Tools
|
|
4
|
+
import { registerRegisterAssetTool } from './tools/register-asset.js';
|
|
5
|
+
import { registerVerifyAssetTool } from './tools/verify-asset.js';
|
|
6
|
+
import { registerListAssetsTool } from './tools/list-assets.js';
|
|
7
|
+
import { registerGetAssetTool } from './tools/get-asset.js';
|
|
8
|
+
import { registerDeleteAssetTool } from './tools/delete-asset.js';
|
|
9
|
+
import { registerListAlertsTool } from './tools/list-alerts.js';
|
|
10
|
+
import { registerGetTrustScoreTool } from './tools/get-trust-score.js';
|
|
11
|
+
import { registerListAuditLogsTool } from './tools/list-audit-logs.js';
|
|
12
|
+
import { registerCreateApiKeyTool } from './tools/create-api-key.js';
|
|
13
|
+
import { registerListApiKeysTool } from './tools/list-api-keys.js';
|
|
14
|
+
import { registerRevokeApiKeyTool } from './tools/revoke-api-key.js';
|
|
15
|
+
import { registerCreateWebhookTool } from './tools/create-webhook.js';
|
|
16
|
+
import { registerListWebhooksTool } from './tools/list-webhooks.js';
|
|
17
|
+
import { registerVerifyWebhookSignatureTool } from './tools/verify-webhook-signature.js';
|
|
18
|
+
// Resources
|
|
19
|
+
import { registerOrganizationInfoResource } from './resources/organization-info.js';
|
|
20
|
+
import { registerTrustScoreResource } from './resources/trust-score.js';
|
|
21
|
+
import { registerAssetResource } from './resources/asset.js';
|
|
22
|
+
import { registerActiveAlertsResource } from './resources/alerts-active.js';
|
|
23
|
+
// Prompts
|
|
24
|
+
import { registerAuditReportPrompt } from './prompts/audit-report.js';
|
|
25
|
+
import { registerSecurityReviewPrompt } from './prompts/security-review.js';
|
|
26
|
+
/**
|
|
27
|
+
* Cria e configura o MCP Server Certynix.
|
|
28
|
+
*
|
|
29
|
+
* @param apiKey - API Key Certynix (cnx_live_sk_* ou cnx_test_sk_*)
|
|
30
|
+
* @param options - Opções de configuração do cliente
|
|
31
|
+
* @returns McpServer configurado e pronto para conectar
|
|
32
|
+
*/
|
|
33
|
+
export function createServer(apiKey, options) {
|
|
34
|
+
const client = new CertynixClient({
|
|
35
|
+
apiKey,
|
|
36
|
+
...(options?.baseUrl !== undefined ? { baseUrl: options.baseUrl } : {}),
|
|
37
|
+
...(options?.timeout !== undefined ? { timeout: options.timeout } : {}),
|
|
38
|
+
});
|
|
39
|
+
const server = new McpServer({
|
|
40
|
+
name: 'certynix',
|
|
41
|
+
version: '1.0.0',
|
|
42
|
+
});
|
|
43
|
+
// Registrar 14 Tools
|
|
44
|
+
registerRegisterAssetTool(server, client);
|
|
45
|
+
registerVerifyAssetTool(server, client);
|
|
46
|
+
registerListAssetsTool(server, client);
|
|
47
|
+
registerGetAssetTool(server, client);
|
|
48
|
+
registerDeleteAssetTool(server, client);
|
|
49
|
+
registerListAlertsTool(server, client);
|
|
50
|
+
registerGetTrustScoreTool(server, client);
|
|
51
|
+
registerListAuditLogsTool(server, client);
|
|
52
|
+
registerCreateApiKeyTool(server, client);
|
|
53
|
+
registerListApiKeysTool(server, client);
|
|
54
|
+
registerRevokeApiKeyTool(server, client);
|
|
55
|
+
registerCreateWebhookTool(server, client);
|
|
56
|
+
registerListWebhooksTool(server, client);
|
|
57
|
+
registerVerifyWebhookSignatureTool(server); // Tool local — não precisa do client
|
|
58
|
+
// Registrar 4 Resources
|
|
59
|
+
registerOrganizationInfoResource(server, client);
|
|
60
|
+
registerTrustScoreResource(server, client);
|
|
61
|
+
registerAssetResource(server, client);
|
|
62
|
+
registerActiveAlertsResource(server, client);
|
|
63
|
+
// Registrar 2 Prompts
|
|
64
|
+
registerAuditReportPrompt(server);
|
|
65
|
+
registerSecurityReviewPrompt(server);
|
|
66
|
+
return server;
|
|
67
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { formatError } from '../utils/format.js';
|
|
3
|
+
export function registerCreateApiKeyTool(server, client) {
|
|
4
|
+
server.tool('create_api_key', 'Cria uma nova API Key para integração server-to-server. A chave completa é exibida apenas uma vez — guarde em local seguro imediatamente. Não é possível recuperá-la depois.', {
|
|
5
|
+
name: z
|
|
6
|
+
.string()
|
|
7
|
+
.min(1)
|
|
8
|
+
.max(100)
|
|
9
|
+
.describe("Nome descritivo da API Key (ex: 'Servidor de Produção', 'CI/CD Pipeline - GitHub Actions')."),
|
|
10
|
+
}, async (params) => {
|
|
11
|
+
try {
|
|
12
|
+
const apiKey = await client.apiKeys.create({ name: params.name });
|
|
13
|
+
// ATENÇÃO: O keyValue DEVE ser exibido integralmente — é o único momento que o usuário verá.
|
|
14
|
+
// Esta é a ÚNICA exceção à regra de mascaramento: a key recém-criada deve ser mostrada ao usuário.
|
|
15
|
+
return {
|
|
16
|
+
content: [
|
|
17
|
+
{
|
|
18
|
+
type: 'text',
|
|
19
|
+
text: JSON.stringify({
|
|
20
|
+
id: apiKey.id,
|
|
21
|
+
name: apiKey.name,
|
|
22
|
+
key_value: apiKey.keyValue,
|
|
23
|
+
created_at: apiKey.createdAt,
|
|
24
|
+
warning: 'IMPORTANTE: Guarde esta API Key em local seguro agora. Por motivos de segurança, ela não poderá ser exibida novamente. Se perdida, será necessário criar uma nova e revogar esta.',
|
|
25
|
+
}, null, 2),
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
return {
|
|
32
|
+
content: [{ type: 'text', text: formatError(err) }],
|
|
33
|
+
isError: true,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { formatError } from '../utils/format.js';
|
|
3
|
+
export function registerCreateWebhookTool(server, client) {
|
|
4
|
+
server.tool('create_webhook', 'Cria um endpoint para receber eventos em tempo real via webhook. Suporta: asset.created, asset.verified, exposure.alert.created. Cada delivery é assinado com HMAC-SHA256. O signing_secret é exibido apenas uma vez — guarde-o imediatamente.', {
|
|
5
|
+
url: z
|
|
6
|
+
.string()
|
|
7
|
+
.url()
|
|
8
|
+
.describe('URL HTTPS que receberá os eventos. Deve retornar HTTP 2xx em até 10 segundos.'),
|
|
9
|
+
events: z
|
|
10
|
+
.array(z.enum(['asset.created', 'asset.verified', 'exposure.alert.created']))
|
|
11
|
+
.optional()
|
|
12
|
+
.describe('Eventos a assinar. Se omitido, assina todos os eventos disponíveis (asset.created, asset.verified, exposure.alert.created).'),
|
|
13
|
+
}, async (params) => {
|
|
14
|
+
try {
|
|
15
|
+
const webhook = await client.webhooks.create({
|
|
16
|
+
url: params.url,
|
|
17
|
+
...(params.events !== undefined ? { events: params.events } : {}),
|
|
18
|
+
});
|
|
19
|
+
// ATENÇÃO: signing_secret DEVE ser exibido — é o único momento que o usuário verá.
|
|
20
|
+
// Esta é a ÚNICA exceção à regra de mascaramento para este campo.
|
|
21
|
+
return {
|
|
22
|
+
content: [
|
|
23
|
+
{
|
|
24
|
+
type: 'text',
|
|
25
|
+
text: JSON.stringify({
|
|
26
|
+
id: webhook.id,
|
|
27
|
+
url: webhook.url,
|
|
28
|
+
events: webhook.events,
|
|
29
|
+
signing_secret: webhook.signingSecret,
|
|
30
|
+
created_at: webhook.createdAt,
|
|
31
|
+
warning: 'IMPORTANTE: Guarde o signing_secret em local seguro agora. Por motivos de segurança, ele não poderá ser exibido novamente. Use-o para validar a assinatura HMAC-SHA256 de cada delivery recebido.',
|
|
32
|
+
usage_example: 'Use a tool verify_webhook_signature para validar deliveries recebidos com este signing_secret.',
|
|
33
|
+
}, null, 2),
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
return {
|
|
40
|
+
content: [{ type: 'text', text: formatError(err) }],
|
|
41
|
+
isError: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { formatError } from '../utils/format.js';
|
|
3
|
+
export function registerDeleteAssetTool(server, client) {
|
|
4
|
+
server.tool('delete_asset', 'Remove logicamente um asset. O asset não aparece mais em listagens, mas o histórico é preservado para auditoria. Esta ação é irreversível — requer confirmação explícita com confirm: true.', {
|
|
5
|
+
asset_id: z
|
|
6
|
+
.string()
|
|
7
|
+
.min(1)
|
|
8
|
+
.describe('ID do asset a ser removido.'),
|
|
9
|
+
confirm: z
|
|
10
|
+
.literal(true)
|
|
11
|
+
.describe('Deve ser true para confirmar a remoção. Esta ação é irreversível e não pode ser desfeita.'),
|
|
12
|
+
}, async (params) => {
|
|
13
|
+
try {
|
|
14
|
+
// Guardar verificação de segurança mesmo com o schema obrigando literal(true)
|
|
15
|
+
if (params.confirm !== true) {
|
|
16
|
+
return {
|
|
17
|
+
content: [
|
|
18
|
+
{
|
|
19
|
+
type: 'text',
|
|
20
|
+
text: 'Error: confirm must be true to delete an asset. This action is irreversible.',
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
isError: true,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
await client.assets.delete(params.asset_id);
|
|
27
|
+
return {
|
|
28
|
+
content: [
|
|
29
|
+
{
|
|
30
|
+
type: 'text',
|
|
31
|
+
text: JSON.stringify({
|
|
32
|
+
success: true,
|
|
33
|
+
asset_id: params.asset_id,
|
|
34
|
+
message: 'Asset removido com sucesso. O histórico de auditoria foi preservado.',
|
|
35
|
+
warning: 'Esta ação é irreversível. O asset não aparecerá mais em listagens.',
|
|
36
|
+
}, null, 2),
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
return {
|
|
43
|
+
content: [{ type: 'text', text: formatError(err) }],
|
|
44
|
+
isError: true,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { formatError } from '../utils/format.js';
|
|
3
|
+
export function registerGetAssetTool(server, client) {
|
|
4
|
+
server.tool('get_asset', 'Busca um asset específico pelo ID retornado no registro. Inclui metadados completos: hash, status, Trust Score individual, contagem de verificações públicas e histórico de eventos.', {
|
|
5
|
+
asset_id: z
|
|
6
|
+
.string()
|
|
7
|
+
.min(1)
|
|
8
|
+
.describe('ID único do asset (retornado pelo register_asset ou list_assets).'),
|
|
9
|
+
}, async (params) => {
|
|
10
|
+
try {
|
|
11
|
+
const asset = await client.assets.get(params.asset_id);
|
|
12
|
+
return {
|
|
13
|
+
content: [
|
|
14
|
+
{
|
|
15
|
+
type: 'text',
|
|
16
|
+
text: JSON.stringify({
|
|
17
|
+
id: asset.id,
|
|
18
|
+
hash: asset.hash,
|
|
19
|
+
status: asset.status,
|
|
20
|
+
trust_score: asset.trustScore,
|
|
21
|
+
source_type: asset.sourceType,
|
|
22
|
+
source_reference: asset.sourceReference,
|
|
23
|
+
public_verification_count: asset.publicVerificationCount,
|
|
24
|
+
is_first_registrant: asset.isFirstRegistrant,
|
|
25
|
+
is_sample: asset.isSample,
|
|
26
|
+
created_at: asset.createdAt,
|
|
27
|
+
events: asset.events,
|
|
28
|
+
verification_url: `https://certynix.com/verify/${asset.id}`,
|
|
29
|
+
}, null, 2),
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
return {
|
|
36
|
+
content: [{ type: 'text', text: formatError(err) }],
|
|
37
|
+
isError: true,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { formatError } from '../utils/format.js';
|
|
2
|
+
export function registerGetTrustScoreTool(server, client) {
|
|
3
|
+
server.tool('get_trust_score', 'Retorna o Trust Score V2 da organização — pontuação de confiança de 0 a 100 calculada em 4 pilares: Identidade (35%), Segurança (25%), Comportamento (20%) e Assets (20%). Inclui penalidades ativas e componentes detalhados.', {}, async (_params) => {
|
|
4
|
+
try {
|
|
5
|
+
const trustScore = await client.trustScore.get();
|
|
6
|
+
const scoreLabel = trustScore.score >= 90
|
|
7
|
+
? 'Excelente'
|
|
8
|
+
: trustScore.score >= 75
|
|
9
|
+
? 'Bom'
|
|
10
|
+
: trustScore.score >= 60
|
|
11
|
+
? 'Regular'
|
|
12
|
+
: trustScore.score >= 40
|
|
13
|
+
? 'Fraco'
|
|
14
|
+
: 'Crítico';
|
|
15
|
+
return {
|
|
16
|
+
content: [
|
|
17
|
+
{
|
|
18
|
+
type: 'text',
|
|
19
|
+
text: JSON.stringify({
|
|
20
|
+
score: trustScore.score,
|
|
21
|
+
score_label: scoreLabel,
|
|
22
|
+
components: {
|
|
23
|
+
identity: trustScore.components.identity,
|
|
24
|
+
security: trustScore.components.security,
|
|
25
|
+
behavior: trustScore.components.behavior,
|
|
26
|
+
assets: trustScore.components.assets,
|
|
27
|
+
},
|
|
28
|
+
penalties: trustScore.penalties,
|
|
29
|
+
algorithm_version: trustScore.algorithmVersion,
|
|
30
|
+
updated_at: trustScore.updatedAt,
|
|
31
|
+
}, null, 2),
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
return {
|
|
38
|
+
content: [{ type: 'text', text: formatError(err) }],
|
|
39
|
+
isError: true,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { formatError } from '../utils/format.js';
|
|
3
|
+
export function registerListAlertsTool(server, client) {
|
|
4
|
+
server.tool('list_alerts', 'Lista os Exposure Alerts ativos — anomalias de integridade detectadas automaticamente. Inclui surtos de verificação, versões conflitantes e assets nunca verificados.', {
|
|
5
|
+
limit: z
|
|
6
|
+
.number()
|
|
7
|
+
.int()
|
|
8
|
+
.min(1)
|
|
9
|
+
.max(100)
|
|
10
|
+
.optional()
|
|
11
|
+
.describe('Número de alertas por página (máximo 100, padrão 20).'),
|
|
12
|
+
cursor: z
|
|
13
|
+
.string()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe('Cursor de paginação retornado pela chamada anterior.'),
|
|
16
|
+
severity: z
|
|
17
|
+
.enum(['low', 'medium', 'high'])
|
|
18
|
+
.optional()
|
|
19
|
+
.describe('Filtrar por severidade do alerta.'),
|
|
20
|
+
resolved: z
|
|
21
|
+
.boolean()
|
|
22
|
+
.optional()
|
|
23
|
+
.describe('true = apenas alertas resolvidos, false = apenas ativos (padrão: false — alertas ativos).'),
|
|
24
|
+
}, async (params) => {
|
|
25
|
+
try {
|
|
26
|
+
const page = await client.alerts.listPage({
|
|
27
|
+
...(params.limit !== undefined ? { limit: params.limit } : {}),
|
|
28
|
+
...(params.cursor !== undefined ? { cursor: params.cursor } : {}),
|
|
29
|
+
...(params.severity !== undefined ? { severity: params.severity } : {}),
|
|
30
|
+
...(params.resolved !== undefined ? { resolved: params.resolved } : {}),
|
|
31
|
+
});
|
|
32
|
+
return {
|
|
33
|
+
content: [
|
|
34
|
+
{
|
|
35
|
+
type: 'text',
|
|
36
|
+
text: JSON.stringify({
|
|
37
|
+
data: page.data,
|
|
38
|
+
pagination: {
|
|
39
|
+
has_more: page.pagination.has_more,
|
|
40
|
+
next_cursor: page.pagination.next_cursor,
|
|
41
|
+
},
|
|
42
|
+
total_returned: page.data.length,
|
|
43
|
+
}, null, 2),
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
return {
|
|
50
|
+
content: [{ type: 'text', text: formatError(err) }],
|
|
51
|
+
isError: true,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { formatError } from '../utils/format.js';
|
|
3
|
+
export function registerListApiKeysTool(server, client) {
|
|
4
|
+
server.tool('list_api_keys', 'Lista as API Keys da organização. A chave completa nunca é retornada — apenas o prefixo e os últimos 4 caracteres são exibidos por segurança.', {
|
|
5
|
+
limit: z
|
|
6
|
+
.number()
|
|
7
|
+
.int()
|
|
8
|
+
.min(1)
|
|
9
|
+
.max(100)
|
|
10
|
+
.optional()
|
|
11
|
+
.describe('Número de API Keys por página (máximo 100, padrão 20).'),
|
|
12
|
+
cursor: z
|
|
13
|
+
.string()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe('Cursor de paginação retornado pela chamada anterior.'),
|
|
16
|
+
}, async (params) => {
|
|
17
|
+
try {
|
|
18
|
+
const page = await client.apiKeys.listPage({
|
|
19
|
+
...(params.limit !== undefined ? { limit: params.limit } : {}),
|
|
20
|
+
...(params.cursor !== undefined ? { cursor: params.cursor } : {}),
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
content: [
|
|
24
|
+
{
|
|
25
|
+
type: 'text',
|
|
26
|
+
text: JSON.stringify({
|
|
27
|
+
data: page.data.map((key) => ({
|
|
28
|
+
id: key.id,
|
|
29
|
+
name: key.name,
|
|
30
|
+
// keyValue nunca retornado em listagens — apenas no momento de criação
|
|
31
|
+
key_prefix: key.prefix,
|
|
32
|
+
created_at: key.createdAt,
|
|
33
|
+
})),
|
|
34
|
+
pagination: {
|
|
35
|
+
has_more: page.pagination.has_more,
|
|
36
|
+
next_cursor: page.pagination.next_cursor,
|
|
37
|
+
},
|
|
38
|
+
total_returned: page.data.length,
|
|
39
|
+
}, null, 2),
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
return {
|
|
46
|
+
content: [{ type: 'text', text: formatError(err) }],
|
|
47
|
+
isError: true,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|