@kavinga/commerce-tools 0.1.3 → 0.1.7
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 +1 -1
- package/dist/mcps/jira/prompts/workflow.d.ts +2 -0
- package/dist/mcps/jira/prompts/workflow.d.ts.map +1 -0
- package/dist/mcps/jira/prompts/workflow.js +15 -0
- package/dist/mcps/jira/prompts/workflow.js.map +1 -0
- package/dist/mcps/jira/server.d.ts +3 -0
- package/dist/mcps/jira/server.d.ts.map +1 -0
- package/dist/mcps/jira/server.js +28 -0
- package/dist/mcps/jira/server.js.map +1 -0
- package/dist/mcps/jira/tools.d.ts +13 -0
- package/dist/mcps/jira/tools.d.ts.map +1 -0
- package/dist/mcps/jira/tools.js +124 -0
- package/dist/mcps/jira/tools.js.map +1 -0
- package/dist/mcps/jira/types.d.ts +47 -0
- package/dist/mcps/jira/types.d.ts.map +1 -0
- package/dist/mcps/jira/types.js +2 -0
- package/dist/mcps/jira/types.js.map +1 -0
- package/dist/mcps/newrelic/prompts/commerce.js +9 -9
- package/dist/mcps/newrelic/prompts/security.d.ts +10 -0
- package/dist/mcps/newrelic/prompts/security.d.ts.map +1 -1
- package/dist/mcps/newrelic/prompts/security.js +40 -9
- package/dist/mcps/newrelic/prompts/security.js.map +1 -1
- package/package.json +11 -4
package/README.md
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../../../mcps/jira/prompts/workflow.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kBAAkB,QAavB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const jiraWorkflowPrompt = `
|
|
2
|
+
Workflow for investigating a Jira ticket using New Relic, via the \`get_jira_issue\`, \`get_account_id_by_project_id\`, and \`execute_nrql\` MCP tools.
|
|
3
|
+
|
|
4
|
+
## Workflow
|
|
5
|
+
|
|
6
|
+
1. **Fetch the ticket.** Call \`get_jira_issue\` with the issue key (e.g. \`PRO-234\`).
|
|
7
|
+
2. **Check \`extractedIds\`** in the tool result.
|
|
8
|
+
- If \`accountId\` or \`projectId\` is present, use it.
|
|
9
|
+
- **If both are empty, do not guess.** Ask the user: "I couldn't find a New Relic account ID or project ID in this ticket — can you provide one?" Wait for their answer before continuing.
|
|
10
|
+
- Also skim \`descriptionText\`/\`commentsText\` yourself — the regex extraction is best-effort and may miss ids written in an unusual format; if you spot one it missed, use that instead (or confirm with the user).
|
|
11
|
+
3. **Resolve the New Relic account** using the \`newrelic-commerce\` skill's rules: if you have a project ID (not an account ID), call \`get_account_id_by_project_id\` first.
|
|
12
|
+
4. **Investigate with NRQL**, following the \`newrelic-commerce\` skill's mandatory field-discovery rules and entity → filter table (run \`SELECT * FROM <entity> ... LIMIT 1\` before using any field name; use \`execute_nrql\` for the actual investigation).
|
|
13
|
+
5. **Summarize findings**, tying them back to the ticket: reference the ticket key/summary, what you investigated, what you found, and any recommended next step. Do not modify the Jira ticket — posting comments or updating status is out of scope; only report back to the user.
|
|
14
|
+
`.trim();
|
|
15
|
+
//# sourceMappingURL=workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow.js","sourceRoot":"","sources":["../../../../mcps/jira/prompts/workflow.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;CAajC,CAAC,IAAI,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../mcps/jira/server.ts"],"names":[],"mappings":";AACA,OAAO,eAAe,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import 'dotenv/config';
|
|
3
|
+
import * as z from 'zod/v4';
|
|
4
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
5
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
6
|
+
import { ensureJiraAuth, runGetJiraIssue } from './tools.js';
|
|
7
|
+
const server = new McpServer({ name: 'jira', version: '0.1.0' }, { instructions: 'Fetch a Jira issue and surface any New Relic account/project id mentioned in its text.' });
|
|
8
|
+
server.registerTool('get_jira_issue', {
|
|
9
|
+
title: 'Get Jira Issue',
|
|
10
|
+
description: 'Fetch a Jira issue by key (e.g. PRO-234): summary, status, description, comments, and a best-effort extraction of a New Relic account/project id mentioned in the text. If extractedIds is empty, ask the user for the New Relic id before proceeding.',
|
|
11
|
+
inputSchema: z.object({
|
|
12
|
+
issue_key: z.string().min(1).describe('Jira issue key, e.g. PRO-234'),
|
|
13
|
+
}),
|
|
14
|
+
}, async ({ issue_key }) => {
|
|
15
|
+
const text = await runGetJiraIssue(issue_key);
|
|
16
|
+
return { content: [{ type: 'text', text }] };
|
|
17
|
+
});
|
|
18
|
+
async function main() {
|
|
19
|
+
ensureJiraAuth();
|
|
20
|
+
const transport = new StdioServerTransport();
|
|
21
|
+
await server.connect(transport);
|
|
22
|
+
console.error('Jira MCP server running on stdio');
|
|
23
|
+
}
|
|
24
|
+
main().catch((error) => {
|
|
25
|
+
console.error('Fatal error in main():', error);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
});
|
|
28
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../mcps/jira/server.ts"],"names":[],"mappings":";AACA,OAAO,eAAe,CAAC;AACvB,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7D,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,EAClC,EAAE,YAAY,EAAE,wFAAwF,EAAE,CAC3G,CAAC;AAEF,MAAM,CAAC,YAAY,CACjB,gBAAgB,EAChB;IACE,KAAK,EAAE,gBAAgB;IACvB,WAAW,EACT,wPAAwP;IAC1P,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QACpB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,8BAA8B,CAAC;KACtE,CAAC;CACH,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;IACtB,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,SAAS,CAAC,CAAC;IAC9C,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AAC/C,CAAC,CACF,CAAC;AAEF,KAAK,UAAU,IAAI;IACjB,cAAc,EAAE,CAAC;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;AACpD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;IAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ExtractedIds, JiraIssueResponse } from './types.js';
|
|
2
|
+
export declare function getJiraBaseUrl(): string;
|
|
3
|
+
export declare function getJiraEmail(): string;
|
|
4
|
+
export declare function getJiraApiToken(): string;
|
|
5
|
+
export declare function ensureJiraAuth(): void;
|
|
6
|
+
export declare function fetchJiraIssue(issueKey: string, timeoutSeconds?: number): Promise<JiraIssueResponse>;
|
|
7
|
+
export declare function stripHtml(html: string): string;
|
|
8
|
+
export declare function flattenComments(comments: Array<{
|
|
9
|
+
body: string;
|
|
10
|
+
}>): string;
|
|
11
|
+
export declare function extractIds(text: string): ExtractedIds;
|
|
12
|
+
export declare function runGetJiraIssue(issueKey: string): Promise<string>;
|
|
13
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../mcps/jira/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAgBlE,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wBAAgB,cAAc,IAAI,IAAI,CAYrC;AAOD,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,cAAc,GAAE,MAAgC,GAC/C,OAAO,CAAC,iBAAiB,CAAC,CA2C5B;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAW9C;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,CAGzE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAoBrD;AAED,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBvE"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
const DEFAULT_TIMEOUT_SECONDS = 30;
|
|
2
|
+
const ISSUE_FIELDS = 'summary,description,comment,status,assignee,reporter';
|
|
3
|
+
const ACCOUNT_ID_PATTERNS = [
|
|
4
|
+
/new relic account(?:\s*id)?[:\s]+(\d{5,})/i,
|
|
5
|
+
/account[\s_-]?id[:\s]*(\d{5,})/i,
|
|
6
|
+
/accountId=(\d+)/i,
|
|
7
|
+
];
|
|
8
|
+
const PROJECT_ID_PATTERNS = [
|
|
9
|
+
/new relic project(?:\s*id)?[:\s]+([\w-]{3,})/i,
|
|
10
|
+
/project[\s_-]?id[:\s]*([\w-]{3,})/i,
|
|
11
|
+
];
|
|
12
|
+
export function getJiraBaseUrl() {
|
|
13
|
+
return (process.env.JIRA_BASE_URL ?? '').trim().replace(/\/+$/, '');
|
|
14
|
+
}
|
|
15
|
+
export function getJiraEmail() {
|
|
16
|
+
return (process.env.JIRA_EMAIL ?? '').trim();
|
|
17
|
+
}
|
|
18
|
+
export function getJiraApiToken() {
|
|
19
|
+
return (process.env.JIRA_API_TOKEN ?? '').trim();
|
|
20
|
+
}
|
|
21
|
+
export function ensureJiraAuth() {
|
|
22
|
+
const missing = [
|
|
23
|
+
!getJiraBaseUrl() && 'JIRA_BASE_URL',
|
|
24
|
+
!getJiraEmail() && 'JIRA_EMAIL',
|
|
25
|
+
!getJiraApiToken() && 'JIRA_API_TOKEN',
|
|
26
|
+
].filter((v) => Boolean(v));
|
|
27
|
+
if (missing.length > 0) {
|
|
28
|
+
throw new Error(`${missing.join(', ')} not set. Set ${missing.length > 1 ? 'them' : 'it'} in the environment or .env before running the server.`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function buildAuthHeader() {
|
|
32
|
+
const credentials = `${getJiraEmail()}:${getJiraApiToken()}`;
|
|
33
|
+
return `Basic ${Buffer.from(credentials).toString('base64')}`;
|
|
34
|
+
}
|
|
35
|
+
export async function fetchJiraIssue(issueKey, timeoutSeconds = DEFAULT_TIMEOUT_SECONDS) {
|
|
36
|
+
const trimmedKey = issueKey.trim();
|
|
37
|
+
if (!trimmedKey)
|
|
38
|
+
throw new Error('issue_key is required and must be non-empty.');
|
|
39
|
+
const url = `${getJiraBaseUrl()}/rest/api/3/issue/${encodeURIComponent(trimmedKey)}?fields=${ISSUE_FIELDS}&expand=renderedFields`;
|
|
40
|
+
const controller = new AbortController();
|
|
41
|
+
const timer = setTimeout(() => controller.abort(), Math.max(1, timeoutSeconds) * 1000);
|
|
42
|
+
try {
|
|
43
|
+
const response = await fetch(url, {
|
|
44
|
+
method: 'GET',
|
|
45
|
+
headers: {
|
|
46
|
+
Authorization: buildAuthHeader(),
|
|
47
|
+
Accept: 'application/json',
|
|
48
|
+
},
|
|
49
|
+
signal: controller.signal,
|
|
50
|
+
});
|
|
51
|
+
if (response.status === 404) {
|
|
52
|
+
throw new Error(`Issue ${trimmedKey} not found (check the key and JIRA_BASE_URL).`);
|
|
53
|
+
}
|
|
54
|
+
if (response.status === 401 || response.status === 403) {
|
|
55
|
+
throw new Error(`Jira request failed (HTTP ${response.status}): check JIRA_EMAIL and JIRA_API_TOKEN.`);
|
|
56
|
+
}
|
|
57
|
+
const body = (await response.json());
|
|
58
|
+
if (response.status !== 200) {
|
|
59
|
+
const errorMessages = Array.isArray(body.errorMessages) ? body.errorMessages : [];
|
|
60
|
+
const errors = body.errors ?? {};
|
|
61
|
+
const messages = [...errorMessages, ...Object.entries(errors).map(([k, v]) => `${k}: ${v}`)];
|
|
62
|
+
throw new Error(`Jira request failed (HTTP ${response.status}): ${messages.length > 0 ? messages.join('; ') : JSON.stringify(body)}`);
|
|
63
|
+
}
|
|
64
|
+
return body;
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
clearTimeout(timer);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export function stripHtml(html) {
|
|
71
|
+
return html
|
|
72
|
+
.replace(/<[^>]*>/g, ' ')
|
|
73
|
+
.replace(/&/g, '&')
|
|
74
|
+
.replace(/</g, '<')
|
|
75
|
+
.replace(/>/g, '>')
|
|
76
|
+
.replace(/"/g, '"')
|
|
77
|
+
.replace(/'/g, "'")
|
|
78
|
+
.replace(/ /g, ' ')
|
|
79
|
+
.replace(/\s+/g, ' ')
|
|
80
|
+
.trim();
|
|
81
|
+
}
|
|
82
|
+
export function flattenComments(comments) {
|
|
83
|
+
if (comments.length === 0)
|
|
84
|
+
return '';
|
|
85
|
+
return comments.map((c) => stripHtml(c.body)).join('\n---\n');
|
|
86
|
+
}
|
|
87
|
+
export function extractIds(text) {
|
|
88
|
+
const result = {};
|
|
89
|
+
for (const pattern of ACCOUNT_ID_PATTERNS) {
|
|
90
|
+
const match = text.match(pattern);
|
|
91
|
+
if (match) {
|
|
92
|
+
result.accountId = match[1];
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
for (const pattern of PROJECT_ID_PATTERNS) {
|
|
97
|
+
const match = text.match(pattern);
|
|
98
|
+
if (match) {
|
|
99
|
+
result.projectId = match[1];
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
export async function runGetJiraIssue(issueKey) {
|
|
106
|
+
const issue = await fetchJiraIssue(issueKey);
|
|
107
|
+
const descriptionText = issue.renderedFields?.description
|
|
108
|
+
? stripHtml(issue.renderedFields.description)
|
|
109
|
+
: '(no description)';
|
|
110
|
+
const commentsText = issue.renderedFields?.comment?.comments
|
|
111
|
+
? flattenComments(issue.renderedFields.comment.comments) || '(no comments)'
|
|
112
|
+
: '(no comments)';
|
|
113
|
+
const extractedIds = extractIds(`${descriptionText}\n${commentsText}`);
|
|
114
|
+
return JSON.stringify({
|
|
115
|
+
key: issue.key,
|
|
116
|
+
url: `${getJiraBaseUrl()}/browse/${issue.key}`,
|
|
117
|
+
summary: issue.fields.summary,
|
|
118
|
+
status: issue.fields.status?.name ?? '',
|
|
119
|
+
descriptionText,
|
|
120
|
+
commentsText,
|
|
121
|
+
extractedIds,
|
|
122
|
+
}, null, 2);
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../../mcps/jira/tools.ts"],"names":[],"mappings":"AAEA,MAAM,uBAAuB,GAAG,EAAE,CAAC;AACnC,MAAM,YAAY,GAAG,sDAAsD,CAAC;AAE5E,MAAM,mBAAmB,GAAa;IACpC,4CAA4C;IAC5C,iCAAiC;IACjC,kBAAkB;CACnB,CAAC;AAEF,MAAM,mBAAmB,GAAa;IACpC,+CAA+C;IAC/C,oCAAoC;CACrC,CAAC;AAEF,MAAM,UAAU,cAAc;IAC5B,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,OAAO,GAAG;QACd,CAAC,cAAc,EAAE,IAAI,eAAe;QACpC,CAAC,YAAY,EAAE,IAAI,YAAY;QAC/B,CAAC,eAAe,EAAE,IAAI,gBAAgB;KACvC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAEzC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,wDAAwD,CACjI,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,eAAe;IACtB,MAAM,WAAW,GAAG,GAAG,YAAY,EAAE,IAAI,eAAe,EAAE,EAAE,CAAC;IAC7D,OAAO,SAAS,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;AAChE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,QAAgB,EAChB,iBAAyB,uBAAuB;IAEhD,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;IACnC,IAAI,CAAC,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAEjF,MAAM,GAAG,GAAG,GAAG,cAAc,EAAE,qBAAqB,kBAAkB,CAAC,UAAU,CAAC,WAAW,YAAY,wBAAwB,CAAC;IAElI,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,cAAc,CAAC,GAAG,IAAI,CAAC,CAAC;IAEvF,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,KAAK;YACb,OAAO,EAAE;gBACP,aAAa,EAAE,eAAe,EAAE;gBAChC,MAAM,EAAE,kBAAkB;aAC3B;YACD,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;QAEH,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,SAAS,UAAU,+CAA+C,CAAC,CAAC;QACtF,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvD,MAAM,IAAI,KAAK,CACb,6BAA6B,QAAQ,CAAC,MAAM,yCAAyC,CACtF,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA4B,CAAC;QAEhE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,MAAM,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAE,IAAI,CAAC,aAA0B,CAAC,CAAC,CAAC,EAAE,CAAC;YAChG,MAAM,MAAM,GAAI,IAAI,CAAC,MAA6C,IAAI,EAAE,CAAC;YACzE,MAAM,QAAQ,GAAG,CAAC,GAAG,aAAa,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;YAC7F,MAAM,IAAI,KAAK,CACb,6BAA6B,QAAQ,CAAC,MAAM,MAAM,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CACrH,CAAC;QACJ,CAAC;QAED,OAAO,IAAoC,CAAC;IAC9C,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,OAAO,IAAI;SACR,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC;SACxB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAAiC;IAC/D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACrC,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,MAAM,GAAiB,EAAE,CAAC;IAEhC,KAAK,MAAM,OAAO,IAAI,mBAAmB,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC5B,MAAM;QACR,CAAC;IACH,CAAC;IAED,KAAK,MAAM,OAAO,IAAI,mBAAmB,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC5B,MAAM;QACR,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAgB;IACpD,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,QAAQ,CAAC,CAAC;IAE7C,MAAM,eAAe,GAAG,KAAK,CAAC,cAAc,EAAE,WAAW;QACvD,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC;QAC7C,CAAC,CAAC,kBAAkB,CAAC;IACvB,MAAM,YAAY,GAAG,KAAK,CAAC,cAAc,EAAE,OAAO,EAAE,QAAQ;QAC1D,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,eAAe;QAC3E,CAAC,CAAC,eAAe,CAAC;IAEpB,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,eAAe,KAAK,YAAY,EAAE,CAAC,CAAC;IAEvE,OAAO,IAAI,CAAC,SAAS,CACnB;QACE,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,GAAG,EAAE,GAAG,cAAc,EAAE,WAAW,KAAK,CAAC,GAAG,EAAE;QAC9C,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO;QAC7B,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,IAAI,EAAE;QACvC,eAAe;QACf,YAAY;QACZ,YAAY;KACb,EACD,IAAI,EACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export interface JiraUser {
|
|
2
|
+
displayName: string;
|
|
3
|
+
emailAddress?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface JiraCommentRaw {
|
|
6
|
+
id: string;
|
|
7
|
+
}
|
|
8
|
+
export interface JiraIssueFields {
|
|
9
|
+
summary: string;
|
|
10
|
+
status: {
|
|
11
|
+
name: string;
|
|
12
|
+
};
|
|
13
|
+
assignee?: JiraUser | null;
|
|
14
|
+
reporter?: JiraUser | null;
|
|
15
|
+
comment?: {
|
|
16
|
+
comments: JiraCommentRaw[];
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export interface JiraRenderedComment {
|
|
20
|
+
id: string;
|
|
21
|
+
body: string;
|
|
22
|
+
}
|
|
23
|
+
export interface JiraIssueResponse {
|
|
24
|
+
id: string;
|
|
25
|
+
key: string;
|
|
26
|
+
fields: JiraIssueFields;
|
|
27
|
+
renderedFields?: {
|
|
28
|
+
description?: string;
|
|
29
|
+
comment?: {
|
|
30
|
+
comments: JiraRenderedComment[];
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export interface ExtractedIds {
|
|
35
|
+
accountId?: string;
|
|
36
|
+
projectId?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface JiraIssueSummary {
|
|
39
|
+
key: string;
|
|
40
|
+
url: string;
|
|
41
|
+
summary: string;
|
|
42
|
+
status: string;
|
|
43
|
+
descriptionText: string;
|
|
44
|
+
commentsText: string;
|
|
45
|
+
extractedIds: ExtractedIds;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../mcps/jira/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3B,OAAO,CAAC,EAAE;QAAE,QAAQ,EAAE,cAAc,EAAE,CAAA;KAAE,CAAC;CAC1C;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,eAAe,CAAC;IACxB,cAAc,CAAC,EAAE;QACf,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,OAAO,CAAC,EAAE;YAAE,QAAQ,EAAE,mBAAmB,EAAE,CAAA;SAAE,CAAC;KAC/C,CAAC;CACH;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,YAAY,CAAC;CAC5B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../mcps/jira/types.ts"],"names":[],"mappings":""}
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
export const commercePrompt = `
|
|
2
|
-
|
|
2
|
+
Reference for querying New Relic with NRQL for Adobe Commerce projects, using the New Relic MCP tools \`get_account_id_by_project_id\` and \`execute_nrql\`.
|
|
3
3
|
|
|
4
|
-
**Critical:** Do not use any attribute name in NRQL unless it was returned by a \`SELECT * FROM <entity> ... LIMIT 1\` query
|
|
4
|
+
**Critical:** Do not use any attribute name in NRQL unless it was returned by a \`SELECT * FROM <entity> ... LIMIT 1\` query already run for that entity. Run that discovery query first; then use only the keys from the result. Never guess or invent field names.
|
|
5
5
|
|
|
6
|
-
##
|
|
6
|
+
## Workflow
|
|
7
7
|
|
|
8
|
-
1. **
|
|
8
|
+
1. **Resolve the account.** If an Adobe Commerce project ID is given (and no account ID):
|
|
9
9
|
- Call the New Relic MCP tool \`get_account_id_by_project_id\` with that project ID.
|
|
10
10
|
- Use the returned \`accountId\` from the first matching entity for all NRQL execution.
|
|
11
|
-
- If no entities are found,
|
|
11
|
+
- If no entities are found, report it and suggest checking the project ID or NEW_RELIC_API_KEY.
|
|
12
12
|
|
|
13
|
-
2. **
|
|
14
|
-
-
|
|
13
|
+
2. **Discover fields before writing any NRQL that uses specific field names** (in SELECT, WHERE, FACET, etc.):
|
|
14
|
+
- First run \`SELECT * FROM <entity> ... LIMIT 1\` (with the correct WHERE for that entity and a SINCE clause).
|
|
15
15
|
- Inspect the JSON result and note the **exact attribute names** (keys) returned.
|
|
16
16
|
- **Only use those attribute names** in subsequent queries. Do not assume, guess, or invent field names.
|
|
17
17
|
|
|
18
|
-
3. **Build NRQL** from the
|
|
18
|
+
3. **Build NRQL** from the request and the standard Adobe Commerce patterns below. Use the project ID in filters as shown. For any non-\`SELECT *\` query, use only fields obtained in step 2.
|
|
19
19
|
|
|
20
|
-
4. **Execute NRQL** with the MCP tool \`execute_nrql\`, passing the \`account_id\` from step 1 (or the
|
|
20
|
+
4. **Execute NRQL** with the MCP tool \`execute_nrql\`, passing the \`account_id\` from step 1 (or the account ID given directly). Use a sensible \`timeout_seconds\` (e.g. 30) for large result sets.
|
|
21
21
|
|
|
22
22
|
5. **Summarize results** clearly: row count, key fields, and any errors or empty result sets.
|
|
23
23
|
|
|
@@ -1,2 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-contained MCP prompt: embeds the full commerce reference because MCP
|
|
3
|
+
* prompt consumers cannot load the newrelic-commerce skill.
|
|
4
|
+
*/
|
|
1
5
|
export declare const securityPrompt: string;
|
|
6
|
+
/**
|
|
7
|
+
* Subagent system prompt for the Claude Code / Cursor plugins: persona plus
|
|
8
|
+
* the security workflow, deferring the full entity/filter reference to the
|
|
9
|
+
* newrelic-commerce skill instead of duplicating it.
|
|
10
|
+
*/
|
|
11
|
+
export declare const securityAgentPrompt: string;
|
|
2
12
|
//# sourceMappingURL=security.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../../../mcps/newrelic/prompts/security.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../../../mcps/newrelic/prompts/security.ts"],"names":[],"mappings":"AAmEA;;;GAGG;AACH,eAAO,MAAM,cAAc,QAQJ,CAAC;AAExB;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,QAaT,CAAC"}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { commercePrompt } from './commerce.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Security-analysis knowledge shared by the MCP prompt and the plugin agent.
|
|
4
|
+
* Keep this self-contained apart from the entity filters / field-discovery
|
|
5
|
+
* rules, which the consumers supply (commercePrompt or the newrelic-commerce
|
|
6
|
+
* skill).
|
|
7
|
+
*/
|
|
8
|
+
const securityCore = `
|
|
10
9
|
### Attack categories to investigate
|
|
11
10
|
|
|
12
11
|
| Category | Primary entities | Key signals |
|
|
@@ -23,7 +22,7 @@ You are also a security analyst. In addition to the general NRQL and Adobe Comme
|
|
|
23
22
|
|
|
24
23
|
### Investigation workflow
|
|
25
24
|
|
|
26
|
-
1. **Resolve account** —
|
|
25
|
+
1. **Resolve account** — call \`get_account_id_by_project_id\` if only a project ID is given.
|
|
27
26
|
|
|
28
27
|
2. **Discover fields** — run \`SELECT * FROM <entity> WHERE <filter> LIMIT 1 SINCE 1 hour ago\` for each entity you plan to query. Only use field names from those results.
|
|
29
28
|
|
|
@@ -64,4 +63,36 @@ You are also a security analyst. In addition to the general NRQL and Adobe Comme
|
|
|
64
63
|
- When evidence is ambiguous, state it clearly and suggest additional queries rather than asserting an attack occurred.
|
|
65
64
|
- If the user asks to block an IP or apply a WAF rule, note that those actions must be taken outside New Relic (e.g. in the Fastly or CDN console) and are not performed by this tool.
|
|
66
65
|
`.trim();
|
|
66
|
+
/**
|
|
67
|
+
* Self-contained MCP prompt: embeds the full commerce reference because MCP
|
|
68
|
+
* prompt consumers cannot load the newrelic-commerce skill.
|
|
69
|
+
*/
|
|
70
|
+
export const securityPrompt = `${commercePrompt}
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Security & Attack Analysis
|
|
75
|
+
|
|
76
|
+
In addition to the general NRQL and Adobe Commerce guidance above, detect and investigate attacks against Adobe Commerce projects using New Relic data.
|
|
77
|
+
|
|
78
|
+
${securityCore}`.trim();
|
|
79
|
+
/**
|
|
80
|
+
* Subagent system prompt for the Claude Code / Cursor plugins: persona plus
|
|
81
|
+
* the security workflow, deferring the full entity/filter reference to the
|
|
82
|
+
* newrelic-commerce skill instead of duplicating it.
|
|
83
|
+
*/
|
|
84
|
+
export const securityAgentPrompt = `
|
|
85
|
+
You are a security analyst for Adobe Commerce projects on New Relic. You detect and investigate attacks by running NRQL queries with the New Relic MCP tools \`get_account_id_by_project_id\` and \`execute_nrql\`.
|
|
86
|
+
|
|
87
|
+
For the full entity → filter table and field-discovery rules, use the \`newrelic-commerce\` skill. Essentials:
|
|
88
|
+
|
|
89
|
+
- **Log and infrastructure samples** (ProcessSample, SystemSample, Mysql/Redis/Elasticsearch/Rabbitmq samples): \`apmApplicationNames = '|<project id>|'\` (literal pipe characters around the project ID).
|
|
90
|
+
- **Fastly (CDN) logs:** \`cache_status IS NOT NULL AND project_id = '<project id>'\`.
|
|
91
|
+
- **APM events** (Transaction, TransactionError, PageView, ErrorTrace): \`appName = '<project id>'\` (no pipes).
|
|
92
|
+
- **Never use a field name** that was not returned by a \`SELECT * FROM <entity> ... LIMIT 1\` discovery query you already ran for that entity.
|
|
93
|
+
- Always add a \`SINCE ...\` clause when querying Log data.
|
|
94
|
+
|
|
95
|
+
## Security & Attack Analysis
|
|
96
|
+
|
|
97
|
+
${securityCore}`.trim();
|
|
67
98
|
//# sourceMappingURL=security.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../../../mcps/newrelic/prompts/security.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,cAAc
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../../../mcps/newrelic/prompts/security.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyDpB,CAAC,IAAI,EAAE,CAAC;AAET;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,cAAc;;;;;;;;EAQ7C,YAAY,EAAE,CAAC,IAAI,EAAE,CAAC;AAExB;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;EAajC,YAAY,EAAE,CAAC,IAAI,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kavinga/commerce-tools",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Multi-provider AI tooling platform with MCPs, agents, skills, and commands",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"newrelic-mcp": "./dist/mcps/newrelic/server.js"
|
|
7
|
+
"newrelic-mcp": "./dist/mcps/newrelic/server.js",
|
|
8
|
+
"jira-mcp": "./dist/mcps/jira/server.js"
|
|
8
9
|
},
|
|
9
10
|
"files": [
|
|
10
11
|
"dist/"
|
|
11
12
|
],
|
|
12
13
|
"scripts": {
|
|
13
|
-
"build": "tsc",
|
|
14
|
+
"build": "tsc && npm run build:plugins",
|
|
15
|
+
"build:plugins": "tsx plugins/build.ts",
|
|
14
16
|
"dev:newrelic": "tsx mcps/newrelic/server.ts",
|
|
15
|
-
"start:newrelic": "node dist/mcps/newrelic/server.js"
|
|
17
|
+
"start:newrelic": "node dist/mcps/newrelic/server.js",
|
|
18
|
+
"dev:jira": "tsx mcps/jira/server.ts",
|
|
19
|
+
"start:jira": "node dist/mcps/jira/server.js"
|
|
16
20
|
},
|
|
17
21
|
"dependencies": {
|
|
18
22
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
@@ -26,5 +30,8 @@
|
|
|
26
30
|
},
|
|
27
31
|
"engines": {
|
|
28
32
|
"node": ">=18"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
29
36
|
}
|
|
30
37
|
}
|