UNPKG

@jupiterone/jupiterone-mcp

Version:

Model Context Protocol server for JupiterOne account rules and rule details

971 lines 84.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.JupiterOneMcpServer = void 0; const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js"); const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js"); const zod_1 = require("zod"); const jupiterone_client_js_1 = require("../client/jupiterone-client.js"); const load_description_js_1 = require("../utils/load-description.js"); const j1ql_validator_js_1 = require("../utils/j1ql-validator.js"); class JupiterOneMcpServer { server; client; validator; hasEnvironmentAccount; constructor(config) { this.client = new jupiterone_client_js_1.JupiterOneClient(config); this.validator = new j1ql_validator_js_1.J1QLValidator(this.client.j1qlService); this.server = new mcp_js_1.McpServer({ name: 'jupiterone-mcp', version: '1.0.0', }); this.hasEnvironmentAccount = !config.accountId; this.setupTools(); } registerTool(options) { const { name, schema, handler, description } = options; let finalSchema = schema; if (this.hasEnvironmentAccount) { finalSchema = { ...schema, accountId: zod_1.z .string() .describe('JupiterOne Account ID for this request. This should be explicitly asked for with the first tool call.'), }; } this.server.tool(name, description || '', finalSchema, async (params) => { let client = this.client; let toolParams = params; let validator = this.validator; if (this.hasEnvironmentAccount) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { accountId, ...otherParams } = params; if (!accountId) { return { content: [ { type: 'text', text: 'Error: accountId is required for this operation because no default JUPITERONE_ACCOUNT_ID is configured.', }, ], isError: true, }; } client = this.client.cloneWithAccountId(accountId); validator = new j1ql_validator_js_1.J1QLValidator(client.j1qlService); toolParams = otherParams; } return handler(toolParams, client, validator); }); } setupTools() { // Tool: List all rules this.registerTool({ name: 'list-rules', description: (0, load_description_js_1.loadDescription)('list-rules.md'), schema: { limit: zod_1.z.number().min(1).max(1000).optional(), cursor: zod_1.z.string().optional(), }, handler: async ({ limit, cursor }, client) => { try { const response = await client.listRuleInstances(limit, cursor); const instances = response.questionInstances || []; return { content: [ { type: 'text', text: JSON.stringify({ returned: instances.length, rules: instances.map((instance) => ({ id: instance.id, name: instance.name, description: instance.description, version: instance.version, pollingInterval: instance.pollingInterval, lastEvaluationStartOn: instance.lastEvaluationStartOn, lastEvaluationEndOn: instance.lastEvaluationEndOn, latestAlertId: instance.latestAlertId, latestAlertIsActive: instance.latestAlertIsActive, type: instance.type, tags: instance.tags, outputs: instance.outputs, })), pageInfo: response.pageInfo, }, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error listing rules: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } }, }); // Tool: Get rule details this.registerTool({ name: 'get-rule-details', schema: { ruleId: zod_1.z.string(), }, handler: async ({ ruleId }, client) => { try { const instances = await client.getAllRuleInstances(); const rule = instances.find((instance) => instance.id === ruleId); if (!rule) { return { content: [ { type: 'text', text: `Rule with ID ${ruleId} not found`, }, ], isError: true, }; } return { content: [ { type: 'text', text: JSON.stringify({ id: rule.id, resourceGroupId: rule.resourceGroupId, accountId: rule.accountId, name: rule.name, description: rule.description, version: rule.version, lastEvaluationStartOn: rule.lastEvaluationStartOn, lastEvaluationEndOn: rule.lastEvaluationEndOn, evaluationStep: rule.evaluationStep, specVersion: rule.specVersion, notifyOnFailure: rule.notifyOnFailure, triggerActionsOnNewEntitiesOnly: rule.triggerActionsOnNewEntitiesOnly, ignorePreviousResults: rule.ignorePreviousResults, pollingInterval: rule.pollingInterval, templates: rule.templates, outputs: rule.outputs, labels: rule.labels?.map((label) => ({ labelName: label.labelName, labelValue: label.labelValue, })) || [], question: rule.question ? { queries: rule.question.queries?.map((query) => ({ query: query.query, name: query.name, includeDeleted: query.includeDeleted, })) || [], } : null, questionId: rule.questionId, latest: rule.latest, deleted: rule.deleted, type: rule.type, operations: rule.operations?.map((op) => ({ when: op.when, actions: op.actions, })) || [], latestAlertId: rule.latestAlertId, latestAlertIsActive: rule.latestAlertIsActive, state: rule.state ? { actions: rule.state.actions, } : null, tags: rule.tags, remediationSteps: rule.remediationSteps, }, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error getting rule details: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } }, }); // Tool: Test connection this.registerTool({ name: 'test-connection', schema: {}, handler: async (_, client) => { try { const isConnected = await client.testConnection(); const accountInfo = isConnected ? await client.getAccountInfo() : null; return { content: [ { type: 'text', text: JSON.stringify({ connected: isConnected, account: accountInfo, }, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Connection test failed: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } }, }); // Tool: Evaluate rule this.registerTool({ name: 'evaluate-rule', schema: { ruleId: zod_1.z.string(), }, handler: async ({ ruleId }, client) => { try { const result = await client.evaluateRuleInstance(ruleId); return { content: [ { type: 'text', text: JSON.stringify({ ruleId, id: result.id, __typename: result.__typename, }, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error evaluating rule: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } }, }); // Tool: Get active alerts this.registerTool({ name: 'get-active-alerts', description: (0, load_description_js_1.loadDescription)('list-alerts.md'), schema: { limit: zod_1.z.number().min(1).max(1000).optional(), }, handler: async ({ limit }, client) => { try { const instances = await client.getAllAlertInstances('ACTIVE'); if (!instances || !Array.isArray(instances)) { throw new Error('Invalid response from JupiterOne API: instances is not an array'); } const limitedInstances = limit ? instances.slice(0, limit) : instances; return { content: [ { type: 'text', text: JSON.stringify({ total: instances.length, returned: limitedInstances.length, activeAlerts: limitedInstances.map((instance) => ({ id: instance.id, name: instance.questionRuleInstance?.name || instance.reportRuleInstance?.name || 'Unknown', description: instance.questionRuleInstance?.description || instance.reportRuleInstance?.description, level: instance.level, status: instance.status, createdOn: instance.createdOn, lastUpdatedOn: instance.lastUpdatedOn, lastEvaluationBeginOn: instance.lastEvaluationBeginOn, lastEvaluationEndOn: instance.lastEvaluationEndOn, recordCount: instance.lastEvaluationResult?.rawDataDescriptors?.[0]?.recordCount || 0, tags: instance.questionRuleInstance?.tags || [], labels: instance.questionRuleInstance?.labels || [], outputs: instance.lastEvaluationResult?.outputs || [], users: instance.users, ruleId: instance.ruleId, ruleVersion: instance.ruleVersion, endReason: instance.endReason, dismissedOn: instance.dismissedOn, })), }, null, 2), }, ], }; } catch (error) { console.error('Error in get-active-alerts:', error); return { content: [ { type: 'text', text: `Error getting active alerts: ${error instanceof Error ? error.message : 'Unknown error'}. Please check your JupiterOne API credentials and connection.`, }, ], isError: true, }; } }, }); // Tool: Get dashboards this.registerTool({ name: 'get-dashboards', schema: {}, handler: async (_, client) => { try { const dashboards = await client.getDashboards(); return { content: [ { type: 'text', text: JSON.stringify({ total: dashboards.length, dashboards: dashboards.map((dashboard) => ({ id: dashboard.id, name: dashboard.name, category: dashboard.category, supportedUseCase: dashboard.supportedUseCase, isJ1ManagedBoard: dashboard.isJ1ManagedBoard, resourceGroupId: dashboard.resourceGroupId, starred: dashboard.starred, lastUpdated: dashboard._timeUpdated, createdAt: dashboard._createdAt, prerequisites: dashboard.prerequisites ? { prerequisitesMet: dashboard.prerequisites.prerequisitesMet, preRequisitesGroupsFulfilled: dashboard.prerequisites.preRequisitesGroupsFulfilled, preRequisitesGroupsRequired: dashboard.prerequisites.preRequisitesGroupsRequired, } : null, })), }, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error getting dashboards: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } }, }); // Tool: Create dashboard this.registerTool({ name: 'create-dashboard', description: (0, load_description_js_1.loadDescription)('create-dashboard.md'), schema: { name: zod_1.z.string(), type: zod_1.z.string(), }, handler: async ({ name, type }, client) => { try { const result = await client.createDashboard({ name, type }); return { content: [ { type: 'text', text: JSON.stringify({ id: result.id, name, type, }, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error creating dashboard: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } }, }); // Tool: Get dashboard details this.registerTool({ name: 'get-dashboard-details', schema: { dashboardId: zod_1.z.string(), }, handler: async ({ dashboardId }, client) => { try { const dashboard = await client.getDashboard(dashboardId); return { content: [ { type: 'text', text: JSON.stringify({ id: dashboard.id, name: dashboard.name, category: dashboard.category, supportedUseCase: dashboard.supportedUseCase, isJ1ManagedBoard: dashboard.isJ1ManagedBoard, published: dashboard.published, publishedToUserIds: dashboard.publishedToUserIds, publishedToGroupIds: dashboard.publishedToGroupIds, groupIds: dashboard.groupIds, userIds: dashboard.userIds, scopeFilters: dashboard.scopeFilters, resourceGroupId: dashboard.resourceGroupId, starred: dashboard.starred, lastUpdated: dashboard._timeUpdated, createdAt: dashboard._createdAt, prerequisites: dashboard.prerequisites ? { prerequisitesMet: dashboard.prerequisites.prerequisitesMet, preRequisitesGroupsFulfilled: dashboard.prerequisites.preRequisitesGroupsFulfilled, preRequisitesGroupsRequired: dashboard.prerequisites.preRequisitesGroupsRequired, } : null, parameters: dashboard.parameters.map((param) => ({ id: param.id, label: param.label, name: param.name, type: param.type, valueType: param.valueType, default: param.default, options: param.options, requireValue: param.requireValue, disableCustomInput: param.disableCustomInput, })), widgets: dashboard.widgets.map((widget) => ({ id: widget.id, title: widget.title, description: widget.description, type: widget.type, questionId: widget.questionId, noResultMessage: widget.noResultMessage, includeDeleted: widget.includeDeleted, config: { queries: widget.config.queries.map((query) => ({ id: query.id, name: query.name, query: query.query, })), settings: widget.config.settings, postQueryFilters: widget.config.postQueryFilters, disableQueryPolicyFilters: widget.config.disableQueryPolicyFilters, }, })), layouts: { xs: dashboard.layouts.xs.map((item) => ({ i: item.i, x: item.x, y: item.y, w: item.w, h: item.h, static: item.static, moved: item.moved, })), sm: dashboard.layouts.sm.map((item) => ({ i: item.i, x: item.x, y: item.y, w: item.w, h: item.h, static: item.static, moved: item.moved, })), md: dashboard.layouts.md.map((item) => ({ i: item.i, x: item.x, y: item.y, w: item.w, h: item.h, static: item.static, moved: item.moved, })), lg: dashboard.layouts.lg.map((item) => ({ i: item.i, x: item.x, y: item.y, w: item.w, h: item.h, static: item.static, moved: item.moved, })), xl: dashboard.layouts.xl.map((item) => ({ i: item.i, x: item.x, y: item.y, w: item.w, h: item.h, static: item.static, moved: item.moved, })), }, }, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error getting dashboard details: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } }, }); // Tool: Get integration definitions this.registerTool({ name: 'get-integration-definitions', description: (0, load_description_js_1.loadDescription)('get-integration-definitions.md'), schema: { cursor: zod_1.z.string().optional().describe('Optional cursor for pagination'), includeConfig: zod_1.z.boolean().optional().describe('Whether to include configuration fields'), }, handler: async ({ cursor, includeConfig }, client) => { try { const definitions = await client.getIntegrationDefinitions(cursor, includeConfig); return { content: [ { type: 'text', text: JSON.stringify({ total: definitions.definitions.length, definitions: definitions.definitions.map((def) => ({ id: def.id, name: def.name, type: def.type, title: def.title, displayMode: def.displayMode, integrationType: def.integrationType, integrationClass: def.integrationClass, integrationCategory: def.integrationCategory, beta: def.beta, docsWebLink: def.docsWebLink, repoWebLink: def.repoWebLink, invocationPaused: def.invocationPaused, managedExecutionDisabled: def.managedExecutionDisabled, managedCreateDisabled: def.managedCreateDisabled, managedDeleteDisabled: def.managedDeleteDisabled, totalInstanceCount: def.totalInstanceCount, description: def.description, provisioningType: def.provisioningType, customDefinitionType: def.customDefinitionType, integrationPlatformFeatures: def.integrationPlatformFeatures, configFields: def.configFields, authSections: def.authSections, configSections: def.configSections, })), pageInfo: definitions.pageInfo, }, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error getting integration definitions: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } }, }); // Tool: Get integration instances this.registerTool({ name: 'get-integration-instances', description: (0, load_description_js_1.loadDescription)('get-integration-instances.md'), schema: { definitionId: zod_1.z .string() .optional() .describe('Optional ID to filter instances by definition'), limit: zod_1.z .number() .min(1) .max(1000) .optional() .describe('Optional limit for number of instances to return'), }, handler: async ({ definitionId, limit }, client) => { try { const instances = await client.getIntegrationInstances(definitionId, undefined, limit); return { content: [ { type: 'text', text: JSON.stringify({ total: instances.instances.length, instances: instances.instances.map((instance) => ({ id: instance.id, name: instance.name, accountId: instance.accountId, sourceIntegrationInstanceId: instance.sourceIntegrationInstanceId, pollingInterval: instance.pollingInterval, pollingIntervalCronExpression: instance.pollingIntervalCronExpression, integrationDefinitionId: instance.integrationDefinitionId, description: instance.description, config: instance.config, instanceRelationship: instance.instanceRelationship, resourceGroupId: instance.resourceGroupId, createdOn: instance.createdOn, createdBy: instance.createdBy, updatedOn: instance.updatedOn, updatedBy: instance.updatedBy, mostRecentJob: instance.mostRecentJob ? { status: instance.mostRecentJob.status, hasSkippedSteps: instance.mostRecentJob.hasSkippedSteps, createDate: instance.mostRecentJob.createDate, } : null, })), pageInfo: instances.pageInfo, }, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error getting integration instances: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } }, }); // Tool: Get integration jobs this.registerTool({ name: 'get-integration-jobs', schema: { status: zod_1.z .enum(['PENDING', 'RUNNING', 'COMPLETED', 'FAILED', 'CANCELLED']) .optional() .describe('Optional status to filter jobs'), integrationInstanceId: zod_1.z .string() .optional() .describe('Optional ID to filter jobs by instance'), integrationDefinitionId: zod_1.z .string() .optional() .describe('Optional ID to filter jobs by definition'), integrationInstanceIds: zod_1.z .array(zod_1.z.string()) .optional() .describe('Optional array of instance IDs to filter jobs'), size: zod_1.z .number() .min(1) .max(1000) .optional() .describe('Optional size limit for number of jobs to return'), }, handler: async ({ status, integrationInstanceId, integrationDefinitionId, integrationInstanceIds, size }, client) => { try { const jobs = await client.getIntegrationJobs(status, integrationInstanceId, integrationDefinitionId, integrationInstanceIds, undefined, size); return { content: [ { type: 'text', text: JSON.stringify({ total: jobs.jobs.length, jobs: jobs.jobs.map((job) => ({ id: job.id, status: job.status, integrationInstanceId: job.integrationInstanceId, createDate: job.createDate, endDate: job.endDate, hasSkippedSteps: job.hasSkippedSteps, integrationInstance: job.integrationInstance, integrationDefinition: job.integrationDefinition, })), pageInfo: jobs.pageInfo, }, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error getting integration jobs: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } }, }); // Tool: Create inline question rule instance this.registerTool({ name: 'create-inline-question-rule', description: (0, load_description_js_1.loadDescription)('create-inline-question-rule.md'), schema: { name: zod_1.z.string().describe('Name of the rule'), description: zod_1.z.string().describe('Description of the rule'), notifyOnFailure: zod_1.z.boolean().optional().describe('Whether to notify on failure'), triggerActionsOnNewEntitiesOnly: zod_1.z .boolean() .optional() .describe('Whether to trigger actions only on new entities'), ignorePreviousResults: zod_1.z .boolean() .optional() .describe('Whether to ignore previous results'), pollingInterval: zod_1.z .enum([ 'DISABLED', 'THIRTY_MINUTES', 'ONE_HOUR', 'FOUR_HOURS', 'EIGHT_HOURS', 'TWELVE_HOURS', 'ONE_DAY', 'ONE_WEEK', ]) .describe('How frequently to evaluate the rule'), outputs: zod_1.z.array(zod_1.z.string()).describe('Output fields from the rule evaluation'), specVersion: zod_1.z.number().optional().describe('Specification version'), tags: zod_1.z.array(zod_1.z.string()).optional().describe('Tags for categorizing the rule'), templates: zod_1.z.record(zod_1.z.any()).optional().describe('Template variables'), question: zod_1.z .object({ queries: zod_1.z.array(zod_1.z.object({ query: zod_1.z.string().describe('J1QL query string'), name: zod_1.z.string().describe('Name identifier for the query'), version: zod_1.z.string().optional().describe('Version of the query'), includeDeleted: zod_1.z.boolean().describe('Whether to include deleted entities'), })), }) .describe('Question configuration'), labels: zod_1.z .array(zod_1.z.object({ labelName: zod_1.z.string(), labelValue: zod_1.z.string().nullable(), })) .describe('Labels for the rule'), operations: zod_1.z .array(zod_1.z.object({ when: zod_1.z .object({ type: zod_1.z.literal('FILTER'), condition: zod_1.z .array(zod_1.z.union([zod_1.z.string(), zod_1.z.number(), zod_1.z.array(zod_1.z.union([zod_1.z.string(), zod_1.z.number()]))])) .describe('Filter condition array (e.g., ["AND", ["queries.query0.total", ">", 0]])'), }) .describe('Condition that triggers the actions'), actions: zod_1.z .array(zod_1.z.object({ id: zod_1.z.string().optional(), type: zod_1.z .string() .describe('Action type (e.g., SET_PROPERTY, CREATE_ALERT, SEND_EMAIL)'), targetProperty: zod_1.z .string() .optional() .describe('Property to set (for SET_PROPERTY actions)'), targetValue: zod_1.z .any() .optional() .describe('Value to set (for SET_PROPERTY actions)'), integrationInstanceId: zod_1.z .string() .optional() .describe('ID of the integration instance for integration actions'), recipients: zod_1.z .array(zod_1.z.string()) .optional() .describe('Email recipients for SEND_EMAIL action'), body: zod_1.z.string().optional().describe('Message body for email/slack actions'), channels: zod_1.z .array(zod_1.z.string()) .optional() .describe('Slack channels for SEND_SLACK_MESSAGE action'), bucket: zod_1.z.string().optional().describe('S3 bucket name for SEND_TO_S3 action'), region: zod_1.z.string().optional().describe('AWS region for SEND_TO_S3 action'), data: zod_1.z.any().optional().describe('Additional data for actions'), entityClass: zod_1.z .string() .optional() .describe('Entity class for CREATE_JIRA_TICKET action'), summary: zod_1.z .string() .optional() .describe('Summary for CREATE_JIRA_TICKET action'), issueType: zod_1.z .string() .optional() .describe('Issue type for CREATE_JIRA_TICKET action'), project: zod_1.z .string() .optional() .describe('Project key for CREATE_JIRA_TICKET action'), updateContentOnChanges: zod_1.z .boolean() .optional() .describe('Whether to update content on changes for CREATE_JIRA_TICKET action'), additionalFields: zod_1.z .any() .optional() .describe('Additional fields for CREATE_JIRA_TICKET action'), })) .describe('Actions to take when condition is met'), })) .describe('Operations to perform when conditions are met'), }, handler: async ({ name, description, notifyOnFailure, triggerActionsOnNewEntitiesOnly, ignorePreviousResults, pollingInterval, outputs, specVersion, labels, tags, templates, question, operations, }, client, validator) => { try { // Validate all queries before creating the rule const validationResults = await this.validateQueries(question.queries, validator); if (validationResults.length > 0) { return this.createValidationErrorResponse(validationResults); } const instance = { name, description, notifyOnFailure, triggerActionsOnNewEntitiesOnly, ignorePreviousResults, pollingInterval, outputs, specVersion, labels, tags, templates, question, operations, }; const result = await client.createInlineQuestionRuleInstance(instance); return { content: [ { type: 'text', text: JSON.stringify({ success: true, rule: { id: result.id, name: result.name, description: result.description, version: result.version, pollingInterval: result.pollingInterval, outputs: result.outputs, specVersion: result.specVersion, notifyOnFailure: result.notifyOnFailure, triggerActionsOnNewEntitiesOnly: result.triggerActionsOnNewEntitiesOnly, ignorePreviousResults: result.ignorePreviousResults, tags: result.tags, question: result.question, operations: result.operations, latestAlertId: result.latestAlertId, latestAlertIsActive: result.latestAlertIsActive, }, }, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error creating inline question rule: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } }, }); // Tool: Update inline question rule instance this.registerTool({ name: 'update-inline-question-rule', description: (0, load_description_js_1.loadDescription)('update-inline-question-rule.md'), schema: { id: zod_1.z.string().describe('ID of the rule to update'), name: zod_1.z.string().describe('Name of the rule'), description: zod_1.z.string().describe('Description of the rule'), notifyOnFailure: zod_1.z.boolean().describe('Whether to notify on failure'), triggerActionsOnNewEntitiesOnly: zod_1.z .boolean() .describe('Whether to trigger actions only on new entities'), ignorePreviousResults: zod_1.z.boolean().describe('Whether to ignore previous results'), pollingInterval: zod_1.z .enum([ 'DISABLED', 'THIRTY_MINUTES', 'ONE_HOUR', 'FOUR_HOURS', 'EIGHT_HOURS', 'TWELVE_HOURS', 'ONE_DAY', 'ONE_WEEK', ]) .describe('How frequently to evaluate the rule'), outputs: zod_1.z.array(zod_1.z.string()).describe('Output fields from the rule evaluation'), specVersion: zod_1.z.number().describe('Specification version'), tags: zod_1.z.array(zod_1.z.string()).describe('Tags for categorizing the rule'), templates: zod_1.z.record(zod_1.z.any()).describe('Template variables'), labels: zod_1.z .array(zod_1.z.object({ labelName: zod_1.z.string(), labelValue: zod_1.z.string().nullable(), })) .describe('Labels for the rule'), resourceGroupId: zod_1.z.string().nullable().describe('Resource group ID'), remediationSteps: zod_1.z .string() .nullable() .describe('Steps to remediate issues found by the rule'), question: zod_1.z .object({ queries: zod_1.z.array(zod_1.z.object({ query: zod_1.z.string().describe('J1QL query string'), name: zod_1.z.string().describe('Name identifier for the query'), version: zod_1.z.string().optional().de