@pentatrail/mcp-server
Advanced tools
+11
-3
@@ -29,5 +29,13 @@ "use strict"; | ||
| }); | ||
| // Register the public CTEM surface: 16 read tools plus `ctem_exclude_bulk`, | ||
| // which writes and needs the `ctem:exclusions:write` scope. The other tool | ||
| // modules under src/tools/ are internal and deliberately not registered here. | ||
| // Register the public CTEM surface. It is READ-ONLY: every tool here needs | ||
| // only `ctem:read` (#1310). The write tool `ctem_exclude_bulk` used to be | ||
| // registered here too; it now lives only in the internal `tools/ctem.ts`, | ||
| // because publishing a bulk-exclude without publishing its inverse would put | ||
| // a door with no way back on the public surface. The other tool modules under | ||
| // src/tools/ are likewise internal and deliberately not registered here. | ||
| // | ||
| // ⚠ The set of tools this call registers is asserted by | ||
| // `src/__tests__/tool-registry.test.ts` and gated in CI by | ||
| // `scripts/audit/check-mcp-public-exposure.ts` (every path served here must be | ||
| // declared `exposure: "public"` in g2-2-route-spec.json). | ||
| (0, public_js_1.registerPublicCtemTools)(server, client); | ||
@@ -34,0 +42,0 @@ // Start stdio transport |
+6
-19
@@ -6,4 +6,6 @@ "use strict"; | ||
| function registerPublicCtemTools(server, client) { | ||
| server.tool('ctem_list_domains', '[READ] List all monitored domains for the authenticated contract', {}, async () => { | ||
| const res = await client.get('/v1/ctem/domains'); | ||
| server.tool('ctem_list_domains', '[READ] List the monitored domains for the authenticated contract. Start here: by default it returns active domains only, and every domain in that default result can be passed straight to the other tools as domain_id. Setting include_archived drops the status filter entirely, so the result then also contains non-active domains (archived, and legacy paused) whose ids the other tools reject with PT_API_NOT_FOUND.', { | ||
| include_archived: zod_1.z.boolean().optional().describe('Also return non-active domains — archived and legacy paused (default: false). Only active ids are accepted as domain_id elsewhere.'), | ||
| }, async (params) => { | ||
| const res = await client.get('/v1/ctem/domains', params); | ||
| return { content: [{ type: 'text', text: JSON.stringify(res, null, 2) }], isError: !res.success }; | ||
@@ -154,3 +156,4 @@ }); | ||
| source_type: zod_1.z.string().optional(), | ||
| status_code_range: zod_1.z.string().optional().describe('e.g. "200-299" or "4xx"'), | ||
| status_code_range: zod_1.z.enum(['2xx3xx', '2xx', '3xx', '4xx', '5xx']).optional() | ||
| .describe('HTTP status filter. Only these five tokens are matched server-side; a range like "200-299" silently returns nothing.'), | ||
| order_by: zod_1.z.enum(['url', 'status_code', 'first_seen_at']).optional(), | ||
@@ -179,18 +182,2 @@ ascending: zod_1.z.boolean().optional(), | ||
| }); | ||
| // ── ctem_exclude_bulk ── | ||
| server.tool('ctem_exclude_bulk', '[WRITE] Exclude multiple assets in one operation. Required scope: ctem:exclusions:write. Audit-logged with array length.', { | ||
| domain_id: zod_1.z.string().uuid().describe('Origin domain ID (required)'), | ||
| category: zod_1.z.enum(['host_fqdn', 'ip', 'port_service', 'tech', 'url', 'bucket']).describe('Asset category (matches inner asm_exclude_assets_bulk enum exactly)'), | ||
| category_keys: zod_1.z.array(zod_1.z.string().min(1)).min(1).max(1000).describe('Array of category keys (1-1000 items, each non-empty). Each key is normalized server-side to lowercase with surrounding whitespace removed, so keys that differ only in case or padding collapse into one row and are counted in skipped_count.'), | ||
| reason: zod_1.z.string().max(500).optional(), | ||
| }, async (params) => { | ||
| const { domain_id, category, category_keys, reason } = params; | ||
| const res = await client.post('/v1/ctem/exclusions/bulk', { | ||
| origin_domain_id: domain_id, | ||
| category, | ||
| category_keys, | ||
| reason: reason ?? null, | ||
| }); | ||
| return { content: [{ type: 'text', text: JSON.stringify(res, null, 2) }], isError: !res.success }; | ||
| }); | ||
| } |
@@ -1,1 +0,1 @@ | ||
| export declare const VERSION = "0.6.1"; | ||
| export declare const VERSION = "0.7.0"; |
+1
-1
@@ -8,2 +8,2 @@ "use strict"; | ||
| // see RELEASING.md. | ||
| exports.VERSION = '0.6.1'; | ||
| exports.VERSION = '0.7.0'; |
+1
-1
| { | ||
| "name": "@pentatrail/mcp-server", | ||
| "version": "0.6.1", | ||
| "version": "0.7.0", | ||
| "mcpName": "co.pentatrail/ctem", | ||
@@ -5,0 +5,0 @@ "description": "PentaTrail MCP Server — access your CTEM / ASM (attack surface management) data from an AI agent via the Model Context Protocol", |
+7
-15
@@ -7,6 +7,5 @@ # @pentatrail/mcp-server | ||
| The published server exposes the 17 public CTEM tools registered by | ||
| `registerPublicCtemTools` — 16 read tools plus one write tool | ||
| (`ctem_exclude_bulk`, which needs the `ctem:exclusions:write` scope). Internal | ||
| MCP-only tools are not registered. | ||
| The published server exposes the public CTEM tools registered by | ||
| `registerPublicCtemTools`, and that surface is **read-only** — every tool needs | ||
| only the `ctem:read` scope. Internal MCP-only tools are not registered. | ||
@@ -51,7 +50,7 @@ ## Setup | ||
| ### CTEM Read (16) | ||
| ### CTEM Read | ||
| | Tool | Description | | ||
| |------|-------------| | ||
| | `ctem_list_domains` | List all monitored domains for your contract | | ||
| | `ctem_list_domains` | List your monitored domains. **Start here** — by default it returns active domains only, and every domain in that default result can be passed straight to the other tools as `domain_id`. `include_archived: true` drops the status filter, so the result then also contains non-active domains (archived, and legacy paused) whose ids the other tools reject with `PT_API_NOT_FOUND` | | ||
| | `ctem_list_hosts` | List hosts with enrichment (port/tech/finding counts, sortable) | | ||
@@ -73,8 +72,2 @@ | `ctem_list_findings` | List vulnerabilities sorted by Threat Discovery Level (TDL) | | ||
| ### CTEM Write (1) | ||
| | Tool | Description | | ||
| |------|-------------| | ||
| | `ctem_exclude_bulk` | Exclude multiple assets in one call. Requires the `ctem:exclusions:write` scope; audit-logged. | | ||
| ## Usage Examples | ||
@@ -94,5 +87,4 @@ | ||
| - PentaTrail account with an active contract | ||
| - API key with `ctem:read` scope (add `ctem:exclusions:write` only if you want | ||
| `ctem_exclude_bulk` to work; without it that one tool returns a scope error and | ||
| the 16 read tools are unaffected) | ||
| - API key with the `ctem:read` scope — that is the only scope this server needs, | ||
| because every tool it registers reads | ||
@@ -99,0 +91,0 @@ ## License |
26354
-0.11%360
-1.37%90
-8.16%