@securityreviewai/security-review-mcp 0.2.9
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 +264 -0
- package/dist/api/client.js +345 -0
- package/dist/bin/security-review-mcp.js +172 -0
- package/dist/index.js +1 -0
- package/dist/integrations/confluenceClient.js +161 -0
- package/dist/integrations/jiraClient.js +133 -0
- package/dist/prompts/analysisPrompts.js +496 -0
- package/dist/prompts/generationPrompts.js +229 -0
- package/dist/resources/reviewResources.js +101 -0
- package/dist/server.js +38 -0
- package/dist/tools/common.js +14 -0
- package/dist/tools/documentTools.js +140 -0
- package/dist/tools/integrationTools.js +100 -0
- package/dist/tools/projectTools.js +104 -0
- package/dist/tools/reviewArtifactTools.js +61 -0
- package/dist/tools/reviewTools.js +405 -0
- package/dist/tools/workflowTools.js +105 -0
- package/dist/utils/env.js +46 -0
- package/dist/utils/serialization.js +27 -0
- package/package.json +36 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import * as z from "zod/v4";
|
|
2
|
+
export function registerGenerationPrompts(server) {
|
|
3
|
+
server.registerPrompt("document_generation", {
|
|
4
|
+
description: "Generate a detailed architecture/design document from minimal user input. The generated document is suitable for uploading to SRAI as a knowledge base document for security review. Use when the user wants to create an SRAI project from a brief description.",
|
|
5
|
+
argsSchema: {
|
|
6
|
+
system_name: z.string(),
|
|
7
|
+
brief_description: z.string(),
|
|
8
|
+
doc_type: z.string().default("architecture"),
|
|
9
|
+
},
|
|
10
|
+
}, async ({ system_name, brief_description, doc_type }) => {
|
|
11
|
+
const normalized = doc_type.toLowerCase();
|
|
12
|
+
if (normalized === "api") {
|
|
13
|
+
return promptText(apiTemplate(system_name, brief_description));
|
|
14
|
+
}
|
|
15
|
+
if (normalized === "deployment") {
|
|
16
|
+
return promptText(deploymentTemplate(system_name, brief_description));
|
|
17
|
+
}
|
|
18
|
+
return promptText(architectureTemplate(system_name, brief_description));
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function architectureTemplate(systemName, briefDescription) {
|
|
22
|
+
return `You are a senior software architect. Generate a comprehensive architecture document for the system described below. The document should be detailed enough for a security team to perform a thorough threat model analysis.
|
|
23
|
+
|
|
24
|
+
## System: ${systemName}
|
|
25
|
+
|
|
26
|
+
## Brief Description
|
|
27
|
+
${briefDescription}
|
|
28
|
+
|
|
29
|
+
## INSTRUCTIONS
|
|
30
|
+
|
|
31
|
+
Generate a detailed **Architecture Design Document** that includes ALL of the following sections. Be thorough and specific -- infer reasonable architectural decisions from the brief description.
|
|
32
|
+
|
|
33
|
+
### Document Structure
|
|
34
|
+
|
|
35
|
+
**1. Executive Summary**
|
|
36
|
+
- System purpose and business context
|
|
37
|
+
- Key stakeholders
|
|
38
|
+
- High-level architecture style (monolith, microservices, serverless, etc.)
|
|
39
|
+
|
|
40
|
+
**2. System Overview**
|
|
41
|
+
- Detailed functional description
|
|
42
|
+
- Key business capabilities
|
|
43
|
+
- User types and their roles
|
|
44
|
+
- System boundaries
|
|
45
|
+
|
|
46
|
+
**3. Architecture Components**
|
|
47
|
+
For each component, describe:
|
|
48
|
+
- Name and purpose
|
|
49
|
+
- Technology stack (language, framework, database, etc.)
|
|
50
|
+
- APIs exposed (endpoints, protocols)
|
|
51
|
+
- Data stored or processed
|
|
52
|
+
- Authentication/authorization mechanisms
|
|
53
|
+
- Scaling characteristics
|
|
54
|
+
|
|
55
|
+
**4. Data Architecture**
|
|
56
|
+
- Data models (key entities and relationships)
|
|
57
|
+
- Data stores (databases, caches, file storage)
|
|
58
|
+
- Data classification (what is sensitive, PII, financial, etc.)
|
|
59
|
+
- Data flow between components
|
|
60
|
+
- Data retention and lifecycle
|
|
61
|
+
|
|
62
|
+
**5. Integration Architecture**
|
|
63
|
+
- External services and APIs consumed
|
|
64
|
+
- Third-party integrations
|
|
65
|
+
- Message queues or event buses
|
|
66
|
+
- Webhooks and callbacks
|
|
67
|
+
|
|
68
|
+
**6. Infrastructure & Deployment**
|
|
69
|
+
- Cloud provider and services used
|
|
70
|
+
- Network topology (VPCs, subnets, security groups)
|
|
71
|
+
- Container orchestration (if applicable)
|
|
72
|
+
- CI/CD pipeline
|
|
73
|
+
- Environment strategy (dev, staging, production)
|
|
74
|
+
|
|
75
|
+
**7. Security Architecture**
|
|
76
|
+
- Authentication mechanism (OAuth2, JWT, SAML, etc.)
|
|
77
|
+
- Authorization model (RBAC, ABAC, etc.)
|
|
78
|
+
- Encryption at rest and in transit
|
|
79
|
+
- Secret management approach
|
|
80
|
+
- Logging and monitoring
|
|
81
|
+
- Network security controls
|
|
82
|
+
|
|
83
|
+
**8. Non-Functional Requirements**
|
|
84
|
+
- Performance targets
|
|
85
|
+
- Availability/SLA requirements
|
|
86
|
+
- Compliance requirements
|
|
87
|
+
- Scalability approach
|
|
88
|
+
|
|
89
|
+
Format the document with clear markdown headers, bullet points, and tables where appropriate. Make it comprehensive but readable. This document will be used for security threat modeling, so err on the side of more detail about data flows, trust boundaries, and security-relevant decisions.`;
|
|
90
|
+
}
|
|
91
|
+
function apiTemplate(systemName, briefDescription) {
|
|
92
|
+
return `You are a senior API architect. Generate a comprehensive API specification document for the system described below. The document should be detailed enough for a security team to assess the API attack surface.
|
|
93
|
+
|
|
94
|
+
## System: ${systemName}
|
|
95
|
+
|
|
96
|
+
## Brief Description
|
|
97
|
+
${briefDescription}
|
|
98
|
+
|
|
99
|
+
## INSTRUCTIONS
|
|
100
|
+
|
|
101
|
+
Generate a detailed **API Design Document** that includes ALL of the following sections:
|
|
102
|
+
|
|
103
|
+
**1. API Overview**
|
|
104
|
+
- Base URL structure
|
|
105
|
+
- API versioning strategy
|
|
106
|
+
- Authentication mechanism
|
|
107
|
+
- Rate limiting policy
|
|
108
|
+
|
|
109
|
+
**2. Authentication & Authorization**
|
|
110
|
+
- Auth flows (OAuth2, API keys, JWT, etc.)
|
|
111
|
+
- Token lifecycle (issuance, refresh, revocation)
|
|
112
|
+
- Permission model
|
|
113
|
+
- Scopes or roles
|
|
114
|
+
|
|
115
|
+
**3. API Endpoints**
|
|
116
|
+
For each major endpoint group, document:
|
|
117
|
+
- HTTP method and path
|
|
118
|
+
- Request parameters (path, query, body)
|
|
119
|
+
- Request/response schemas
|
|
120
|
+
- Authentication requirements
|
|
121
|
+
- Rate limits
|
|
122
|
+
- Error responses
|
|
123
|
+
|
|
124
|
+
**4. Data Models**
|
|
125
|
+
- Key request/response objects
|
|
126
|
+
- Sensitive fields and their handling
|
|
127
|
+
- Validation rules
|
|
128
|
+
|
|
129
|
+
**5. Security Controls**
|
|
130
|
+
- Input validation approach
|
|
131
|
+
- Output encoding
|
|
132
|
+
- CORS policy
|
|
133
|
+
- Security headers
|
|
134
|
+
- Request size limits
|
|
135
|
+
- File upload handling
|
|
136
|
+
|
|
137
|
+
**6. Error Handling**
|
|
138
|
+
- Error response format
|
|
139
|
+
- Error codes and their meanings
|
|
140
|
+
- Information leakage prevention
|
|
141
|
+
|
|
142
|
+
**7. Integration Patterns**
|
|
143
|
+
- Webhooks and callbacks
|
|
144
|
+
- Pagination strategy
|
|
145
|
+
- Filtering and sorting
|
|
146
|
+
- Batch operations
|
|
147
|
+
|
|
148
|
+
Format as a detailed technical document suitable for security threat modeling.`;
|
|
149
|
+
}
|
|
150
|
+
function deploymentTemplate(systemName, briefDescription) {
|
|
151
|
+
return `You are a senior DevOps/platform engineer. Generate a comprehensive deployment and infrastructure document for the system described below. The document should be detailed enough for a security team to assess the infrastructure attack surface.
|
|
152
|
+
|
|
153
|
+
## System: ${systemName}
|
|
154
|
+
|
|
155
|
+
## Brief Description
|
|
156
|
+
${briefDescription}
|
|
157
|
+
|
|
158
|
+
## INSTRUCTIONS
|
|
159
|
+
|
|
160
|
+
Generate a detailed **Deployment & Infrastructure Document** including:
|
|
161
|
+
|
|
162
|
+
**1. Infrastructure Overview**
|
|
163
|
+
- Cloud provider(s) and regions
|
|
164
|
+
- Architecture style (containers, VMs, serverless)
|
|
165
|
+
- High availability design
|
|
166
|
+
|
|
167
|
+
**2. Network Architecture**
|
|
168
|
+
- VPC/network design
|
|
169
|
+
- Subnet layout (public, private, data)
|
|
170
|
+
- Load balancers and CDN
|
|
171
|
+
- DNS configuration
|
|
172
|
+
- Firewall rules and security groups
|
|
173
|
+
- Network segmentation
|
|
174
|
+
|
|
175
|
+
**3. Compute Resources**
|
|
176
|
+
- Application servers / containers
|
|
177
|
+
- Container orchestration (Kubernetes, ECS, etc.)
|
|
178
|
+
- Auto-scaling policies
|
|
179
|
+
- Resource limits
|
|
180
|
+
|
|
181
|
+
**4. Data Infrastructure**
|
|
182
|
+
- Database servers and configuration
|
|
183
|
+
- Caching layer
|
|
184
|
+
- Object storage
|
|
185
|
+
- Backup and recovery strategy
|
|
186
|
+
|
|
187
|
+
**5. CI/CD Pipeline**
|
|
188
|
+
- Source code management
|
|
189
|
+
- Build process
|
|
190
|
+
- Testing stages
|
|
191
|
+
- Deployment strategy (blue-green, canary, rolling)
|
|
192
|
+
- Artifact management
|
|
193
|
+
|
|
194
|
+
**6. Security Infrastructure**
|
|
195
|
+
- Identity and access management
|
|
196
|
+
- Secret management (Vault, AWS Secrets Manager, etc.)
|
|
197
|
+
- Certificate management
|
|
198
|
+
- WAF and DDoS protection
|
|
199
|
+
- Vulnerability scanning
|
|
200
|
+
|
|
201
|
+
**7. Observability**
|
|
202
|
+
- Logging infrastructure
|
|
203
|
+
- Metrics collection
|
|
204
|
+
- Alerting rules
|
|
205
|
+
- Distributed tracing
|
|
206
|
+
- Incident response procedures
|
|
207
|
+
|
|
208
|
+
**8. Compliance**
|
|
209
|
+
- Encryption at rest configuration
|
|
210
|
+
- Encryption in transit configuration
|
|
211
|
+
- Audit logging
|
|
212
|
+
- Data residency
|
|
213
|
+
- Compliance certifications
|
|
214
|
+
|
|
215
|
+
Format as a detailed technical document suitable for security threat modeling.`;
|
|
216
|
+
}
|
|
217
|
+
function promptText(text) {
|
|
218
|
+
return {
|
|
219
|
+
messages: [
|
|
220
|
+
{
|
|
221
|
+
role: "user",
|
|
222
|
+
content: {
|
|
223
|
+
type: "text",
|
|
224
|
+
text,
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
};
|
|
229
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { getApiClient, SraiApiError } from "../api/client.js";
|
|
3
|
+
import { jsonStringify } from "../utils/serialization.js";
|
|
4
|
+
export function registerReviewResources(server) {
|
|
5
|
+
server.registerResource("SRAI Projects", "srai://projects", {
|
|
6
|
+
description: "List of all projects in the SRAI platform.",
|
|
7
|
+
mimeType: "application/json",
|
|
8
|
+
}, async (uri) => {
|
|
9
|
+
try {
|
|
10
|
+
const result = await getApiClient().listProjects();
|
|
11
|
+
return {
|
|
12
|
+
contents: [
|
|
13
|
+
{
|
|
14
|
+
uri: uri.href,
|
|
15
|
+
text: jsonStringify(result),
|
|
16
|
+
mimeType: "application/json",
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
return resourceError(uri, error);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
server.registerResource("Project Reviews", new ResourceTemplate("srai://projects/{project_id}/reviews", { list: undefined }), {
|
|
26
|
+
description: "List of all reviews for a specific SRAI project.",
|
|
27
|
+
mimeType: "application/json",
|
|
28
|
+
}, async (uri, variables) => {
|
|
29
|
+
try {
|
|
30
|
+
const projectId = Number(variables.project_id);
|
|
31
|
+
const result = await getApiClient().listReviews(projectId);
|
|
32
|
+
return {
|
|
33
|
+
contents: [
|
|
34
|
+
{
|
|
35
|
+
uri: uri.href,
|
|
36
|
+
text: jsonStringify(result),
|
|
37
|
+
mimeType: "application/json",
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
return resourceError(uri, error);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
server.registerResource("Review Summary", new ResourceTemplate("srai://projects/{project_id}/reviews/{review_id}/summary", { list: undefined }), {
|
|
47
|
+
description: "Summary of a specific security review including status, artifact counts, and workflow progress.",
|
|
48
|
+
mimeType: "application/json",
|
|
49
|
+
}, async (uri, variables) => {
|
|
50
|
+
try {
|
|
51
|
+
const projectId = Number(variables.project_id);
|
|
52
|
+
const reviewId = Number(variables.review_id);
|
|
53
|
+
const review = await getApiClient().getReview(projectId, reviewId);
|
|
54
|
+
return {
|
|
55
|
+
contents: [
|
|
56
|
+
{
|
|
57
|
+
uri: uri.href,
|
|
58
|
+
text: jsonStringify(review),
|
|
59
|
+
mimeType: "application/json",
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
return resourceError(uri, error);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
server.registerResource("Project Documents", new ResourceTemplate("srai://projects/{project_id}/documents", { list: undefined }), {
|
|
69
|
+
description: "List of all documents in a specific SRAI project.",
|
|
70
|
+
mimeType: "application/json",
|
|
71
|
+
}, async (uri, variables) => {
|
|
72
|
+
try {
|
|
73
|
+
const projectId = Number(variables.project_id);
|
|
74
|
+
const result = await getApiClient().listDocuments(projectId);
|
|
75
|
+
return {
|
|
76
|
+
contents: [
|
|
77
|
+
{
|
|
78
|
+
uri: uri.href,
|
|
79
|
+
text: jsonStringify(result),
|
|
80
|
+
mimeType: "application/json",
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
return resourceError(uri, error);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
function resourceError(uri, error) {
|
|
91
|
+
const message = error instanceof SraiApiError ? error.detail : error instanceof Error ? error.message : String(error);
|
|
92
|
+
return {
|
|
93
|
+
contents: [
|
|
94
|
+
{
|
|
95
|
+
uri: uri.href,
|
|
96
|
+
text: jsonStringify({ error: message }),
|
|
97
|
+
mimeType: "application/json",
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { registerAnalysisPrompts } from "./prompts/analysisPrompts.js";
|
|
3
|
+
import { registerGenerationPrompts } from "./prompts/generationPrompts.js";
|
|
4
|
+
import { registerReviewResources } from "./resources/reviewResources.js";
|
|
5
|
+
import { registerDocumentTools } from "./tools/documentTools.js";
|
|
6
|
+
import { registerIntegrationTools } from "./tools/integrationTools.js";
|
|
7
|
+
import { registerProjectTools } from "./tools/projectTools.js";
|
|
8
|
+
import { registerReviewArtifactTools } from "./tools/reviewArtifactTools.js";
|
|
9
|
+
import { registerReviewTools } from "./tools/reviewTools.js";
|
|
10
|
+
import { registerWorkflowTools } from "./tools/workflowTools.js";
|
|
11
|
+
export function createServer() {
|
|
12
|
+
const mcp = new McpServer({
|
|
13
|
+
name: "SRAI Security Review",
|
|
14
|
+
version: "1.0.0",
|
|
15
|
+
description: "You are a security review assistant powered by the SRAI platform. " +
|
|
16
|
+
"When user does not provide numeric IDs, resolve project by name first " +
|
|
17
|
+
"using find_project_by_name, then list and resolve reviews only inside " +
|
|
18
|
+
"that project using list_reviews_by_project_name or list_reviews. " +
|
|
19
|
+
"Never assume project_id=1 or review_id=1. " +
|
|
20
|
+
"You can create projects, upload documents, run security reviews, " +
|
|
21
|
+
"fetch external content (Jira and Confluence), " +
|
|
22
|
+
"and analyze any content for security objectives, components, " +
|
|
23
|
+
"data dictionaries, threat scenarios (product, netwrok, workload type etc), and countermeasures. " +
|
|
24
|
+
"Use the available prompts for structured security analysis guidance." +
|
|
25
|
+
"do not create projects in securityreview until the user explicitly asks to do so" +
|
|
26
|
+
"if he asks to give a threat model in the chat, just give it there.",
|
|
27
|
+
});
|
|
28
|
+
registerProjectTools(mcp);
|
|
29
|
+
registerDocumentTools(mcp);
|
|
30
|
+
registerReviewTools(mcp);
|
|
31
|
+
registerWorkflowTools(mcp);
|
|
32
|
+
registerIntegrationTools(mcp);
|
|
33
|
+
registerReviewArtifactTools(mcp);
|
|
34
|
+
registerAnalysisPrompts(mcp);
|
|
35
|
+
registerGenerationPrompts(mcp);
|
|
36
|
+
registerReviewResources(mcp);
|
|
37
|
+
return mcp;
|
|
38
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SraiApiError } from "../api/client.js";
|
|
2
|
+
import { errorToolResult, textToolResult } from "../utils/serialization.js";
|
|
3
|
+
export async function runTool(task) {
|
|
4
|
+
try {
|
|
5
|
+
const result = await task();
|
|
6
|
+
return textToolResult(result);
|
|
7
|
+
}
|
|
8
|
+
catch (error) {
|
|
9
|
+
if (error instanceof SraiApiError) {
|
|
10
|
+
return errorToolResult(error.detail, error.statusCode);
|
|
11
|
+
}
|
|
12
|
+
return errorToolResult(error instanceof Error ? error.message : String(error));
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import * as z from "zod/v4";
|
|
5
|
+
import { getApiClient } from "../api/client.js";
|
|
6
|
+
import { errorToolResult, textToolResult } from "../utils/serialization.js";
|
|
7
|
+
import { runTool } from "./common.js";
|
|
8
|
+
export function registerDocumentTools(server) {
|
|
9
|
+
server.registerTool("list_documents", {
|
|
10
|
+
description: "List all documents in an SRAI project. Returns document IDs, names, types, and processing status. Use this to find documents for a review.",
|
|
11
|
+
inputSchema: {
|
|
12
|
+
project_id: z.number().int(),
|
|
13
|
+
},
|
|
14
|
+
}, async ({ project_id }) => runTool(async () => getApiClient().listDocuments(project_id)));
|
|
15
|
+
server.registerTool("get_document", {
|
|
16
|
+
description: "Get detailed information about a specific document in an SRAI project. Returns name, description, type, file path, and processing status.",
|
|
17
|
+
inputSchema: {
|
|
18
|
+
project_id: z.number().int(),
|
|
19
|
+
document_id: z.number().int(),
|
|
20
|
+
},
|
|
21
|
+
}, async ({ project_id, document_id }) => runTool(async () => getApiClient().getDocument(project_id, document_id)));
|
|
22
|
+
server.registerTool("get_document_evaluation", {
|
|
23
|
+
description: "Get the evaluation data for a processed document. Returns the extracted content summary and quality assessment. Only available after document processing is complete.",
|
|
24
|
+
inputSchema: {
|
|
25
|
+
project_id: z.number().int(),
|
|
26
|
+
document_id: z.number().int(),
|
|
27
|
+
},
|
|
28
|
+
}, async ({ project_id, document_id }) => runTool(async () => getApiClient().getDocumentEvaluation(project_id, document_id)));
|
|
29
|
+
server.registerTool("upload_component_diagram", {
|
|
30
|
+
description: "Upload a component diagram image file to an SRAI project using a local file path. This is optimized for IDE usage where the user provides a path from their local machine. Accepted file extensions: .png, .jpg, .jpeg.",
|
|
31
|
+
inputSchema: {
|
|
32
|
+
project_id: z.number().int(),
|
|
33
|
+
name: z.string(),
|
|
34
|
+
description: z.string(),
|
|
35
|
+
file_path: z.string(),
|
|
36
|
+
},
|
|
37
|
+
}, async ({ project_id, name, description, file_path }) => {
|
|
38
|
+
try {
|
|
39
|
+
const resolvedPath = expandUserPath(file_path);
|
|
40
|
+
const stat = await fs.stat(resolvedPath).catch(() => undefined);
|
|
41
|
+
if (!stat || !stat.isFile()) {
|
|
42
|
+
return errorToolResult(`File not found or not a file: ${file_path}`, 400);
|
|
43
|
+
}
|
|
44
|
+
const extension = path.extname(resolvedPath).toLowerCase();
|
|
45
|
+
if (![".png", ".jpg", ".jpeg"].includes(extension)) {
|
|
46
|
+
return errorToolResult("Invalid diagram file type. Accepted extensions are: .png, .jpg, .jpeg", 400);
|
|
47
|
+
}
|
|
48
|
+
const fileBytes = await fs.readFile(resolvedPath);
|
|
49
|
+
const result = await getApiClient().uploadComponentDiagram(project_id, name, description, fileBytes, path.basename(resolvedPath), contentTypeForExtension(extension));
|
|
50
|
+
return textToolResult(result);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
return errorToolResult(error instanceof Error ? `Unable to read file: ${error.message}` : String(error), 400);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
server.registerTool("upload_document", {
|
|
57
|
+
description: "Upload a knowledge base document or component diagram to an SRAI project. Provide file content as base64-encoded string, file name, and document type. Valid document_type values: 'Uploaded Knowledgebase doc', 'Component Diagram'. The document will be processed asynchronously after upload.",
|
|
58
|
+
inputSchema: {
|
|
59
|
+
project_id: z.number().int(),
|
|
60
|
+
name: z.string(),
|
|
61
|
+
description: z.string(),
|
|
62
|
+
document_type: z.string(),
|
|
63
|
+
file_content_base64: z.string(),
|
|
64
|
+
file_name: z.string(),
|
|
65
|
+
},
|
|
66
|
+
}, async ({ project_id, name, description, document_type, file_content_base64, file_name }) => runTool(async () => getApiClient().uploadDocument(project_id, name, description, document_type, Buffer.from(file_content_base64, "base64"), file_name)));
|
|
67
|
+
server.registerTool("create_document_from_content", {
|
|
68
|
+
description: "Create and upload a document from raw text/markdown content. The content is converted to a .txt file and uploaded to the SRAI project. Use this when you have generated or received text content that needs to be saved as a document in SRAI for security review. The LLM should generate detailed content before calling this tool.",
|
|
69
|
+
inputSchema: {
|
|
70
|
+
project_id: z.number().int(),
|
|
71
|
+
name: z.string(),
|
|
72
|
+
description: z.string(),
|
|
73
|
+
content: z.string(),
|
|
74
|
+
},
|
|
75
|
+
}, async ({ project_id, name, description, content }) => runTool(async () => getApiClient().uploadDocument(project_id, name, description, "Uploaded Knowledgebase doc", Buffer.from(content, "utf8"), `${name.replaceAll(" ", "_").toLowerCase()}.txt`)));
|
|
76
|
+
server.registerTool("link_external_document", {
|
|
77
|
+
description: "Link an external document to an SRAI project. Supports linking from Jira issues, Confluence pages, and GitHub repositories. Provide the appropriate source-specific fields in the payload.\n\nFor Jira: include document_type='Jira Issue', jira_issue_id, and optionally jira_project_key or jira_issue_url.\nFor Confluence: include document_type='Confluence Page', confluence_page_id, confluence_space_key, or confluence_page_url.\nFor GitHub: include document_type='Github Repository' and github_repo_url.",
|
|
78
|
+
inputSchema: {
|
|
79
|
+
project_id: z.number().int(),
|
|
80
|
+
name: z.string(),
|
|
81
|
+
description: z.string(),
|
|
82
|
+
document_type: z.string(),
|
|
83
|
+
source_id: z.string().default(""),
|
|
84
|
+
source_url: z.string().default(""),
|
|
85
|
+
source_key: z.string().default(""),
|
|
86
|
+
},
|
|
87
|
+
}, async ({ project_id, name, description, document_type, source_id, source_url, source_key }) => runTool(async () => {
|
|
88
|
+
const payload = {
|
|
89
|
+
name,
|
|
90
|
+
description,
|
|
91
|
+
document_type,
|
|
92
|
+
};
|
|
93
|
+
const docTypeLower = document_type.toLowerCase();
|
|
94
|
+
if (docTypeLower.includes("jira")) {
|
|
95
|
+
if (source_url) {
|
|
96
|
+
payload.jira_issue_url = source_url;
|
|
97
|
+
}
|
|
98
|
+
if (source_id) {
|
|
99
|
+
payload.jira_issue_id = source_id;
|
|
100
|
+
}
|
|
101
|
+
if (source_key) {
|
|
102
|
+
payload.jira_project_key = source_key;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else if (docTypeLower.includes("confluence")) {
|
|
106
|
+
if (source_url) {
|
|
107
|
+
payload.confluence_page_url = source_url;
|
|
108
|
+
}
|
|
109
|
+
if (source_id) {
|
|
110
|
+
payload.confluence_page_id = source_id;
|
|
111
|
+
}
|
|
112
|
+
if (source_key) {
|
|
113
|
+
payload.confluence_space_key = source_key;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else if (docTypeLower.includes("github")) {
|
|
117
|
+
if (source_url) {
|
|
118
|
+
payload.github_repo_url = source_url;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return getApiClient().linkDocument(project_id, payload);
|
|
122
|
+
}));
|
|
123
|
+
}
|
|
124
|
+
function expandUserPath(filePath) {
|
|
125
|
+
if (filePath.startsWith("~/")) {
|
|
126
|
+
return path.join(os.homedir(), filePath.slice(2));
|
|
127
|
+
}
|
|
128
|
+
return filePath;
|
|
129
|
+
}
|
|
130
|
+
function contentTypeForExtension(extension) {
|
|
131
|
+
switch (extension) {
|
|
132
|
+
case ".png":
|
|
133
|
+
return "image/png";
|
|
134
|
+
case ".jpg":
|
|
135
|
+
case ".jpeg":
|
|
136
|
+
return "image/jpeg";
|
|
137
|
+
default:
|
|
138
|
+
return "application/octet-stream";
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import * as z from "zod/v4";
|
|
2
|
+
import { getApiClient, SraiApiError } from "../api/client.js";
|
|
3
|
+
import { ConfluenceClient } from "../integrations/confluenceClient.js";
|
|
4
|
+
import { JiraClient } from "../integrations/jiraClient.js";
|
|
5
|
+
import { errorToolResult, textToolResult } from "../utils/serialization.js";
|
|
6
|
+
export function registerIntegrationTools(server) {
|
|
7
|
+
server.registerTool("fetch_jira_issue", {
|
|
8
|
+
description: "Fetch a Jira issue by its key (e.g., 'PROJ-123') or URL. Returns the issue summary, description, acceptance criteria, comments, status, priority, and labels. The fetched content can then be analyzed using the security analysis prompts, or linked to an SRAI project. Requires JIRA_BASE_URL, JIRA_EMAIL, and JIRA_API_TOKEN environment variables.",
|
|
9
|
+
inputSchema: {
|
|
10
|
+
issue_id: z.string(),
|
|
11
|
+
},
|
|
12
|
+
}, async ({ issue_id }) => textToolResult(await new JiraClient().fetchIssue(issue_id)));
|
|
13
|
+
server.registerTool("fetch_confluence_page", {
|
|
14
|
+
description: "Fetch a Confluence page by its ID or URL. Returns the page title, space, and full text content. The fetched content can then be analyzed using the security analysis prompts, or linked to an SRAI project. Requires CONFLUENCE_BASE_URL, CONFLUENCE_EMAIL, and CONFLUENCE_API_TOKEN environment variables.",
|
|
15
|
+
inputSchema: {
|
|
16
|
+
page_id: z.string().default(""),
|
|
17
|
+
page_url: z.string().default(""),
|
|
18
|
+
},
|
|
19
|
+
}, async ({ page_id, page_url }) => {
|
|
20
|
+
try {
|
|
21
|
+
const client = new ConfluenceClient();
|
|
22
|
+
if (page_url) {
|
|
23
|
+
return textToolResult(await client.fetchPageByUrl(page_url));
|
|
24
|
+
}
|
|
25
|
+
if (page_id) {
|
|
26
|
+
return textToolResult(await client.fetchPage(page_id));
|
|
27
|
+
}
|
|
28
|
+
return errorToolResult("Provide either page_id or page_url");
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
return errorToolResult(error instanceof Error ? error.message : String(error));
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
server.registerTool("search_confluence_pages", {
|
|
35
|
+
description: "Search Confluence pages using CQL and return matching pages with content. Use this when you don't have a page ID/URL yet or need to fetch multiple pages. Examples: cql='space=ENG and type=page and title~\"Threat Model\"'. Requires CONFLUENCE_BASE_URL, CONFLUENCE_EMAIL, and CONFLUENCE_API_TOKEN.",
|
|
36
|
+
inputSchema: {
|
|
37
|
+
cql: z.string(),
|
|
38
|
+
limit: z.number().int().default(10),
|
|
39
|
+
include_content: z.boolean().default(true),
|
|
40
|
+
},
|
|
41
|
+
}, async ({ cql, limit, include_content }) => {
|
|
42
|
+
try {
|
|
43
|
+
const result = await new ConfluenceClient().searchPages(cql, limit, include_content);
|
|
44
|
+
return textToolResult(result);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
return errorToolResult(error instanceof Error ? error.message : String(error));
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
server.registerTool("fetch_and_link_to_srai", {
|
|
51
|
+
description: "Fetch content from an external source AND link it as a document in an SRAI project. This is a convenience tool that combines fetching (from Jira or Confluence) with linking it to a project.\n\nParameters:\n- project_id: The SRAI project to link to\n- source_type: 'jira' or 'confluence'\n- source_identifier: The issue key or page ID/URL\n- name: Document name in SRAI\n- description: Document description in SRAI",
|
|
52
|
+
inputSchema: {
|
|
53
|
+
project_id: z.number().int(),
|
|
54
|
+
source_type: z.string(),
|
|
55
|
+
source_identifier: z.string(),
|
|
56
|
+
name: z.string(),
|
|
57
|
+
description: z.string(),
|
|
58
|
+
},
|
|
59
|
+
}, async ({ project_id, source_type, source_identifier, name, description }) => {
|
|
60
|
+
const docTypeMap = {
|
|
61
|
+
jira: "Jira Issue",
|
|
62
|
+
confluence: "Confluence Page",
|
|
63
|
+
};
|
|
64
|
+
const docType = docTypeMap[source_type.toLowerCase()];
|
|
65
|
+
if (!docType) {
|
|
66
|
+
return errorToolResult(`Unknown source_type: ${source_type}. Use: jira, confluence`);
|
|
67
|
+
}
|
|
68
|
+
const payload = {
|
|
69
|
+
name,
|
|
70
|
+
description,
|
|
71
|
+
document_type: docType,
|
|
72
|
+
};
|
|
73
|
+
if (source_type.toLowerCase() === "jira") {
|
|
74
|
+
if (source_identifier.includes("://")) {
|
|
75
|
+
payload.jira_issue_url = source_identifier;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
payload.jira_issue_id = source_identifier;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else if (source_type.toLowerCase() === "confluence") {
|
|
82
|
+
if (source_identifier.includes("://")) {
|
|
83
|
+
payload.confluence_page_url = source_identifier;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
payload.confluence_page_id = source_identifier;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const result = await getApiClient().linkDocument(project_id, payload);
|
|
91
|
+
return textToolResult(result);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
if (error instanceof SraiApiError) {
|
|
95
|
+
return errorToolResult(error.detail, error.statusCode);
|
|
96
|
+
}
|
|
97
|
+
return errorToolResult(error instanceof Error ? error.message : String(error));
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|