Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Add the following configuration to your Claude Desktop configuration file:
"env": {
"JUPITERONE_API_KEY": "your-api-key-here",
"JUPITERONE_ACCOUNT_ID": "your-account-id-here",
"JUPITERONE_BASE_URL": "https://graphql.us.jupiterone.io"
"JUPITERONE_GRAPHQL_URL": "https://graphql.us.jupiterone.io"
}
}
}
Expand All @@ -54,7 +54,7 @@ Then add this configuration to your Claude Desktop config file:
"env": {
"JUPITERONE_API_KEY": "your-api-key-here",
"JUPITERONE_ACCOUNT_ID": "your-account-id-here",
"JUPITERONE_BASE_URL": "https://graphql.us.jupiterone.io"
"JUPITERONE_GRAPHQL_URL": "https://graphql.us.jupiterone.io"
}
}
}
Expand All @@ -79,7 +79,7 @@ Replace the placeholder values with your actual JupiterOne credentials:

- **JUPITERONE_API_KEY**: Your JupiterOne API key (required)
- **JUPITERONE_ACCOUNT_ID**: Your JupiterOne account ID (required).
- **JUPITERONE_BASE_URL**: JupiterOne GraphQL endpoint (optional, defaults to `https://graphql.us.jupiterone.io`)
- **JUPITERONE_GRAPHQL_URL**: JupiterOne GraphQL endpoint (optional, defaults to `https://graphql.us.jupiterone.io`)

### Getting Your JupiterOne Credentials

Expand Down
2 changes: 1 addition & 1 deletion example.env
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# JupiterOne API Configuration
JUPITERONE_API_KEY=your-api-key
JUPITERONE_ACCOUNT_ID=j1dev
JUPITERONE_BASE_URL=https://graphql.dev.jupiterone.io
JUPITERONE_GRAPHQL_URL=https://graphql.dev.jupiterone.io

