@jupiterone/jupiterone-mcp 0.0.2 → 0.0.4
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/README.md +95 -1
- package/dist/client/graphql/queries.d.ts +2 -2
- package/dist/client/graphql/queries.d.ts.map +1 -1
- package/dist/client/graphql/queries.js +6 -54
- package/dist/client/graphql/queries.js.map +1 -1
- package/dist/client/jupiterone-client.d.ts +1 -1
- package/dist/client/jupiterone-client.d.ts.map +1 -1
- package/dist/client/jupiterone-client.js.map +1 -1
- package/dist/client/services/j1ql-service.d.ts.map +1 -1
- package/dist/client/services/j1ql-service.js +35 -4
- package/dist/client/services/j1ql-service.js.map +1 -1
- package/dist/descriptions/create-inline-question-rule.md +19 -2
- package/dist/descriptions/execute-j1ql-query.md +47 -94
- package/dist/descriptions/get-integration-definitions.md +27 -0
- package/dist/descriptions/get-integration-instances.md +35 -0
- package/dist/descriptions/list-rules.md +44 -5
- package/dist/descriptions/update-inline-question-rule.md +363 -0
- package/dist/server/mcp-server.d.ts +5 -0
- package/dist/server/mcp-server.d.ts.map +1 -1
- package/dist/server/mcp-server.js +80 -20
- package/dist/server/mcp-server.js.map +1 -1
- package/dist/utils/j1ql-validator.d.ts +34 -0
- package/dist/utils/j1ql-validator.d.ts.map +1 -0
- package/dist/utils/j1ql-validator.js +288 -0
- package/dist/utils/j1ql-validator.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Get Integration Instances Tool
|
|
2
|
+
|
|
3
|
+
Get all integration instances in your JupiterOne account. This tool returns a list of configured integration instances, including their configuration, status, and recent job information. Integration instances are the actual configured connections to external services like AWS accounts, GitHub repositories, etc. Unless you have an integration definition id, you typically will not want to query this yet. To get an integration definition id, use the `get-integration-definitions` tool. If you need an integration instance id for another task (such as creating a rule action), ask the user which of the possible integrations they want you to use.
|
|
4
|
+
|
|
5
|
+
## Parameters
|
|
6
|
+
- `definitionId` (optional): Filter instances by a specific integration definition ID. Use this to get only instances of a particular integration type.
|
|
7
|
+
- `limit` (optional): Maximum number of instances to return (between 1 and 1000).
|
|
8
|
+
|
|
9
|
+
## Example Usage
|
|
10
|
+
Get all integration instances:
|
|
11
|
+
```json
|
|
12
|
+
{}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Get the first 10 integration instances:
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"limit": 10
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Get all instances of a specific integration type:
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"definitionId": "integration-definition-id-here"
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Get the first 5 instances of a specific integration type:
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"definitionId": "integration-definition-id-here",
|
|
33
|
+
"limit": 5
|
|
34
|
+
}
|
|
35
|
+
```
|
|
@@ -1,14 +1,53 @@
|
|
|
1
1
|
# List Rules Tool
|
|
2
2
|
|
|
3
|
-
List
|
|
3
|
+
List rules in your JupiterOne account using cursor pagination. This tool returns a page of rule instances, including their IDs, names, descriptions, versions, polling intervals, and other metadata. Use the cursor parameter to navigate through pages of results. This does not get alerts, but rather the configuration behind what may generate an alert, or other workflow action.
|
|
4
4
|
|
|
5
5
|
## Parameters
|
|
6
|
-
- `limit` (optional): Maximum number of rules to return (between 1 and 1000).
|
|
6
|
+
- `limit` (optional): Maximum number of rules to return per page (between 1 and 1000). Defaults to 100 if not specified.
|
|
7
|
+
- `cursor` (optional): Pagination cursor to get the next page of results. Use the `endCursor` from a previous response's `pageInfo`. Omit this parameter to get the first page.
|
|
7
8
|
|
|
8
9
|
## Example Usage
|
|
9
|
-
|
|
10
|
+
Get the first page of rules (default page size):
|
|
11
|
+
```json
|
|
12
|
+
{}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Get the first page with specific limit:
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"limit": 50
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Get the next page using a cursor:
|
|
10
23
|
```json
|
|
11
24
|
{
|
|
12
|
-
"limit":
|
|
25
|
+
"limit": 50,
|
|
26
|
+
"cursor": "cursor_value_from_previous_response"
|
|
13
27
|
}
|
|
14
|
-
```
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Response Format
|
|
31
|
+
All responses include pagination information:
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"returned": 50,
|
|
35
|
+
"rules": [...],
|
|
36
|
+
"pageInfo": {
|
|
37
|
+
"hasNextPage": true,
|
|
38
|
+
"endCursor": "cursor_for_next_page"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
- `returned`: Number of rules in this page
|
|
44
|
+
- `rules`: Array of rule objects
|
|
45
|
+
- `pageInfo.hasNextPage`: Whether there are more pages available
|
|
46
|
+
- `pageInfo.endCursor`: Cursor to use for the next page (if `hasNextPage` is true)
|
|
47
|
+
|
|
48
|
+
## Pagination Pattern
|
|
49
|
+
To get all rules across multiple pages:
|
|
50
|
+
1. Call with no cursor to get the first page
|
|
51
|
+
2. Check if `pageInfo.hasNextPage` is true
|
|
52
|
+
3. If true, call again with `cursor` set to `pageInfo.endCursor`
|
|
53
|
+
4. Repeat until `hasNextPage` is false
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
# JupiterOne Rule Update Tool - Complete Guide
|
|
2
|
+
|
|
3
|
+
**Purpose**: Updates existing inline question-based alert rules in JupiterOne. This tool modifies the configuration of an existing rule while preserving its identity and version history.
|
|
4
|
+
|
|
5
|
+
**Important**: Before updating a rule, use the `get-rule-details` tool to retrieve the current configuration. This ensures you have all required fields and can see what needs to be changed.
|
|
6
|
+
|
|
7
|
+
## Key Requirements for Updates
|
|
8
|
+
|
|
9
|
+
### 1. Required Fields for Updates
|
|
10
|
+
When updating a rule, you must provide **ALL** fields, not just the ones you want to change. The update operation replaces the entire rule configuration, so missing fields will result in errors.
|
|
11
|
+
|
|
12
|
+
**Critical Required Fields**:
|
|
13
|
+
- `id`: The existing rule ID (from `get-rule-details`)
|
|
14
|
+
- `version`: The current version number (from `get-rule-details`)
|
|
15
|
+
- `specVersion`: Usually 1
|
|
16
|
+
- `ignorePreviousResults`: Must be included
|
|
17
|
+
- `templates`: Must be included (use `{}` if empty)
|
|
18
|
+
- `tags`: Must be included but should always be empty `[]` (deprecated)
|
|
19
|
+
- `labels`: Use this for actual tagging functionality
|
|
20
|
+
- `resourceGroupId`: Must be included (can be null)
|
|
21
|
+
- `remediationSteps`: Must be included (can be null)
|
|
22
|
+
|
|
23
|
+
### 2. Condition Format (Critical)
|
|
24
|
+
The `condition` parameter must use JupiterOne's specific array format:
|
|
25
|
+
- **Structure**: `["LOGICAL_OPERATOR", [left_value, operator, right_value]]`
|
|
26
|
+
- **Example**: `["AND", ["queries.queryName.total", ">", 0]]`
|
|
27
|
+
- **Supported operators**: `>`, `<`, `>=`, `<=`, `=`, `!=`
|
|
28
|
+
- **Logical operators**: `"AND"`, `"OR"`
|
|
29
|
+
|
|
30
|
+
### 3. Operations Structure
|
|
31
|
+
The `when` clause should only contain:
|
|
32
|
+
- `type`: Always `"FILTER"`
|
|
33
|
+
- `condition`: The array format described above
|
|
34
|
+
- **Do NOT include**: `version`, `specVersion` (these belong at the rule level, not in the when clause)
|
|
35
|
+
|
|
36
|
+
### 4. Query Naming Convention
|
|
37
|
+
- Query names in the `queries` array must match the references in conditions
|
|
38
|
+
- Example: If query name is `"users"`, reference it as `"queries.users.total"`
|
|
39
|
+
- **IMPORTANT**: Use `"query0"` as the standard query name for compatibility with existing patterns
|
|
40
|
+
|
|
41
|
+
### 5. Version Management
|
|
42
|
+
- The `version` field will be automatically incremented by JupiterOne
|
|
43
|
+
- You must provide the current version number in your update request
|
|
44
|
+
- Get the current version using `get-rule-details` before updating
|
|
45
|
+
|
|
46
|
+
### 6. Tags vs Labels (Important)
|
|
47
|
+
- **DEPRECATED**: The `tags` array field is deprecated and should always be set to an empty array `[]`
|
|
48
|
+
- **USE INSTEAD**: For tagging functionality, use the `labels` field with key-value pairs
|
|
49
|
+
- **Format**: `labels: [{"labelName": "key", "labelValue": "value"}]`
|
|
50
|
+
- **When users ask for tagging**: Always use the `labels` field to meet their needs
|
|
51
|
+
- **Note**: The `tags` field is still required in the schema for compatibility but should remain empty
|
|
52
|
+
|
|
53
|
+
## Update Workflow
|
|
54
|
+
|
|
55
|
+
### Step 1: Get Current Rule Configuration
|
|
56
|
+
```
|
|
57
|
+
Use get-rule-details with the rule ID to get the current configuration
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Step 2: Modify Required Fields
|
|
61
|
+
Update only the fields you need to change while preserving all other required fields.
|
|
62
|
+
|
|
63
|
+
### Step 3: Submit Update
|
|
64
|
+
Use this tool with the complete configuration including your changes.
|
|
65
|
+
|
|
66
|
+
## Required Schema Fields for Updates
|
|
67
|
+
|
|
68
|
+
### Complete Required Parameters for update-inline-question-rule
|
|
69
|
+
**CRITICAL**: All of these fields must be included for successful rule updates:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"id": "existing-rule-id",
|
|
74
|
+
"name": "Updated Rule Name",
|
|
75
|
+
"description": "Updated rule description",
|
|
76
|
+
"notifyOnFailure": true,
|
|
77
|
+
"triggerActionsOnNewEntitiesOnly": true,
|
|
78
|
+
"ignorePreviousResults": false,
|
|
79
|
+
"pollingInterval": "ONE_DAY",
|
|
80
|
+
"specVersion": 1,
|
|
81
|
+
"version": 2,
|
|
82
|
+
"templates": {},
|
|
83
|
+
"outputs": ["alertLevel"],
|
|
84
|
+
"tags": [],
|
|
85
|
+
"labels": [
|
|
86
|
+
{"labelName": "environment", "labelValue": "production"},
|
|
87
|
+
{"labelName": "team", "labelValue": "security"}
|
|
88
|
+
],
|
|
89
|
+
"resourceGroupId": null,
|
|
90
|
+
"remediationSteps": null,
|
|
91
|
+
"question": {
|
|
92
|
+
"queries": [
|
|
93
|
+
{
|
|
94
|
+
"query": "FIND Entity...",
|
|
95
|
+
"name": "query0",
|
|
96
|
+
"version": "v1",
|
|
97
|
+
"includeDeleted": false
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
"operations": [
|
|
102
|
+
{
|
|
103
|
+
"when": {
|
|
104
|
+
"type": "FILTER",
|
|
105
|
+
"condition": ["AND", ["queries.query0.total", ">", 0]]
|
|
106
|
+
},
|
|
107
|
+
"actions": [...]
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Key Update Requirements**:
|
|
114
|
+
- `id`: Must match the existing rule ID
|
|
115
|
+
- `version`: Must be the current version number from the existing rule
|
|
116
|
+
- `ignorePreviousResults`: Must be included (typically `false`)
|
|
117
|
+
- `templates`: Must be included (use `{}` if empty)
|
|
118
|
+
- `tags`: Must be included but should always be empty `[]` (deprecated field)
|
|
119
|
+
- `labels`: Use this for actual tagging functionality with key-value pairs
|
|
120
|
+
- `resourceGroupId`: Must be included (can be null)
|
|
121
|
+
- `remediationSteps`: Must be included (can be null)
|
|
122
|
+
- Query `name`: Use `"query0"` for primary query
|
|
123
|
+
- Query `version`: Include `"v1"` for compatibility
|
|
124
|
+
- Query `includeDeleted`: Must be explicitly set to `false`
|
|
125
|
+
|
|
126
|
+
## Available Action Types
|
|
127
|
+
|
|
128
|
+
### 1. SET_PROPERTY
|
|
129
|
+
Sets a property value on the alert (commonly used for alert severity levels).
|
|
130
|
+
|
|
131
|
+
**Configuration**:
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"type": "SET_PROPERTY",
|
|
135
|
+
"targetProperty": "alertLevel",
|
|
136
|
+
"targetValue": "CRITICAL"
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Common Values for alertLevel**: `"LOW"`, `"MEDIUM"`, `"HIGH"`, `"CRITICAL"`, `"INFO"`
|
|
141
|
+
|
|
142
|
+
### 2. CREATE_ALERT
|
|
143
|
+
Creates a basic alert in JupiterOne.
|
|
144
|
+
|
|
145
|
+
**Configuration**:
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"type": "CREATE_ALERT"
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Note**: This is the most basic action and should almost always be included.
|
|
153
|
+
|
|
154
|
+
### 3. SEND_EMAIL
|
|
155
|
+
Sends email notifications to specified recipients.
|
|
156
|
+
|
|
157
|
+
**Configuration**:
|
|
158
|
+
```json
|
|
159
|
+
{
|
|
160
|
+
"type": "SEND_EMAIL",
|
|
161
|
+
"recipients": ["user1@company.com", "user2@company.com"],
|
|
162
|
+
"body": "Affected Items: <br><br>* {{queries.query0.data|mapProperty('displayName')|join('<br>* ')}}"
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### 4. TAG_ENTITIES
|
|
167
|
+
Adds or removes tags from entities that triggered the rule.
|
|
168
|
+
|
|
169
|
+
**Configuration**:
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"type": "TAG_ENTITIES",
|
|
173
|
+
"entities": "{{queries.query0.data}}",
|
|
174
|
+
"tags": [
|
|
175
|
+
{"name": "existing tag to remove", "value": null},
|
|
176
|
+
{"name": "new tag", "value": "tag value"}
|
|
177
|
+
]
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### 5. SEND_SLACK_MESSAGE
|
|
182
|
+
Sends messages to Slack channels (requires Slack integration).
|
|
183
|
+
|
|
184
|
+
**Configuration**:
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"integrationInstanceId": "d97d9127-c532-410a-bf0a-9ea93f66c3d2",
|
|
188
|
+
"type": "SEND_SLACK_MESSAGE",
|
|
189
|
+
"channels": ["#security-alerts", "#general"],
|
|
190
|
+
"body": "*Affected Items:* \n\n- {{queries.query0.data|mapProperty('displayName')|join('\n- ')}}"
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### 6. SEND_TO_S3
|
|
195
|
+
Sends alert data to an S3 bucket (requires AWS S3 integration).
|
|
196
|
+
|
|
197
|
+
**Configuration**:
|
|
198
|
+
```json
|
|
199
|
+
{
|
|
200
|
+
"integrationInstanceId": "f89568b4-2a1b-4bd8-8abd-aee21270df75",
|
|
201
|
+
"type": "SEND_TO_S3",
|
|
202
|
+
"bucket": "security-alerts-bucket",
|
|
203
|
+
"region": "us-east-1",
|
|
204
|
+
"data": {
|
|
205
|
+
"description": "{{alertWebLink}}\n\n**Affected Items:**\n\n* {{queries.query0.data|mapProperty('displayName')|join('\n* ')}}"
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### 7. CREATE_JIRA_TICKET
|
|
211
|
+
Creates a Jira ticket for the alert (requires Jira integration).
|
|
212
|
+
|
|
213
|
+
**Configuration**:
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
"integrationInstanceId": "53a99eaa-18a5-45ef-b748-2de39d642a91",
|
|
217
|
+
"type": "CREATE_JIRA_TICKET",
|
|
218
|
+
"entityClass": "Finding",
|
|
219
|
+
"summary": "Security Alert: Critical Unencrypted Data Found",
|
|
220
|
+
"issueType": "Bug",
|
|
221
|
+
"project": "SEC",
|
|
222
|
+
"updateContentOnChanges": false,
|
|
223
|
+
"additionalFields": {
|
|
224
|
+
"description": {
|
|
225
|
+
"type": "doc",
|
|
226
|
+
"version": 1,
|
|
227
|
+
"content": [
|
|
228
|
+
{
|
|
229
|
+
"type": "paragraph",
|
|
230
|
+
"content": [
|
|
231
|
+
{
|
|
232
|
+
"type": "text",
|
|
233
|
+
"text": "{{alertWebLink}}\n\n**Affected Items:**\n\n* {{queries.query0.data|mapProperty('displayName')|join('\n* ')}}"
|
|
234
|
+
}
|
|
235
|
+
]
|
|
236
|
+
}
|
|
237
|
+
]
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Template Variables and Formatting
|
|
244
|
+
|
|
245
|
+
### Available Variables
|
|
246
|
+
- `{{alertWebLink}}` - Direct link to the alert in JupiterOne
|
|
247
|
+
- `{{queries.queryName.data}}` - Array of entities from the specified query
|
|
248
|
+
- `{{queries.queryName.total}}` - Count of entities from the query
|
|
249
|
+
|
|
250
|
+
### Data Formatting
|
|
251
|
+
- `|mapProperty('fieldName')` - Extract specific field from each entity
|
|
252
|
+
- `|join('separator')` - Join array elements with specified separator
|
|
253
|
+
- Example: `{{queries.users.data|mapProperty('displayName')|join(', ')}}` - Creates comma-separated list of user names
|
|
254
|
+
|
|
255
|
+
## Integration Dependencies
|
|
256
|
+
|
|
257
|
+
For actions requiring integrations, you may need to:
|
|
258
|
+
1. Query available integration instances using `get-integration-instances`
|
|
259
|
+
2. Ask the user which integration to use
|
|
260
|
+
3. Use the integration's `id` as the `integrationInstanceId`
|
|
261
|
+
|
|
262
|
+
**Actions requiring integrations**:
|
|
263
|
+
- `SEND_SLACK_MESSAGE` (Slack integration)
|
|
264
|
+
- `SEND_TO_S3` (AWS S3 integration)
|
|
265
|
+
- `CREATE_JIRA_TICKET` (Jira integration)
|
|
266
|
+
|
|
267
|
+
## Working Example Update
|
|
268
|
+
|
|
269
|
+
### Complete Working Rule Update Structure
|
|
270
|
+
```json
|
|
271
|
+
{
|
|
272
|
+
"id": "12345678-1234-1234-1234-123456789abc",
|
|
273
|
+
"name": "Updated Rule Name",
|
|
274
|
+
"description": "Updated rule description",
|
|
275
|
+
"notifyOnFailure": true,
|
|
276
|
+
"triggerActionsOnNewEntitiesOnly": true,
|
|
277
|
+
"ignorePreviousResults": false,
|
|
278
|
+
"pollingInterval": "ONE_DAY",
|
|
279
|
+
"specVersion": 1,
|
|
280
|
+
"version": 3,
|
|
281
|
+
"templates": {},
|
|
282
|
+
"outputs": ["alertLevel"],
|
|
283
|
+
"tags": [],
|
|
284
|
+
"labels": [
|
|
285
|
+
{"labelName": "severity", "labelValue": "high"},
|
|
286
|
+
{"labelName": "category", "labelValue": "security"}
|
|
287
|
+
],
|
|
288
|
+
"resourceGroupId": null,
|
|
289
|
+
"remediationSteps": "1. Review the affected entities\n2. Apply security patches\n3. Update configurations",
|
|
290
|
+
"question": {
|
|
291
|
+
"queries": [
|
|
292
|
+
{
|
|
293
|
+
"query": "FIND Entity WITH condition",
|
|
294
|
+
"name": "query0",
|
|
295
|
+
"version": "v1",
|
|
296
|
+
"includeDeleted": false
|
|
297
|
+
}
|
|
298
|
+
]
|
|
299
|
+
},
|
|
300
|
+
"operations": [
|
|
301
|
+
{
|
|
302
|
+
"when": {
|
|
303
|
+
"type": "FILTER",
|
|
304
|
+
"condition": ["AND", ["queries.query0.total", ">", 0]]
|
|
305
|
+
},
|
|
306
|
+
"actions": [
|
|
307
|
+
{
|
|
308
|
+
"type": "SET_PROPERTY",
|
|
309
|
+
"targetProperty": "alertLevel",
|
|
310
|
+
"targetValue": "CRITICAL"
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
"type": "CREATE_ALERT"
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"type": "SEND_EMAIL",
|
|
317
|
+
"recipients": ["updated-user@company.com"],
|
|
318
|
+
"body": "Updated notification: {{alertWebLink}}"
|
|
319
|
+
}
|
|
320
|
+
]
|
|
321
|
+
}
|
|
322
|
+
]
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## Common Update Scenarios
|
|
327
|
+
|
|
328
|
+
### 1. Changing Notification Recipients
|
|
329
|
+
Update only the `recipients` array in the `SEND_EMAIL` action while preserving all other fields.
|
|
330
|
+
|
|
331
|
+
### 2. Modifying Polling Interval
|
|
332
|
+
Update the `pollingInterval` field while keeping all other configuration the same.
|
|
333
|
+
|
|
334
|
+
### 3. Adding New Actions
|
|
335
|
+
Add new actions to the `actions` array in the operations.
|
|
336
|
+
|
|
337
|
+
### 4. Updating Query Logic
|
|
338
|
+
Modify the `query` string in the queries array or adjust the `condition` in operations.
|
|
339
|
+
|
|
340
|
+
### 5. Changing Labels
|
|
341
|
+
Update the `labels` array to add, remove, or modify rule labels.
|
|
342
|
+
|
|
343
|
+
## Debugging Tips
|
|
344
|
+
- Always start by getting the current rule configuration with `get-rule-details`
|
|
345
|
+
- Ensure the `version` number matches the current rule version
|
|
346
|
+
- Include ALL required fields, even if they're not changing
|
|
347
|
+
- If you get "Invalid conjunction operator" errors, check the condition array format
|
|
348
|
+
- If you get "additional properties" errors, remove extra fields from the `when` clause
|
|
349
|
+
- If you get missing property errors, ensure all required schema fields are included
|
|
350
|
+
- **Always include**: `id`, `version`, `ignorePreviousResults`, `templates`, `tags`, `labels`, `resourceGroupId`, `remediationSteps`
|
|
351
|
+
- Use `"query0"` as the standard query name for compatibility
|
|
352
|
+
|
|
353
|
+
## Best Practices for Updates
|
|
354
|
+
- Always retrieve the current rule configuration first using `get-rule-details`
|
|
355
|
+
- Only modify the fields that actually need to change
|
|
356
|
+
- Preserve the existing `version` number (it will be auto-incremented)
|
|
357
|
+
- Use the `labels` field for rule organization and tagging (not the deprecated `tags` field)
|
|
358
|
+
- Test rule changes with simple modifications first
|
|
359
|
+
- Document changes in the `description` field if significant
|
|
360
|
+
- When users request tagging functionality, use the `labels` field with key-value pairs
|
|
361
|
+
- Always include `CREATE_ALERT` action as a baseline unless specifically removing it
|
|
362
|
+
|
|
363
|
+
This format ensures reliable rule updates and helps avoid common pitfalls encountered during rule modification.
|
|
@@ -2,8 +2,13 @@ import { JupiterOneConfig } from '../types/jupiterone.js';
|
|
|
2
2
|
export declare class JupiterOneMcpServer {
|
|
3
3
|
private server;
|
|
4
4
|
private client;
|
|
5
|
+
private validator;
|
|
5
6
|
constructor(config: JupiterOneConfig);
|
|
6
7
|
private setupTools;
|
|
8
|
+
private validateQueries;
|
|
9
|
+
private validateWidgetQueries;
|
|
10
|
+
private createValidationErrorResponse;
|
|
11
|
+
private createQueryErrorResponse;
|
|
7
12
|
start(): Promise<void>;
|
|
8
13
|
stop(): Promise<void>;
|
|
9
14
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../../src/server/mcp-server.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../../src/server/mcp-server.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAY1D,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,SAAS,CAAgB;gBAErB,MAAM,EAAE,gBAAgB;IAWpC,OAAO,CAAC,UAAU;YA4uDJ,eAAe;YAqBf,qBAAqB;IAOnC,OAAO,CAAC,6BAA6B;IAkBrC,OAAO,CAAC,wBAAwB;IAc1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAG5B"}
|
|
@@ -3,11 +3,14 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { JupiterOneClient } from '../client/jupiterone-client.js';
|
|
5
5
|
import { loadDescription } from '../utils/load-description.js';
|
|
6
|
+
import { J1QLValidator } from '../utils/j1ql-validator.js';
|
|
6
7
|
export class JupiterOneMcpServer {
|
|
7
8
|
server;
|
|
8
9
|
client;
|
|
10
|
+
validator;
|
|
9
11
|
constructor(config) {
|
|
10
12
|
this.client = new JupiterOneClient(config);
|
|
13
|
+
this.validator = new J1QLValidator(this.client.j1qlService);
|
|
11
14
|
this.server = new McpServer({
|
|
12
15
|
name: 'jupiterone-mcp',
|
|
13
16
|
version: '1.0.0',
|
|
@@ -18,18 +21,18 @@ export class JupiterOneMcpServer {
|
|
|
18
21
|
// Tool: List all rules
|
|
19
22
|
this.server.tool('list-rules', loadDescription('list-rules.md'), {
|
|
20
23
|
limit: z.number().min(1).max(1000).optional(),
|
|
21
|
-
|
|
24
|
+
cursor: z.string().optional(),
|
|
25
|
+
}, async ({ limit, cursor }) => {
|
|
22
26
|
try {
|
|
23
|
-
const
|
|
24
|
-
const
|
|
27
|
+
const response = await this.client.listRuleInstances(limit, cursor);
|
|
28
|
+
const instances = response.questionInstances || [];
|
|
25
29
|
return {
|
|
26
30
|
content: [
|
|
27
31
|
{
|
|
28
32
|
type: 'text',
|
|
29
33
|
text: JSON.stringify({
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
rules: limitedInstances.map((instance) => ({
|
|
34
|
+
returned: instances.length,
|
|
35
|
+
rules: instances.map((instance) => ({
|
|
33
36
|
id: instance.id,
|
|
34
37
|
name: instance.name,
|
|
35
38
|
description: instance.description,
|
|
@@ -43,6 +46,7 @@ export class JupiterOneMcpServer {
|
|
|
43
46
|
tags: instance.tags,
|
|
44
47
|
outputs: instance.outputs,
|
|
45
48
|
})),
|
|
49
|
+
pageInfo: response.pageInfo,
|
|
46
50
|
}, null, 2),
|
|
47
51
|
},
|
|
48
52
|
],
|
|
@@ -469,11 +473,12 @@ export class JupiterOneMcpServer {
|
|
|
469
473
|
}
|
|
470
474
|
});
|
|
471
475
|
// Tool: Get integration definitions
|
|
472
|
-
this.server.tool('get-integration-definitions', {
|
|
476
|
+
this.server.tool('get-integration-definitions', loadDescription('get-integration-definitions.md'), {
|
|
477
|
+
cursor: z.string().optional().describe('Optional cursor for pagination'),
|
|
473
478
|
includeConfig: z.boolean().optional().describe('Whether to include configuration fields'),
|
|
474
|
-
}, async ({ includeConfig }) => {
|
|
479
|
+
}, async ({ cursor, includeConfig }) => {
|
|
475
480
|
try {
|
|
476
|
-
const definitions = await this.client.getIntegrationDefinitions(
|
|
481
|
+
const definitions = await this.client.getIntegrationDefinitions(cursor, includeConfig);
|
|
477
482
|
return {
|
|
478
483
|
content: [
|
|
479
484
|
{
|
|
@@ -524,7 +529,7 @@ export class JupiterOneMcpServer {
|
|
|
524
529
|
}
|
|
525
530
|
});
|
|
526
531
|
// Tool: Get integration instances
|
|
527
|
-
this.server.tool('get-integration-instances', {
|
|
532
|
+
this.server.tool('get-integration-instances', loadDescription('get-integration-instances.md'), {
|
|
528
533
|
definitionId: z
|
|
529
534
|
.string()
|
|
530
535
|
.optional()
|
|
@@ -762,6 +767,11 @@ export class JupiterOneMcpServer {
|
|
|
762
767
|
.describe('Operations to perform when conditions are met'),
|
|
763
768
|
}, async ({ name, description, notifyOnFailure, triggerActionsOnNewEntitiesOnly, ignorePreviousResults, pollingInterval, outputs, specVersion, tags, templates, queries, operations, }) => {
|
|
764
769
|
try {
|
|
770
|
+
// Validate all queries before creating the rule
|
|
771
|
+
const validationResults = await this.validateQueries(queries);
|
|
772
|
+
if (validationResults.length > 0) {
|
|
773
|
+
return this.createValidationErrorResponse(validationResults);
|
|
774
|
+
}
|
|
765
775
|
const instance = {
|
|
766
776
|
name,
|
|
767
777
|
description,
|
|
@@ -820,7 +830,7 @@ export class JupiterOneMcpServer {
|
|
|
820
830
|
}
|
|
821
831
|
});
|
|
822
832
|
// Tool: Update inline question rule instance
|
|
823
|
-
this.server.tool('update-inline-question-rule', {
|
|
833
|
+
this.server.tool('update-inline-question-rule', loadDescription('update-inline-question-rule.md'), {
|
|
824
834
|
id: z.string().describe('ID of the rule to update'),
|
|
825
835
|
name: z.string().describe('Name of the rule'),
|
|
826
836
|
description: z.string().describe('Description of the rule'),
|
|
@@ -933,6 +943,11 @@ export class JupiterOneMcpServer {
|
|
|
933
943
|
.describe('Operations that define when and what actions to take'),
|
|
934
944
|
}, async ({ id, name, description, notifyOnFailure, triggerActionsOnNewEntitiesOnly, ignorePreviousResults, pollingInterval, outputs, specVersion, version, tags, templates, labels, resourceGroupId, remediationSteps, question, operations, }) => {
|
|
935
945
|
try {
|
|
946
|
+
// Validate all queries before updating the rule
|
|
947
|
+
const validationResults = await this.validateQueries(question?.queries);
|
|
948
|
+
if (validationResults.length > 0) {
|
|
949
|
+
return this.createValidationErrorResponse(validationResults);
|
|
950
|
+
}
|
|
936
951
|
const instance = {
|
|
937
952
|
id,
|
|
938
953
|
name,
|
|
@@ -1316,6 +1331,11 @@ export class JupiterOneMcpServer {
|
|
|
1316
1331
|
throw new Error('Input must be a valid object or JSON string');
|
|
1317
1332
|
}
|
|
1318
1333
|
}
|
|
1334
|
+
// Validate queries before creating widget
|
|
1335
|
+
const validationResults = await this.validateWidgetQueries(widgetInput.config?.queries);
|
|
1336
|
+
if (validationResults.length > 0) {
|
|
1337
|
+
return this.createValidationErrorResponse(validationResults);
|
|
1338
|
+
}
|
|
1319
1339
|
const widget = await this.client.createDashboardWidget(dashboardId, widgetInput);
|
|
1320
1340
|
return {
|
|
1321
1341
|
content: [
|
|
@@ -1473,18 +1493,58 @@ export class JupiterOneMcpServer {
|
|
|
1473
1493
|
};
|
|
1474
1494
|
}
|
|
1475
1495
|
catch (error) {
|
|
1476
|
-
return
|
|
1477
|
-
content: [
|
|
1478
|
-
{
|
|
1479
|
-
type: 'text',
|
|
1480
|
-
text: `Error executing J1QL query: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
1481
|
-
},
|
|
1482
|
-
],
|
|
1483
|
-
isError: true,
|
|
1484
|
-
};
|
|
1496
|
+
return this.createQueryErrorResponse(error, query);
|
|
1485
1497
|
}
|
|
1486
1498
|
});
|
|
1487
1499
|
}
|
|
1500
|
+
// Helper methods for validation
|
|
1501
|
+
async validateQueries(queries) {
|
|
1502
|
+
if (!queries || !Array.isArray(queries))
|
|
1503
|
+
return [];
|
|
1504
|
+
const validationResults = [];
|
|
1505
|
+
for (const queryObj of queries) {
|
|
1506
|
+
if (queryObj.query) {
|
|
1507
|
+
const validation = await this.validator.validateQuery(queryObj.query);
|
|
1508
|
+
if (!validation.isValid) {
|
|
1509
|
+
validationResults.push({
|
|
1510
|
+
queryName: queryObj.name || 'Unnamed query',
|
|
1511
|
+
error: validation.error || 'Query validation failed',
|
|
1512
|
+
suggestion: validation.suggestion || 'Please check the query syntax and try again',
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
return validationResults;
|
|
1518
|
+
}
|
|
1519
|
+
async validateWidgetQueries(queries) {
|
|
1520
|
+
// Use the same validation logic as rules - actually execute the queries
|
|
1521
|
+
return this.validateQueries(queries);
|
|
1522
|
+
}
|
|
1523
|
+
createValidationErrorResponse(validationResults) {
|
|
1524
|
+
return {
|
|
1525
|
+
content: [
|
|
1526
|
+
{
|
|
1527
|
+
type: 'text',
|
|
1528
|
+
text: `Query validation failed. Please fix the following issues:\n\n${validationResults
|
|
1529
|
+
.map((r) => `Query: ${r.queryName}\nError: ${r.error}\nSuggestion: ${r.suggestion}`)
|
|
1530
|
+
.join('\n\n')}\n\nUse the execute-j1ql-query tool to test and refine your queries first.`,
|
|
1531
|
+
},
|
|
1532
|
+
],
|
|
1533
|
+
isError: true,
|
|
1534
|
+
};
|
|
1535
|
+
}
|
|
1536
|
+
createQueryErrorResponse(error, query) {
|
|
1537
|
+
const errorResult = this.validator.handleQueryError(error, query);
|
|
1538
|
+
return {
|
|
1539
|
+
content: [
|
|
1540
|
+
{
|
|
1541
|
+
type: 'text',
|
|
1542
|
+
text: `Error executing J1QL query:\n\n${errorResult.error}\n\nSuggestion: ${errorResult.suggestion}\n\nDebugging tips:\n1. Modify existing queries that are already working as expected.\n2. Use discovery queries to understand available data\n3. Verify entity classes exist (use proper capitalization)\n4. Check property names match exactly\n5. Use single quotes for strings, not double quotes\n6. Place aliases after WITH statements\n7. Add LIMIT clause to prevent timeouts`,
|
|
1543
|
+
},
|
|
1544
|
+
],
|
|
1545
|
+
isError: true,
|
|
1546
|
+
};
|
|
1547
|
+
}
|
|
1488
1548
|
async start() {
|
|
1489
1549
|
const transport = new StdioServerTransport();
|
|
1490
1550
|
await this.server.connect(transport);
|