@nohacklabs/mcp-server 1.0.0
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 +243 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +525 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# NoHack Security MCP Server
|
|
2
|
+
|
|
3
|
+
An MCP (Model Context Protocol) server that provides a **generalized Directus query interface** for the NoHack security scanning API. This follows the same pattern as the AI chat agent, using a powerful `query_collection` tool that leverages Directus's full query capabilities.
|
|
4
|
+
|
|
5
|
+
## Key Design
|
|
6
|
+
|
|
7
|
+
Instead of creating specific tools for each entity (list_scans, list_vulnerabilities, etc.), this MCP server uses a **generalized approach**:
|
|
8
|
+
|
|
9
|
+
1. **`query_collection`** - The powerhouse tool that can query ANY Directus collection with full filter, aggregate, groupBy, sort, and pagination support
|
|
10
|
+
2. **`get_collection_schema`** - Discover fields and relations before querying
|
|
11
|
+
3. **Convenience tools** - For fetching full records with all relations
|
|
12
|
+
|
|
13
|
+
This approach is more flexible and maintainable - any new query capability in Directus is automatically available without code changes.
|
|
14
|
+
|
|
15
|
+
## Tools
|
|
16
|
+
|
|
17
|
+
### Schema Discovery
|
|
18
|
+
| Tool | Description |
|
|
19
|
+
|------|-------------|
|
|
20
|
+
| `get_collection_schema` | Get fields, types, and relations for any collection |
|
|
21
|
+
|
|
22
|
+
### The Powerhouse Tool
|
|
23
|
+
| Tool | Description |
|
|
24
|
+
|------|-------------|
|
|
25
|
+
| `query_collection` | Generic Directus query with full capabilities |
|
|
26
|
+
|
|
27
|
+
### Convenience Tools
|
|
28
|
+
| Tool | Description |
|
|
29
|
+
|------|-------------|
|
|
30
|
+
| `get_scan_details` | Full scan with vulns/secrets |
|
|
31
|
+
| `get_vulnerability_details` | Full vulnerability record |
|
|
32
|
+
| `get_repo_config_details` | Full repo with all relations |
|
|
33
|
+
| `get_organization` | Organization details |
|
|
34
|
+
|
|
35
|
+
## Available Collections
|
|
36
|
+
|
|
37
|
+
- `scans` - Security scan records
|
|
38
|
+
- `vulnerabilities` - Detected vulnerabilities
|
|
39
|
+
- `secrets` - Detected secrets
|
|
40
|
+
- `github_repo_config` - Repository configurations
|
|
41
|
+
- `organisations` - Organization data
|
|
42
|
+
|
|
43
|
+
## Query Examples
|
|
44
|
+
|
|
45
|
+
### List Recent Scans
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"collection": "scans",
|
|
50
|
+
"fields": ["id", "session_id", "scan_type", "url", "branch", "date_created"],
|
|
51
|
+
"sort": ["-date_created"],
|
|
52
|
+
"limit": 10,
|
|
53
|
+
"orgId": "org-123"
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Count Vulnerabilities by Severity
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"collection": "vulnerabilities",
|
|
62
|
+
"aggregate": { "count": "*" },
|
|
63
|
+
"groupBy": ["severity"],
|
|
64
|
+
"limit": -1,
|
|
65
|
+
"orgId": "org-123"
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Critical Vulnerabilities from Last 7 Days
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"collection": "vulnerabilities",
|
|
74
|
+
"filter": {
|
|
75
|
+
"_and": [
|
|
76
|
+
{ "severity": { "_eq": "Critical" } },
|
|
77
|
+
{ "date_created": { "_gte": "$NOW(-7 days)" } }
|
|
78
|
+
]
|
|
79
|
+
},
|
|
80
|
+
"fields": ["id", "type", "file", "line", "description"],
|
|
81
|
+
"limit": 50,
|
|
82
|
+
"orgId": "org-123"
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Vulnerabilities for a Specific Repo
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"collection": "vulnerabilities",
|
|
91
|
+
"filter": {
|
|
92
|
+
"scan": {
|
|
93
|
+
"repo_config": {
|
|
94
|
+
"name": { "_eq": "my-repo" }
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"fields": ["id", "type", "severity", "file", "scan.repo_config.name"],
|
|
99
|
+
"limit": 100
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Vulnerability Trend by Month
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"collection": "vulnerabilities",
|
|
108
|
+
"aggregate": { "count": "*" },
|
|
109
|
+
"groupBy": ["year(date_created)", "month(date_created)"],
|
|
110
|
+
"sort": ["year(date_created)", "month(date_created)"],
|
|
111
|
+
"limit": -1,
|
|
112
|
+
"orgId": "org-123"
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Repos with Most Critical Vulnerabilities
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"collection": "vulnerabilities",
|
|
121
|
+
"filter": { "severity": { "_eq": "Critical" } },
|
|
122
|
+
"aggregate": { "count": "*" },
|
|
123
|
+
"groupBy": ["repo_config"],
|
|
124
|
+
"fields": ["repo_config.name", "repo_config.full_name"],
|
|
125
|
+
"sort": ["-count"],
|
|
126
|
+
"limit": -1
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Filter Operators
|
|
131
|
+
|
|
132
|
+
| Operator | Description | Example |
|
|
133
|
+
|----------|-------------|---------|
|
|
134
|
+
| `_eq` | Equals | `{ "severity": { "_eq": "Critical" } }` |
|
|
135
|
+
| `_neq` | Not equals | `{ "status": { "_neq": "archived" } }` |
|
|
136
|
+
| `_in` | In array | `{ "severity": { "_in": ["Critical", "High"] } }` |
|
|
137
|
+
| `_gt`, `_gte`, `_lt`, `_lte` | Comparisons | `{ "confidence": { "_gte": 80 } }` |
|
|
138
|
+
| `_contains`, `_icontains` | Text search | `{ "file": { "_icontains": "auth" } }` |
|
|
139
|
+
| `_starts_with`, `_ends_with` | Text match | `{ "file": { "_ends_with": ".js" } }` |
|
|
140
|
+
| `_null`, `_nnull` | Null checks | `{ "description": { "_nnull": true } }` |
|
|
141
|
+
| `_and`, `_or` | Logical | `{ "_and": [{...}, {...}] }` |
|
|
142
|
+
|
|
143
|
+
## Dynamic Date Expressions
|
|
144
|
+
|
|
145
|
+
Directus supports `$NOW()` for relative dates:
|
|
146
|
+
|
|
147
|
+
| Expression | Meaning |
|
|
148
|
+
|------------|---------|
|
|
149
|
+
| `$NOW(-7 days)` | 7 days ago |
|
|
150
|
+
| `$NOW(-1 month)` | 1 month ago |
|
|
151
|
+
| `$NOW(-1 year)` | 1 year ago |
|
|
152
|
+
| `$NOW(-2 weeks)` | 2 weeks ago |
|
|
153
|
+
|
|
154
|
+
## Installation
|
|
155
|
+
|
|
156
|
+
### From npm (Recommended)
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
npm install -g @nohacklabs/mcp-server
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Or use directly with `npx`:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
npx @nohacklabs/mcp-server
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### From Source
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
git clone https://github.com/nohacklabs/mcp-server
|
|
172
|
+
cd mcp-server
|
|
173
|
+
npm install
|
|
174
|
+
npm run build
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Configuration
|
|
178
|
+
|
|
179
|
+
The server requires two environment variables:
|
|
180
|
+
|
|
181
|
+
| Variable | Description |
|
|
182
|
+
|----------|-------------|
|
|
183
|
+
| `NOHACK_API_URL` | Your NoHack API endpoint |
|
|
184
|
+
| `NOHACK_API_TOKEN` | Your API authentication token |
|
|
185
|
+
|
|
186
|
+
## Usage
|
|
187
|
+
|
|
188
|
+
### Claude Desktop Integration
|
|
189
|
+
|
|
190
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
191
|
+
|
|
192
|
+
```json
|
|
193
|
+
{
|
|
194
|
+
"mcpServers": {
|
|
195
|
+
"nohack-security": {
|
|
196
|
+
"command": "npx",
|
|
197
|
+
"args": ["-y", "@nohacklabs/mcp-server"],
|
|
198
|
+
"env": {
|
|
199
|
+
"NOHACK_API_URL": "https://your-api-endpoint.com",
|
|
200
|
+
"NOHACK_API_TOKEN": "your-api-token"
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Cursor Integration
|
|
208
|
+
|
|
209
|
+
Add to `.cursor/mcp.json` in your project or `~/.cursor/mcp.json` globally:
|
|
210
|
+
|
|
211
|
+
```json
|
|
212
|
+
{
|
|
213
|
+
"mcpServers": {
|
|
214
|
+
"nohack-security": {
|
|
215
|
+
"command": "npx",
|
|
216
|
+
"args": ["-y", "@nohacklabs/mcp-server"],
|
|
217
|
+
"env": {
|
|
218
|
+
"NOHACK_API_URL": "https://your-api-endpoint.com",
|
|
219
|
+
"NOHACK_API_TOKEN": "your-api-token"
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Test with MCP Inspector
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
npx @anthropic-ai/mcp-inspector npx @nohacklabs/mcp-server
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Development
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
npm install # Install dependencies
|
|
236
|
+
npm run build # Build TypeScript
|
|
237
|
+
npm run dev # Watch mode
|
|
238
|
+
npm run inspector # Test with MCP Inspector
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## License
|
|
242
|
+
|
|
243
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// DIRECTUS API CLIENT
|
|
7
|
+
// ============================================================================
|
|
8
|
+
class DirectusClient {
|
|
9
|
+
apiUrl;
|
|
10
|
+
token;
|
|
11
|
+
constructor(apiUrl, token) {
|
|
12
|
+
this.apiUrl = apiUrl;
|
|
13
|
+
this.token = token;
|
|
14
|
+
}
|
|
15
|
+
async fetch(endpoint, options) {
|
|
16
|
+
const url = `${this.apiUrl}${endpoint}`;
|
|
17
|
+
const response = await fetch(url, {
|
|
18
|
+
...options,
|
|
19
|
+
headers: {
|
|
20
|
+
Authorization: `Bearer ${this.token}`,
|
|
21
|
+
"Content-Type": "application/json",
|
|
22
|
+
...options?.headers,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
throw new Error(`API Error: ${response.status} ${response.statusText}`);
|
|
27
|
+
}
|
|
28
|
+
return response.json();
|
|
29
|
+
}
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Schema Discovery
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
async getCollectionSchema(collection) {
|
|
34
|
+
const result = await this.fetch(`/fields/${collection}`);
|
|
35
|
+
const fields = result.data || [];
|
|
36
|
+
// Filter out system/hidden fields and extract essential info
|
|
37
|
+
const relevantFields = fields
|
|
38
|
+
.filter((f) => !f.meta?.hidden &&
|
|
39
|
+
![
|
|
40
|
+
"sort",
|
|
41
|
+
"user_created",
|
|
42
|
+
"user_updated",
|
|
43
|
+
"date_created",
|
|
44
|
+
"date_updated",
|
|
45
|
+
].includes(f.field))
|
|
46
|
+
.map((field) => {
|
|
47
|
+
const isRelation = field.schema?.foreign_key_table ||
|
|
48
|
+
field.meta?.special?.includes("m2o") ||
|
|
49
|
+
field.meta?.special?.includes("o2m");
|
|
50
|
+
return {
|
|
51
|
+
field: field.field,
|
|
52
|
+
type: field.type,
|
|
53
|
+
...(isRelation && {
|
|
54
|
+
relation: field.meta?.related_collection || field.schema?.foreign_key_table,
|
|
55
|
+
relationType: field.meta?.special?.find((s) => ["m2o", "o2m", "m2m"].includes(s)),
|
|
56
|
+
}),
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
const regularFields = relevantFields.filter((f) => !f.relation);
|
|
60
|
+
const relationFields = relevantFields.filter((f) => f.relation);
|
|
61
|
+
return {
|
|
62
|
+
collection,
|
|
63
|
+
totalFields: relevantFields.length,
|
|
64
|
+
fields: regularFields.map((f) => `${f.field} (${f.type})`),
|
|
65
|
+
relations: relationFields.map((f) => `${f.field} → ${f.relation} (${f.relationType})`),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Generic Query
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
async queryCollection(options) {
|
|
72
|
+
const { collection, filter, aggregate, groupBy, sort, limit, offset, fields, deep, orgId, } = options;
|
|
73
|
+
// Define org filter paths for each collection
|
|
74
|
+
const orgFilterPaths = {
|
|
75
|
+
scans: {
|
|
76
|
+
repo_config: {
|
|
77
|
+
organisation: { _eq: orgId },
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
vulnerabilities: {
|
|
81
|
+
scan: {
|
|
82
|
+
repo_config: {
|
|
83
|
+
organisation: { _eq: orgId },
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
secrets: {
|
|
88
|
+
session: {
|
|
89
|
+
repo_config: {
|
|
90
|
+
organisation: { _eq: orgId },
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
github_repo_config: {
|
|
95
|
+
organisation: { _eq: orgId },
|
|
96
|
+
},
|
|
97
|
+
organisations: {
|
|
98
|
+
id: { _eq: orgId },
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
// Get org filter for this collection (if orgId provided)
|
|
102
|
+
let combinedFilter = filter;
|
|
103
|
+
if (orgId) {
|
|
104
|
+
const orgFilter = orgFilterPaths[collection];
|
|
105
|
+
if (orgFilter) {
|
|
106
|
+
combinedFilter = filter ? { _and: [orgFilter, filter] } : orgFilter;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const params = new URLSearchParams();
|
|
110
|
+
if (combinedFilter) {
|
|
111
|
+
params.set("filter", JSON.stringify(combinedFilter));
|
|
112
|
+
}
|
|
113
|
+
// Add aggregation
|
|
114
|
+
if (aggregate) {
|
|
115
|
+
if (aggregate.count) {
|
|
116
|
+
if (Array.isArray(aggregate.count)) {
|
|
117
|
+
aggregate.count.forEach((field) => params.append("aggregate[count][]", field));
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
params.set("aggregate[count]", aggregate.count);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (aggregate.avg) {
|
|
124
|
+
aggregate.avg.forEach((field) => params.append("aggregate[avg][]", field));
|
|
125
|
+
}
|
|
126
|
+
if (aggregate.sum) {
|
|
127
|
+
aggregate.sum.forEach((field) => params.append("aggregate[sum][]", field));
|
|
128
|
+
}
|
|
129
|
+
if (aggregate.min) {
|
|
130
|
+
aggregate.min.forEach((field) => params.append("aggregate[min][]", field));
|
|
131
|
+
}
|
|
132
|
+
if (aggregate.max) {
|
|
133
|
+
aggregate.max.forEach((field) => params.append("aggregate[max][]", field));
|
|
134
|
+
}
|
|
135
|
+
if (aggregate.countDistinct) {
|
|
136
|
+
aggregate.countDistinct.forEach((field) => params.append("aggregate[countDistinct][]", field));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Add groupBy
|
|
140
|
+
if (groupBy && groupBy.length > 0) {
|
|
141
|
+
groupBy.forEach((field) => params.append("groupBy[]", field));
|
|
142
|
+
}
|
|
143
|
+
// Add sort
|
|
144
|
+
if (sort && sort.length > 0) {
|
|
145
|
+
sort.forEach((field) => params.append("sort[]", field));
|
|
146
|
+
}
|
|
147
|
+
// Add limit
|
|
148
|
+
if (limit !== undefined) {
|
|
149
|
+
params.set("limit", limit.toString());
|
|
150
|
+
}
|
|
151
|
+
// Add offset (for pagination)
|
|
152
|
+
if (offset !== undefined) {
|
|
153
|
+
params.set("offset", offset.toString());
|
|
154
|
+
}
|
|
155
|
+
// Add fields (supports dot notation for relations)
|
|
156
|
+
if (fields && fields.length > 0) {
|
|
157
|
+
fields.forEach((field) => params.append("fields[]", field));
|
|
158
|
+
}
|
|
159
|
+
// Add deep query parameters for nested relations
|
|
160
|
+
if (deep) {
|
|
161
|
+
Object.entries(deep).forEach(([key, value]) => {
|
|
162
|
+
if (typeof value === "object") {
|
|
163
|
+
Object.entries(value).forEach(([deepKey, deepValue]) => {
|
|
164
|
+
params.set(`deep[${key}][${deepKey}]`, String(deepValue));
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
// Add meta for total count
|
|
170
|
+
params.set("meta", "total_count,filter_count");
|
|
171
|
+
const result = await this.fetch(`/items/${collection}?${params.toString()}`);
|
|
172
|
+
let data = result.data || [];
|
|
173
|
+
// Post-process: If we're grouping by a relation and requesting relation fields,
|
|
174
|
+
// Directus returns only UUIDs. We need to fetch the relation details separately.
|
|
175
|
+
if (groupBy && fields && data.length > 0) {
|
|
176
|
+
const relationFields = fields.filter((f) => f.includes("."));
|
|
177
|
+
const groupedRelations = groupBy.filter((g) => relationFields.some((f) => f.startsWith(g + ".")) &&
|
|
178
|
+
typeof data[0][g] === "string" // Check if it's a UUID (not expanded)
|
|
179
|
+
);
|
|
180
|
+
if (groupedRelations.length > 0) {
|
|
181
|
+
for (const relation of groupedRelations) {
|
|
182
|
+
const relationIds = [
|
|
183
|
+
...new Set(data.map((item) => item[relation]).filter(Boolean)),
|
|
184
|
+
];
|
|
185
|
+
if (relationIds.length === 0)
|
|
186
|
+
continue;
|
|
187
|
+
const relationCollection = relation === "repo_config"
|
|
188
|
+
? "github_repo_config"
|
|
189
|
+
: relation === "scan"
|
|
190
|
+
? "scans"
|
|
191
|
+
: relation === "session"
|
|
192
|
+
? "sessions"
|
|
193
|
+
: relation;
|
|
194
|
+
const subFields = relationFields
|
|
195
|
+
.filter((f) => f.startsWith(relation + "."))
|
|
196
|
+
.map((f) => f.substring(relation.length + 1));
|
|
197
|
+
const relationParams = new URLSearchParams();
|
|
198
|
+
relationParams.set("filter", JSON.stringify({ id: { _in: relationIds } }));
|
|
199
|
+
subFields.forEach((sf) => relationParams.append("fields[]", sf));
|
|
200
|
+
relationParams.append("fields[]", "id");
|
|
201
|
+
relationParams.set("limit", "-1");
|
|
202
|
+
const relationResult = await this.fetch(`/items/${relationCollection}?${relationParams.toString()}`);
|
|
203
|
+
const relationMap = new Map(relationResult.data.map((item) => [item.id, item]));
|
|
204
|
+
data = data.map((item) => {
|
|
205
|
+
const relationId = item[relation];
|
|
206
|
+
const relationDetails = relationMap.get(relationId);
|
|
207
|
+
if (relationDetails) {
|
|
208
|
+
return { ...item, [relation]: relationDetails };
|
|
209
|
+
}
|
|
210
|
+
return item;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
data,
|
|
217
|
+
meta: result.meta || { total_count: 0, filter_count: 0 },
|
|
218
|
+
count: data.length,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
// ---------------------------------------------------------------------------
|
|
222
|
+
// Convenience Methods
|
|
223
|
+
// ---------------------------------------------------------------------------
|
|
224
|
+
async getScanById(scanId) {
|
|
225
|
+
const result = await this.fetch(`/items/scans/${scanId}?fields=*,secrets.*,vulnerabilities.*,repo_config.organisation,repo_config.name` +
|
|
226
|
+
`&deep[secrets][_limit]=-1&deep[secrets][_filter][status][_eq]=published` +
|
|
227
|
+
`&deep[vulnerabilities][_limit]=-1&deep[vulnerabilities][_filter][status][_in]=published,archived`);
|
|
228
|
+
return result.data;
|
|
229
|
+
}
|
|
230
|
+
async getVulnerabilityById(vulnId) {
|
|
231
|
+
const result = await this.fetch(`/items/vulnerabilities/${vulnId}`);
|
|
232
|
+
return result.data;
|
|
233
|
+
}
|
|
234
|
+
async getRepoConfigById(repoId) {
|
|
235
|
+
const result = await this.fetch(`/items/github_repo_config/${repoId}?fields=*,scans.*,secrets.*,vulnerabilities.*,organisation` +
|
|
236
|
+
`&deep[scans][_limit]=-1&deep[scans][_filter][status][_eq]=published` +
|
|
237
|
+
`&deep[secrets][_limit]=-1&deep[secrets][_filter][status][_eq]=published` +
|
|
238
|
+
`&deep[vulnerabilities][_limit]=-1`);
|
|
239
|
+
return {
|
|
240
|
+
repository: result.data,
|
|
241
|
+
scans: result.data.scans,
|
|
242
|
+
secrets: result.data.secrets,
|
|
243
|
+
vulnerabilities: result.data.vulnerabilities,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
async getOrganization(orgId) {
|
|
247
|
+
const result = await this.fetch(`/items/organisations/${orgId}?fields=id,name,quota`);
|
|
248
|
+
return result.data;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
// ============================================================================
|
|
252
|
+
// MCP SERVER
|
|
253
|
+
// ============================================================================
|
|
254
|
+
const server = new McpServer({
|
|
255
|
+
name: "nohack-security-api",
|
|
256
|
+
version: "1.0.0",
|
|
257
|
+
description: "MCP server for NoHack security scanning API - Generic Directus query interface for managing repo configs, scans, vulnerabilities, and secrets",
|
|
258
|
+
});
|
|
259
|
+
// Get API configuration from environment
|
|
260
|
+
function getClient() {
|
|
261
|
+
const apiUrl = process.env.NOHACK_API_URL;
|
|
262
|
+
const apiToken = process.env.NOHACK_API_TOKEN;
|
|
263
|
+
if (!apiUrl || !apiToken) {
|
|
264
|
+
throw new Error("NOHACK_API_URL and NOHACK_API_TOKEN environment variables are required");
|
|
265
|
+
}
|
|
266
|
+
return new DirectusClient(apiUrl, apiToken);
|
|
267
|
+
}
|
|
268
|
+
// ============================================================================
|
|
269
|
+
// TOOL DEFINITIONS
|
|
270
|
+
// ============================================================================
|
|
271
|
+
// Define collections enum for reuse
|
|
272
|
+
const collectionsEnum = z.enum([
|
|
273
|
+
"scans",
|
|
274
|
+
"vulnerabilities",
|
|
275
|
+
"secrets",
|
|
276
|
+
"github_repo_config",
|
|
277
|
+
"organisations",
|
|
278
|
+
]);
|
|
279
|
+
// --- Schema Discovery Tool ---
|
|
280
|
+
server.registerTool("get_collection_schema", {
|
|
281
|
+
description: `Get the complete schema (fields, types, and relations) for any Directus collection.
|
|
282
|
+
Use this FIRST if you don't know what fields are available.
|
|
283
|
+
|
|
284
|
+
Available collections:
|
|
285
|
+
- 'scans': Security scan records
|
|
286
|
+
- 'vulnerabilities': Detected vulnerabilities (43 fields)
|
|
287
|
+
- 'secrets': Detected secrets
|
|
288
|
+
- 'github_repo_config': Repository configurations
|
|
289
|
+
- 'organisations': Organization data`,
|
|
290
|
+
inputSchema: z.object({
|
|
291
|
+
collection: collectionsEnum.describe("The collection to get schema for"),
|
|
292
|
+
}),
|
|
293
|
+
}, async ({ collection }) => {
|
|
294
|
+
try {
|
|
295
|
+
const client = getClient();
|
|
296
|
+
const schema = await client.getCollectionSchema(collection);
|
|
297
|
+
return {
|
|
298
|
+
content: [{ type: "text", text: JSON.stringify(schema, null, 2) }],
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
return {
|
|
303
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
304
|
+
isError: true,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
// --- The Powerhouse Tool: queryCollection ---
|
|
309
|
+
server.registerTool("query_collection", {
|
|
310
|
+
description: `THE POWERHOUSE TOOL - Generic query for ANY Directus collection with FULL capabilities.
|
|
311
|
+
|
|
312
|
+
USE THIS FOR:
|
|
313
|
+
- Listing data (scans, vulnerabilities, secrets, repos)
|
|
314
|
+
- Getting counts (without fetching all data)
|
|
315
|
+
- Filtering by any field or relation
|
|
316
|
+
- Trend analysis & time-series data
|
|
317
|
+
- Grouping & aggregations
|
|
318
|
+
- Complex multi-condition filters
|
|
319
|
+
- Pagination
|
|
320
|
+
|
|
321
|
+
AVAILABLE COLLECTIONS:
|
|
322
|
+
- 'scans': Security scan data
|
|
323
|
+
- 'vulnerabilities': Vulnerability findings
|
|
324
|
+
- 'secrets': Exposed secrets
|
|
325
|
+
- 'github_repo_config': Repository configurations
|
|
326
|
+
- 'organisations': Organization data
|
|
327
|
+
|
|
328
|
+
CRITICAL RULES:
|
|
329
|
+
1. ALWAYS specify fields (collections have 40+ fields!)
|
|
330
|
+
2. Use limit: -1 ONLY for aggregations (to count ALL records)
|
|
331
|
+
3. Use limit: 10-100 for data queries
|
|
332
|
+
4. Use orgId to filter by organization
|
|
333
|
+
|
|
334
|
+
FILTER OPERATORS:
|
|
335
|
+
- Equality: _eq, _neq, _in, _nin
|
|
336
|
+
- Comparison: _gt, _gte, _lt, _lte
|
|
337
|
+
- Text: _contains, _icontains, _starts_with, _ends_with
|
|
338
|
+
- Null: _null, _nnull, _empty, _nempty
|
|
339
|
+
- Logical: _and, _or
|
|
340
|
+
- Relations: _some, _none
|
|
341
|
+
|
|
342
|
+
DYNAMIC DATES (use in filters):
|
|
343
|
+
- Last 7 days: { "date_created": { "_gte": "$NOW(-7 days)" } }
|
|
344
|
+
- Last month: { "date_created": { "_gte": "$NOW(-1 month)" } }
|
|
345
|
+
|
|
346
|
+
AGGREGATIONS:
|
|
347
|
+
- count: Count records (use "*" or specific field)
|
|
348
|
+
- avg, sum, min, max: Numeric operations
|
|
349
|
+
- groupBy: Group results (e.g., ["severity", "vuln_status"])
|
|
350
|
+
|
|
351
|
+
EXAMPLES:
|
|
352
|
+
// List critical vulnerabilities
|
|
353
|
+
{ collection: "vulnerabilities", filter: { severity: { _eq: "Critical" } }, fields: ["id", "type", "file"], limit: 50 }
|
|
354
|
+
|
|
355
|
+
// Count by severity
|
|
356
|
+
{ collection: "vulnerabilities", aggregate: { count: "*" }, groupBy: ["severity"], limit: -1 }
|
|
357
|
+
|
|
358
|
+
// Recent scans for a repo
|
|
359
|
+
{ collection: "scans", filter: { repo_config: { name: { _eq: "my-repo" } } }, fields: ["id", "scan_type", "date_created"], limit: 10 }`,
|
|
360
|
+
inputSchema: z.object({
|
|
361
|
+
collection: collectionsEnum.describe("The collection to query"),
|
|
362
|
+
filter: z
|
|
363
|
+
.record(z.any())
|
|
364
|
+
.optional()
|
|
365
|
+
.describe('Directus filter object. Supports nested relations. Example: {"severity": {"_eq": "Critical"}, "scan": {"scan_type": {"_eq": "sast"}}}'),
|
|
366
|
+
aggregate: z
|
|
367
|
+
.object({
|
|
368
|
+
count: z.union([z.string(), z.array(z.string())]).optional(),
|
|
369
|
+
avg: z.array(z.string()).optional(),
|
|
370
|
+
sum: z.array(z.string()).optional(),
|
|
371
|
+
min: z.array(z.string()).optional(),
|
|
372
|
+
max: z.array(z.string()).optional(),
|
|
373
|
+
countDistinct: z.array(z.string()).optional(),
|
|
374
|
+
})
|
|
375
|
+
.optional()
|
|
376
|
+
.describe("Aggregation functions"),
|
|
377
|
+
groupBy: z
|
|
378
|
+
.array(z.string())
|
|
379
|
+
.optional()
|
|
380
|
+
.describe('Fields to group by (e.g., ["severity", "vuln_status"])'),
|
|
381
|
+
sort: z
|
|
382
|
+
.array(z.string())
|
|
383
|
+
.optional()
|
|
384
|
+
.describe('Sort fields (prefix with - for descending, e.g., ["-date_created"])'),
|
|
385
|
+
limit: z
|
|
386
|
+
.number()
|
|
387
|
+
.optional()
|
|
388
|
+
.describe("Max results. Use -1 for aggregations to count ALL records, 10-100 for data queries"),
|
|
389
|
+
offset: z.number().optional().describe("Records to skip (for pagination)"),
|
|
390
|
+
fields: z
|
|
391
|
+
.array(z.string())
|
|
392
|
+
.optional()
|
|
393
|
+
.describe('Fields to return. Use dot notation for relations: ["id", "scan.repo_config.name"]'),
|
|
394
|
+
deep: z
|
|
395
|
+
.record(z.any())
|
|
396
|
+
.optional()
|
|
397
|
+
.describe("Deep query parameters for nested relations"),
|
|
398
|
+
orgId: z
|
|
399
|
+
.string()
|
|
400
|
+
.optional()
|
|
401
|
+
.describe("Organization ID to filter by (auto-applied to queries)"),
|
|
402
|
+
}),
|
|
403
|
+
}, async ({ collection, filter, aggregate, groupBy, sort, limit, offset, fields, deep, orgId, }) => {
|
|
404
|
+
try {
|
|
405
|
+
const client = getClient();
|
|
406
|
+
const result = await client.queryCollection({
|
|
407
|
+
collection,
|
|
408
|
+
filter,
|
|
409
|
+
aggregate,
|
|
410
|
+
groupBy,
|
|
411
|
+
sort,
|
|
412
|
+
limit,
|
|
413
|
+
offset,
|
|
414
|
+
fields,
|
|
415
|
+
deep,
|
|
416
|
+
orgId,
|
|
417
|
+
});
|
|
418
|
+
return {
|
|
419
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
catch (error) {
|
|
423
|
+
return {
|
|
424
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
425
|
+
isError: true,
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
// --- Convenience Tools ---
|
|
430
|
+
server.registerTool("get_scan_details", {
|
|
431
|
+
description: `Get detailed information about a specific scan including all vulnerabilities, secrets, and related data.
|
|
432
|
+
Use this when you need the FULL scan record with all relations.`,
|
|
433
|
+
inputSchema: z.object({
|
|
434
|
+
scanId: z.string().describe("The scan ID"),
|
|
435
|
+
}),
|
|
436
|
+
}, async ({ scanId }) => {
|
|
437
|
+
try {
|
|
438
|
+
const client = getClient();
|
|
439
|
+
const result = await client.getScanById(scanId);
|
|
440
|
+
return {
|
|
441
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
catch (error) {
|
|
445
|
+
return {
|
|
446
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
447
|
+
isError: true,
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
server.registerTool("get_vulnerability_details", {
|
|
452
|
+
description: `Get detailed information about a specific vulnerability including code context, source, sink, data flow, and recommendations.
|
|
453
|
+
Use this when you need the FULL vulnerability record.`,
|
|
454
|
+
inputSchema: z.object({
|
|
455
|
+
vulnerabilityId: z.string().describe("The vulnerability ID"),
|
|
456
|
+
}),
|
|
457
|
+
}, async ({ vulnerabilityId }) => {
|
|
458
|
+
try {
|
|
459
|
+
const client = getClient();
|
|
460
|
+
const result = await client.getVulnerabilityById(vulnerabilityId);
|
|
461
|
+
return {
|
|
462
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
catch (error) {
|
|
466
|
+
return {
|
|
467
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
468
|
+
isError: true,
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
server.registerTool("get_repo_config_details", {
|
|
473
|
+
description: `Get detailed repository configuration including all related scans, vulnerabilities, and secrets.
|
|
474
|
+
Use this when you need the FULL repository record with all relations.`,
|
|
475
|
+
inputSchema: z.object({
|
|
476
|
+
repoId: z.string().describe("The repository configuration ID"),
|
|
477
|
+
}),
|
|
478
|
+
}, async ({ repoId }) => {
|
|
479
|
+
try {
|
|
480
|
+
const client = getClient();
|
|
481
|
+
const result = await client.getRepoConfigById(repoId);
|
|
482
|
+
return {
|
|
483
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
catch (error) {
|
|
487
|
+
return {
|
|
488
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
489
|
+
isError: true,
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
server.registerTool("get_organization", {
|
|
494
|
+
description: `Get organization details including name and quota.`,
|
|
495
|
+
inputSchema: z.object({
|
|
496
|
+
orgId: z.string().describe("The organization ID"),
|
|
497
|
+
}),
|
|
498
|
+
}, async ({ orgId }) => {
|
|
499
|
+
try {
|
|
500
|
+
const client = getClient();
|
|
501
|
+
const result = await client.getOrganization(orgId);
|
|
502
|
+
return {
|
|
503
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
catch (error) {
|
|
507
|
+
return {
|
|
508
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
509
|
+
isError: true,
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
// ============================================================================
|
|
514
|
+
// SERVER STARTUP
|
|
515
|
+
// ============================================================================
|
|
516
|
+
async function main() {
|
|
517
|
+
const transport = new StdioServerTransport();
|
|
518
|
+
await server.connect(transport);
|
|
519
|
+
console.error("NoHack Security MCP Server running on stdio");
|
|
520
|
+
}
|
|
521
|
+
main().catch((error) => {
|
|
522
|
+
console.error("Fatal error:", error);
|
|
523
|
+
process.exit(1);
|
|
524
|
+
});
|
|
525
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nohacklabs/mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for NoHack security scanning API - query vulnerabilities, secrets, scans, and repo configs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"nohack-mcp": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"start": "node dist/index.js",
|
|
17
|
+
"dev": "tsc --watch",
|
|
18
|
+
"prepublishOnly": "npm run build",
|
|
19
|
+
"inspector": "npx @anthropic-ai/mcp-inspector node dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@modelcontextprotocol/sdk": "^1.11.0",
|
|
23
|
+
"zod": "^3.25.76"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^22.15.0",
|
|
27
|
+
"typescript": "^5.7.3"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"mcp",
|
|
34
|
+
"model-context-protocol",
|
|
35
|
+
"security",
|
|
36
|
+
"vulnerability",
|
|
37
|
+
"sast",
|
|
38
|
+
"scanning",
|
|
39
|
+
"nohack",
|
|
40
|
+
"ai",
|
|
41
|
+
"cursor",
|
|
42
|
+
"claude"
|
|
43
|
+
],
|
|
44
|
+
"author": "NoHack Labs",
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "https://github.com/nohacklabs/mcp-server"
|
|
49
|
+
},
|
|
50
|
+
"homepage": "https://nohacklabs.com",
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
}
|
|
54
|
+
}
|