cortex_list_jobs
List recent analysis jobs with filters for data type, analyzer name, status, and maximum results.
Instructions
List recent analysis jobs with optional filters
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| dataType | No | Filter by data type | |
| analyzerName | No | Filter by analyzer name | |
| limit | No | Maximum number of jobs to return (default: 50) | |
| status | No | Filter by status (Waiting, InProgress, Success, Failure, Deleted) |
Implementation Reference
- src/tools/jobs.ts:5-405 (registration)The registerJobTools function is where all job tools (including cortex_list_jobs) are registered on the MCP server via server.tool() calls.
export function registerJobTools( server: McpServer, client: CortexClient, ): void { server.tool( "cortex_get_job", "Get the status and details of an analysis job", { jobId: z.string().describe("The job ID to look up"), }, async ({ jobId }) => { try { const job = await client.getJob(jobId); return { content: [ { type: "text" as const, text: JSON.stringify(job, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text" as const, text: `Error getting job: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, ); server.tool( "cortex_get_job_report", "Get the full report of a completed analysis job", { jobId: z.string().describe("The job ID to get the report for"), }, async ({ jobId }) => { try { const report = await client.getJobReport(jobId); return { content: [ { type: "text" as const, text: JSON.stringify(report, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text" as const, text: `Error getting job report: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, ); server.tool( "cortex_wait_and_get_report", "Wait for a job to complete and return the full report (with polling timeout)", { jobId: z.string().describe("The job ID to wait for"), timeout: z .number() .int() .min(1) .max(3600) .default(300) .describe("Timeout in seconds (default: 300, max: 3600)"), }, async ({ jobId, timeout }) => { try { const report = await client.waitAndGetReport(jobId, timeout); const taxonomies = report.report?.summary?.taxonomies ?? []; const taxonomySummary = taxonomies.map( (t) => `[${t.level}] ${t.namespace}:${t.predicate} = ${t.value}`, ); return { content: [ { type: "text" as const, text: JSON.stringify( { jobId: report.id, status: report.status, analyzerName: report.analyzerName, taxonomies: taxonomySummary, fullReport: report.report, }, null, 2, ), }, ], }; } catch (error) { return { content: [ { type: "text" as const, text: `Error waiting for report: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, ); server.tool( "cortex_list_jobs", "List recent analysis jobs with optional filters", { dataType: z .string() .optional() .describe("Filter by data type"), analyzerName: z .string() .optional() .describe("Filter by analyzer name"), limit: z .number() .int() .min(1) .max(500) .default(50) .describe("Maximum number of jobs to return (default: 50)"), status: z .string() .optional() .describe( "Filter by status (Waiting, InProgress, Success, Failure, Deleted)", ), }, async ({ dataType, analyzerName, limit, status }) => { try { const must: Record<string, unknown>[] = []; if (dataType) { must.push({ _field: "dataType", _value: dataType }); } if (analyzerName) { must.push({ _field: "analyzerName", _value: analyzerName }); } if (status) { must.push({ _field: "status", _value: status }); } const query: Record<string, unknown> = must.length > 0 ? { _and: must } : { _field: "status", _value: "*" }; const jobs = await client.searchJobs({ query, range: `0-${limit}`, sort: ["-createdAt"], }); const summary = jobs.map((j) => ({ id: j.id, analyzerName: j.analyzerName, dataType: j.dataType, data: j.data, status: j.status, startDate: j.startDate ? new Date(j.startDate).toISOString() : undefined, endDate: j.endDate ? new Date(j.endDate).toISOString() : undefined, })); return { content: [ { type: "text" as const, text: JSON.stringify(summary, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text" as const, text: `Error listing jobs: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, ); server.tool( "cortex_get_job_artifacts", "Get artifacts (extracted observables/IOCs) from a completed analysis job", { jobId: z.string().describe("The job ID to get artifacts for"), }, async ({ jobId }) => { try { const artifacts = await client.getJobArtifacts(jobId); return { content: [ { type: "text" as const, text: JSON.stringify(artifacts, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text" as const, text: `Error getting job artifacts: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, ); server.tool( "cortex_delete_job", "Delete a specific analysis job by ID", { jobId: z.string().describe("The job ID to delete"), }, async ({ jobId }) => { try { await client.deleteJob(jobId); return { content: [ { type: "text" as const, text: `Job "${jobId}" deleted successfully.`, }, ], }; } catch (error) { return { content: [ { type: "text" as const, text: `Error deleting job: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, ); server.tool( "cortex_cleanup_jobs", "Delete multiple jobs by status or age. Useful for cleaning up failed or old jobs.", { status: z .enum(["Failure", "Deleted", "Success", "Waiting", "InProgress"]) .optional() .describe("Delete jobs with this status"), olderThanDays: z .number() .int() .min(1) .optional() .describe("Delete jobs older than this many days"), dryRun: z .boolean() .default(true) .describe("If true (default), only count matching jobs without deleting them"), limit: z .number() .int() .min(1) .max(500) .default(100) .describe("Maximum jobs to process (default: 100)"), }, async ({ status, olderThanDays, dryRun, limit }) => { try { if (!status && !olderThanDays) { return { content: [ { type: "text" as const, text: "Please specify at least one filter: status or olderThanDays.", }, ], isError: true, }; } const must: Record<string, unknown>[] = []; if (status) { must.push({ _field: "status", _value: status }); } const query: Record<string, unknown> = must.length > 0 ? must.length === 1 ? must[0] : { _and: must } : { _field: "status", _value: "*" }; const jobs = await client.searchJobs({ query, range: `0-${limit}`, sort: ["-createdAt"], }); let matchingJobs = jobs; if (olderThanDays) { const cutoff = Date.now() - olderThanDays * 24 * 60 * 60 * 1000; matchingJobs = jobs.filter( (j) => (j.createdAt ?? 0) < cutoff, ); } if (dryRun) { return { content: [ { type: "text" as const, text: JSON.stringify( { dryRun: true, matchingJobs: matchingJobs.length, message: `Found ${matchingJobs.length} jobs matching criteria. Set dryRun=false to delete them.`, jobs: matchingJobs.map((j) => ({ id: j.id, status: j.status, analyzer: j.analyzerName, data: j.data, createdAt: j.createdAt ? new Date(j.createdAt).toISOString() : undefined, })), }, null, 2, ), }, ], }; } // Actually delete const results = await Promise.allSettled( matchingJobs.map((j) => client.deleteJob(j.id)), ); const deleted = results.filter((r) => r.status === "fulfilled").length; const failed = results.filter((r) => r.status === "rejected").length; return { content: [ { type: "text" as const, text: JSON.stringify( { dryRun: false, deleted, failed, total: matchingJobs.length, message: `Deleted ${deleted}/${matchingJobs.length} jobs.${failed > 0 ? ` ${failed} deletions failed.` : ""}`, }, null, 2, ), }, ], }; } catch (error) { return { content: [ { type: "text" as const, text: `Error cleaning up jobs: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, ); } - src/tools/jobs.ts:125-210 (handler)The cortex_list_jobs tool handler: defines input schema (dataType, analyzerName, limit, status), builds an Elasticsearch-style query, calls client.searchJobs(), and returns a summary array of jobs.
server.tool( "cortex_list_jobs", "List recent analysis jobs with optional filters", { dataType: z .string() .optional() .describe("Filter by data type"), analyzerName: z .string() .optional() .describe("Filter by analyzer name"), limit: z .number() .int() .min(1) .max(500) .default(50) .describe("Maximum number of jobs to return (default: 50)"), status: z .string() .optional() .describe( "Filter by status (Waiting, InProgress, Success, Failure, Deleted)", ), }, async ({ dataType, analyzerName, limit, status }) => { try { const must: Record<string, unknown>[] = []; if (dataType) { must.push({ _field: "dataType", _value: dataType }); } if (analyzerName) { must.push({ _field: "analyzerName", _value: analyzerName }); } if (status) { must.push({ _field: "status", _value: status }); } const query: Record<string, unknown> = must.length > 0 ? { _and: must } : { _field: "status", _value: "*" }; const jobs = await client.searchJobs({ query, range: `0-${limit}`, sort: ["-createdAt"], }); const summary = jobs.map((j) => ({ id: j.id, analyzerName: j.analyzerName, dataType: j.dataType, data: j.data, status: j.status, startDate: j.startDate ? new Date(j.startDate).toISOString() : undefined, endDate: j.endDate ? new Date(j.endDate).toISOString() : undefined, })); return { content: [ { type: "text" as const, text: JSON.stringify(summary, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text" as const, text: `Error listing jobs: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, ); - src/tools/jobs.ts:128-150 (schema)Input schema for cortex_list_jobs: optional dataType (string), optional analyzerName (string), optional limit (int, 1-500, default 50), optional status (string filter).
{ dataType: z .string() .optional() .describe("Filter by data type"), analyzerName: z .string() .optional() .describe("Filter by analyzer name"), limit: z .number() .int() .min(1) .max(500) .default(50) .describe("Maximum number of jobs to return (default: 50)"), status: z .string() .optional() .describe( "Filter by status (Waiting, InProgress, Success, Failure, Deleted)", ), }, - src/client.ts:302-307 (helper)The client.searchJobs() helper method that sends a POST request to /job/_search with the query body.
async searchJobs(query: JobSearchQuery): Promise<Job[]> { return this.request<Job[]>("/job/_search", { method: "POST", body: JSON.stringify(query), }); } - src/index.ts:35-36 (registration)Registration call in the main entry point: registerJobTools(server, client) wires the job tools into the MCP server during startup.
registerJobTools(server, client); registerResponderTools(server, client);