# Optional: Enable debug logging
# DEBUG=jupiterone-mcp*
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@jupiterone/jupiterone-mcp",
"version": "0.0.12",
"version": "0.0.13",
"description": "Model Context Protocol server for JupiterOne account rules and rule details",
"main": "dist/index.js",
"bin": {
Expand Down
177 changes: 177 additions & 0 deletions src/__tests__/schemas.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import { describe, expect, test } from '@jest/globals';
import {
CreateInsightsWidgetInputSchema,
RuleTemplatesSchema,
QueryVariablesSchema,
QueryFlagsSchema,
ActionTargetValueSchema,
ActionDataSchema,
JiraAdditionalFieldsSchema,
} from '../server/schemas';

describe('Schema Validation Tests', () => {
describe('CreateInsightsWidgetInputSchema', () => {
test('should validate a valid widget input', () => {
const validInput = {
title: 'Test Widget',
type: 'pie',
config: {
queries: [
{
query: 'FIND DataStore',
name: 'Query 1',
},
],
settings: {
pie: {
upwardTrendIsGood: true,
},
},
},
};

const result = CreateInsightsWidgetInputSchema.safeParse(validInput);
expect(result.success).toBe(true);
});

test('should reject invalid chart type', () => {
const invalidInput = {
title: 'Test Widget',
type: 'invalid-type',
config: {
queries: [],
},
};

const result = CreateInsightsWidgetInputSchema.safeParse(invalidInput);
expect(result.success).toBe(false);
});
});

describe('RuleTemplatesSchema', () => {
test('should validate various template types', () => {
const validTemplates = {
stringVar: 'test',
numberVar: 123,
boolVar: true,
arrayVar: [1, 2, 3],
objectVar: { key: 'value' },
};

const result = RuleTemplatesSchema.safeParse(validTemplates);
expect(result.success).toBe(true);
});
});

describe('QueryVariablesSchema', () => {
test('should validate query variables including null', () => {
const validVariables = {
name: 'John',
age: 30,
isActive: true,
optional: null,
tags: ['tag1', 'tag2'],
metadata: { key: 'value' },
};

const result = QueryVariablesSchema.safeParse(validVariables);
expect(result.success).toBe(true);
});
});

describe('QueryFlagsSchema', () => {
test('should validate query flags', () => {
const validFlags = {
includeDeleted: true,
deferredResponse: 'FORCE',
returnRowMetadata: false,
customFlag: 'custom-value', // passthrough allows this
};

const result = QueryFlagsSchema.safeParse(validFlags);
expect(result.success).toBe(true);
});

test('should reject invalid deferredResponse value', () => {
const invalidFlags = {
deferredResponse: 'INVALID',
};

const result = QueryFlagsSchema.safeParse(invalidFlags);
expect(result.success).toBe(false);
});
});

describe('ActionTargetValueSchema', () => {
test('should validate various target value types', () => {
const testCases = [
'string-value',
123,
true,
['array', 'values'],
{ key: 'object-value' },
];

testCases.forEach((value) => {
const result = ActionTargetValueSchema.safeParse(value);
expect(result.success).toBe(true);
});
});
});

describe('ActionDataSchema', () => {
test('should validate string data', () => {
const result = ActionDataSchema.safeParse('simple string');
expect(result.success).toBe(true);
});

test('should validate structured data object', () => {
const structuredData = {
description: 'Test description',
title: 'Test title',
content: { nested: 'data' },
};

const result = ActionDataSchema.safeParse(structuredData);
expect(result.success).toBe(true);
});
});

describe('JiraAdditionalFieldsSchema', () => {
test('should validate Jira fields with string description', () => {
const fields = {
description: 'Simple description',
priority: 'High',
labels: ['bug', 'urgent'],
components: ['backend'],
customField: 'custom-value',
};

const result = JiraAdditionalFieldsSchema.safeParse(fields);
expect(result.success).toBe(true);
});

test('should validate Jira fields with document description', () => {
const fields = {
description: {
type: 'doc',
version: 1,
content: [
{
type: 'paragraph',
content: [
{
type: 'text',
text: 'Formatted description',
},
],
},
],
},
};

const result = JiraAdditionalFieldsSchema.safeParse(fields);
expect(result.success).toBe(true);
});
});
});
24 changes: 20 additions & 4 deletions src/generated/description-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -839,8 +839,8 @@ Unified entities typically also have additional enrichment making them valuable

\`\`\`
FIND UnifiedIdentity AS identity
THAT IS << User
THAT RELATES TO AS rel (Device|Host)
THAT IS << User
THAT RELATES TO AS rel (Device|Host)
THAT IS >> UnifiedDevice AS device
RETURN identity.displayName, rel._class, device.displayName
\`\`\`
Expand Down Expand Up @@ -1141,7 +1141,7 @@ Before running any J1QL query, verify:
#### Most Common Errors (Quick Reference)

1. **Missing quotes**: \`name = john\` → \`name = 'john'\`
2. **Wrong quotes**: \`name = "john"\` → \`name = 'john'\`
2. **Wrong quotes**: \`name = "john"\` → \`name = 'john'\`
3. **Alias placement**: \`AS u WITH active = true\` → \`WITH active = true AS u\`
4. **WHERE needs alias**: \`WHERE active = true\` → \`AS u WHERE u.active = true\`
5. **Undefined alias**: \`FIND User RETURN u.name\` → \`FIND User AS u RETURN u.name\`
Expand Down Expand Up @@ -1177,7 +1177,23 @@ Before running any J1QL query, verify:
- LIMIT to prevent timeouts
- Proper capitalization for classes

**Remember**: The execute-j1ql-query tool now provides enhanced error messages with specific suggestions. Always test queries here first!`,
**Remember**: The execute-j1ql-query tool now provides enhanced error messages with specific suggestions. Always test queries here first!

#### 📌 IMPORTANT: Query Results URL

When this tool returns query results, it includes a \`url\` field that provides a direct link to view the results in the JupiterOne UI. **Always share this URL with users when presenting query results** - it allows them to:
- View the data in an interactive table format
- Export results to CSV or other formats
- Save the query for future use
- Share results with team members
- Further refine the query in the JupiterOne UI

Example response:
\`\`\`json
{
"data": [...query results...],
"url": "https://your-account.apps.us.jupiterone.io/home/results?search=..."
}`,
"get-dashboard-details.md": `# Get Dashboard Details Tool

Get detailed information about a specific JupiterOne dashboard including its widgets, layout, and configuration.
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ async function main() {
const config: JupiterOneConfig = {
apiKey: process.env.JUPITERONE_API_KEY || '',
accountId: process.env.JUPITERONE_ACCOUNT_ID || '',
baseUrl: process.env.JUPITERONE_BASE_URL || 'https://graphql.us.jupiterone.io',
baseUrl: process.env.JUPITERONE_GRAPHQL_URL || 'https://graphql.us.jupiterone.io',
oauthToken: process.env.JUPITERONE_OAUTH_TOKEN || '',
};

Expand Down
Loading