@jupiterone/jupiterone-mcp
Version:
Model Context Protocol server for JupiterOne account rules and rule details
103 lines • 5.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.j1qlTools = void 0;
const zod_1 = require("zod");
const load_description_js_1 = require("../../utils/load-description.js");
const errors_js_1 = require("./errors.js");
const result_js_1 = require("./result.js");
const types_js_1 = require("./types.js");
// J1QL output is highly variable: `data` rows carry open `entity`/`properties`, and the envelope
// shape differs by query type. Fields are `.nullish()` (the SDK rejects a declared field receiving
// `null`, e.g. `cursor: null` on the last page) and rows are open so real results never
// false-reject; the schema documents the shape and exposes the typed object for code-mode / UI.
// `data` is array-or-object: `RETURN TREE` returns `{ vertices, edges }`, everything else an array.
const executeJ1qlOutput = {
correlationId: zod_1.z.string().nullish(),
status: zod_1.z.string().nullish(),
query: zod_1.z.string().optional(),
duration: zod_1.z.number().nullish(),
url: zod_1.z.string().optional(),
type: zod_1.z.string().nullish(),
data: zod_1.z.union([zod_1.z.array(zod_1.z.any()), zod_1.z.record(zod_1.z.any())]).nullish(),
cursor: zod_1.z.string().nullish(),
hasMore: zod_1.z.boolean().optional(),
messages: zod_1.z.array(zod_1.z.any()).optional(),
};
// One-line note emitted as a separate text content block when more pages exist, steering agents to
// the cursor parameter rather than SKIP (which re-scans from the start and is not a pagination
// mechanism). A separate block keeps content[0] as pure payload JSON (parity with structuredContent)
// for consumers that parse the text channel.
const PAGINATION_NOTE = 'More results available — call execute-j1ql-query again with the `cursor` parameter. ' +
'Do not use SKIP for pagination.';
const listEntityTypesOutput = {
classes: zod_1.z.record(zod_1.z.any()).nullish(),
types: zod_1.z.record(zod_1.z.any()).nullish(),
};
exports.j1qlTools = [
(0, types_js_1.defineTool)({
name: 'execute-j1ql-query',
title: 'Execute J1QL Query',
description: (0, load_description_js_1.loadDescription)('execute-j1ql-query.md'),
inputSchema: {
query: zod_1.z.string().describe('A J1QL query string that describes what data to return'),
variables: zod_1.z
.record(zod_1.z.any())
.optional()
.describe('A JSON map of values to be used as parameters for the query'),
cursor: zod_1.z
.string()
.optional()
.describe('A token that can be exchanged to fetch the next page of information'),
includeDeleted: zod_1.z
.boolean()
.optional()
.describe('Include recently deleted information in the results'),
flags: zod_1.z.record(zod_1.z.any()).optional().describe('Flags for query execution'),
scopeFilters: zod_1.z
.array(zod_1.z.record(zod_1.z.any()))
.optional()
.describe('Array of filters that define the desired vertex'),
},
annotations: types_js_1.READ_ONLY,
outputSchema: executeJ1qlOutput,
errorContext: 'Error executing J1QL query',
formatError: (error, { query }, validator) => (0, errors_js_1.createQueryErrorResponse)(error, query, validator),
handler: async ({ query, variables, cursor, includeDeleted, flags, scopeFilters }, client) => {
const queryFlags = { ...flags };
if (typeof includeDeleted === 'boolean')
queryFlags.includeDeleted = includeDeleted;
const result = await client.executeJ1qlQuery({
query,
variables,
cursor,
scopeFilters,
flags: Object.keys(queryFlags).length > 0 ? queryFlags : undefined,
});
const accountInfo = await client.getAccountInfo();
const queryUrl = client.j1qlService.constructQueryUrl(query, accountInfo.subdomain);
// The backend signals more pages by returning a cursor. Surface it as an explicit boolean and,
// when true, prepend a cursor-paging note to the text (model-facing) channel; structuredContent
// stays the pure payload.
const hasMore = typeof result.cursor === 'string' && result.cursor.length > 0;
const output = (0, result_js_1.structuredResult)({ ...result, url: queryUrl, hasMore });
if (hasMore) {
output.content.push({ type: 'text', text: PAGINATION_NOTE });
}
return output;
},
}),
(0, types_js_1.defineTool)({
name: 'list-entity-types',
title: 'List Entity Types',
description: (0, load_description_js_1.loadDescription)('list-entity-types.md'),
inputSchema: {},
outputSchema: listEntityTypesOutput,
annotations: types_js_1.READ_ONLY,
errorContext: 'Error listing entity types',
handler: async (_params, client) => {
const results = await client.listEntityTypes();
return (0, result_js_1.structuredResult)(results);
},
}),
];
//# sourceMappingURL=j1ql.js.map