@socketsecurity/mcp 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/build/index.js +269 -86
- package/package.json +13 -4
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ The `depscore` tool allows AI assistants to query the Socket API for dependency
|
|
|
20
20
|
|
|
21
21
|
### Getting an API key
|
|
22
22
|
|
|
23
|
-
To use the Socket MCP Server, you need to create an API key. You can do this by following [these steps](https://docs.socket.dev/reference/creating-and-managing-api-tokens).
|
|
23
|
+
To use the Socket MCP Server, you need to create an API key. You can do this by following [these steps](https://docs.socket.dev/reference/creating-and-managing-api-tokens). The only required permission scope is `packages:list`, which allows the MCP server to query package metadata for dependency scores.
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
### Usage with Claude Desktop
|
package/build/index.js
CHANGED
|
@@ -1,31 +1,65 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
4
5
|
import { z } from "zod";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
import { pino } from 'pino';
|
|
7
|
+
import readline from 'readline';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
import { dirname, join } from 'path';
|
|
10
|
+
import { readFileSync } from 'fs';
|
|
11
|
+
import { createServer } from 'http';
|
|
12
|
+
import { randomUUID } from 'crypto';
|
|
13
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = dirname(__filename);
|
|
16
|
+
// Extract version from package.json
|
|
17
|
+
const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf8'));
|
|
18
|
+
const VERSION = packageJson.version || "0.0.1";
|
|
19
|
+
// Configure pino logger
|
|
20
|
+
const logger = pino({
|
|
10
21
|
level: 'info',
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
22
|
+
transport: {
|
|
23
|
+
targets: [
|
|
24
|
+
{
|
|
25
|
+
target: 'pino/file',
|
|
26
|
+
options: { destination: '/tmp/socket-mcp-error.log' },
|
|
27
|
+
level: 'error'
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
target: 'pino/file',
|
|
31
|
+
options: { destination: '/tmp/socket-mcp.log' },
|
|
32
|
+
level: 'info'
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
16
36
|
});
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
37
|
+
// Socket API URL - use localhost when debugging is enabled, otherwise use production
|
|
38
|
+
const SOCKET_API_URL = process.env.SOCKET_DEBUG === 'true'
|
|
39
|
+
? "http://localhost:8866/v0/purl?alerts=false&compact=false&fixable=false&licenseattrib=false&licensedetails=false"
|
|
40
|
+
: "https://api.socket.dev/v0/purl?alerts=false&compact=false&fixable=false&licenseattrib=false&licensedetails=false";
|
|
41
|
+
// Function to get API key interactively (only for HTTP mode)
|
|
42
|
+
async function getApiKeyInteractively() {
|
|
43
|
+
const rl = readline.createInterface({
|
|
44
|
+
input: process.stdin,
|
|
45
|
+
output: process.stderr
|
|
46
|
+
});
|
|
47
|
+
const apiKey = await new Promise((resolve) => {
|
|
48
|
+
rl.question('Please enter your Socket API key: ', (answer) => {
|
|
49
|
+
rl.close();
|
|
50
|
+
resolve(answer);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
if (!apiKey) {
|
|
54
|
+
logger.error("No API key provided");
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
return apiKey;
|
|
22
58
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"authorization": `Bearer ${SOCKET_API_KEY}`
|
|
28
|
-
};
|
|
59
|
+
// Initialize API key
|
|
60
|
+
let SOCKET_API_KEY = process.env.SOCKET_API_KEY || "";
|
|
61
|
+
// Transport management
|
|
62
|
+
const transports = {};
|
|
29
63
|
// Create server instance
|
|
30
64
|
const server = new McpServer({
|
|
31
65
|
name: "socket",
|
|
@@ -34,34 +68,40 @@ const server = new McpServer({
|
|
|
34
68
|
capabilities: {
|
|
35
69
|
resources: {},
|
|
36
70
|
tools: {},
|
|
71
|
+
streaming: {}
|
|
37
72
|
},
|
|
38
73
|
});
|
|
39
|
-
server.tool("depscore", "Get the dependency score of
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
74
|
+
server.tool("depscore", "Get the dependency score of packages with the `depscore` tool from Socket. Use 'unknown' for version if not known. Use this tool to scan dependencies for their quality and security on existing code or when code is generated. Stop generating code and ask the user how to proceed when any of the scores are low. When checking dependencies, make sure to also check the imports in the code, not just the manifest files (pyproject.toml, package.json, etc).", {
|
|
75
|
+
packages: z.array(z.object({
|
|
76
|
+
ecosystem: z.string().describe("The package ecosystem (e.g., npm, pypi)").default("npm"),
|
|
77
|
+
depname: z.string().describe("The name of the dependency"),
|
|
78
|
+
version: z.string().describe("The version of the dependency, use 'unknown' if not known").default("unknown"),
|
|
79
|
+
})).describe("Array of packages to check"),
|
|
80
|
+
}, async ({ packages }) => {
|
|
81
|
+
logger.info(`Received request for ${packages.length} packages`);
|
|
82
|
+
// Build components array for the API request
|
|
83
|
+
const components = packages.map(pkg => {
|
|
84
|
+
const cleanedVersion = pkg.version.replace(/[\^~]/g, ''); // Remove ^ and ~ from version
|
|
85
|
+
let purl;
|
|
86
|
+
if (cleanedVersion === "1.0.0" || cleanedVersion === "unknown" || !cleanedVersion) {
|
|
87
|
+
purl = `pkg:${pkg.ecosystem}/${pkg.depname}`;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
logger.info(`Using version ${cleanedVersion} for ${pkg.depname}`);
|
|
91
|
+
purl = `pkg:${pkg.ecosystem}/${pkg.depname}@${cleanedVersion}`;
|
|
92
|
+
}
|
|
93
|
+
return { purl };
|
|
94
|
+
});
|
|
55
95
|
try {
|
|
56
|
-
// Make a POST request to the Socket API
|
|
96
|
+
// Make a POST request to the Socket API with all packages
|
|
57
97
|
const response = await fetch(SOCKET_API_URL, {
|
|
58
98
|
method: 'POST',
|
|
59
99
|
headers: SOCKET_HEADERS,
|
|
60
|
-
body: JSON.stringify({ components
|
|
100
|
+
body: JSON.stringify({ components })
|
|
61
101
|
});
|
|
62
102
|
const responseText = await response.text();
|
|
63
103
|
if (response.status !== 200) {
|
|
64
|
-
const errorMsg = `Error processing
|
|
104
|
+
const errorMsg = `Error processing packages: [${response.status}] ${responseText}`;
|
|
65
105
|
logger.error(errorMsg);
|
|
66
106
|
return {
|
|
67
107
|
content: [{ type: "text", text: errorMsg }],
|
|
@@ -69,7 +109,7 @@ server.tool("depscore", "Get the dependency score of a package with the `depscor
|
|
|
69
109
|
};
|
|
70
110
|
}
|
|
71
111
|
else if (!responseText.trim()) {
|
|
72
|
-
const errorMsg =
|
|
112
|
+
const errorMsg = `No packages were found.`;
|
|
73
113
|
logger.error(errorMsg);
|
|
74
114
|
return {
|
|
75
115
|
content: [{ type: "text", text: errorMsg }],
|
|
@@ -78,78 +118,221 @@ server.tool("depscore", "Get the dependency score of a package with the `depscor
|
|
|
78
118
|
}
|
|
79
119
|
try {
|
|
80
120
|
// Handle NDJSON (multiple JSON objects, one per line)
|
|
81
|
-
let
|
|
121
|
+
let results = [];
|
|
82
122
|
if ((response.headers.get('content-type') || '').includes('x-ndjson')) {
|
|
83
123
|
const jsonLines = responseText.split('\n')
|
|
84
124
|
.filter(line => line.trim())
|
|
85
125
|
.map(line => JSON.parse(line));
|
|
86
126
|
if (!jsonLines.length) {
|
|
87
|
-
const errorMsg = `No valid JSON objects found in NDJSON response
|
|
127
|
+
const errorMsg = `No valid JSON objects found in NDJSON response`;
|
|
88
128
|
return {
|
|
89
129
|
content: [{ type: "text", text: errorMsg }],
|
|
90
130
|
isError: true
|
|
91
131
|
};
|
|
92
132
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
type: "text",
|
|
108
|
-
text: `Dependency scores for ${purl}: ${scoreEntries}`
|
|
109
|
-
}
|
|
110
|
-
]
|
|
111
|
-
};
|
|
133
|
+
// Process each result
|
|
134
|
+
for (const jsonData of jsonLines) {
|
|
135
|
+
let purl = `pkg:${jsonData.type || 'unknown'}/${jsonData.name || 'unknown'}@${jsonData.version || 'unknown'}`;
|
|
136
|
+
if (jsonData.score && jsonData.score.overall !== undefined) {
|
|
137
|
+
const scoreEntries = Object.entries(jsonData.score)
|
|
138
|
+
.filter(([key]) => key !== "overall" && key !== "uuid")
|
|
139
|
+
.map(([key, value]) => `${key}: ${value}`)
|
|
140
|
+
.join(', ');
|
|
141
|
+
results.push(`${purl}: ${scoreEntries}`);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
results.push(`${purl}: No score found`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
112
147
|
}
|
|
113
148
|
else {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
149
|
+
const jsonData = JSON.parse(responseText);
|
|
150
|
+
let purl = `pkg:${jsonData.type || 'unknown'}/${jsonData.name || 'unknown'}@${jsonData.version || 'unknown'}`;
|
|
151
|
+
if (jsonData.score && jsonData.score.overall !== undefined) {
|
|
152
|
+
const scoreEntries = Object.entries(jsonData.score)
|
|
153
|
+
.filter(([key]) => key !== "overall" && key !== "uuid")
|
|
154
|
+
.map(([key, value]) => `${key}: ${value}`)
|
|
155
|
+
.join(', ');
|
|
156
|
+
results.push(`${purl}: ${scoreEntries}`);
|
|
157
|
+
}
|
|
122
158
|
}
|
|
159
|
+
return {
|
|
160
|
+
content: [
|
|
161
|
+
{
|
|
162
|
+
type: "text",
|
|
163
|
+
text: results.length > 0
|
|
164
|
+
? `Dependency scores:\n${results.join('\n')}`
|
|
165
|
+
: "No scores found for the provided packages"
|
|
166
|
+
}
|
|
167
|
+
]
|
|
168
|
+
};
|
|
123
169
|
}
|
|
124
170
|
catch (e) {
|
|
125
171
|
const error = e;
|
|
126
|
-
const errorMsg = `JSON parsing error
|
|
172
|
+
const errorMsg = `JSON parsing error: ${error.message} -- Response: ${responseText}`;
|
|
127
173
|
logger.error(errorMsg);
|
|
128
|
-
const llmResponse = `Package ${purl} not found.`;
|
|
129
174
|
return {
|
|
130
|
-
content: [{ type: "text", text:
|
|
175
|
+
content: [{ type: "text", text: "Error parsing response from Socket API" }],
|
|
131
176
|
isError: true
|
|
132
177
|
};
|
|
133
178
|
}
|
|
134
179
|
}
|
|
135
180
|
catch (e) {
|
|
136
181
|
const error = e;
|
|
137
|
-
const errorMsg = `Error processing
|
|
182
|
+
const errorMsg = `Error processing packages: ${error.message}`;
|
|
138
183
|
logger.error(errorMsg);
|
|
139
|
-
const llmResponse = `Package ${purl} not found.`;
|
|
140
184
|
return {
|
|
141
|
-
content: [{ type: "text", text:
|
|
185
|
+
content: [{ type: "text", text: "Error connecting to Socket API" }],
|
|
142
186
|
isError: true
|
|
143
187
|
};
|
|
144
188
|
}
|
|
145
189
|
});
|
|
146
|
-
//
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}
|
|
190
|
+
// Determine transport mode from environment or arguments
|
|
191
|
+
const useHttp = process.env.MCP_HTTP_MODE === 'true' || process.argv.includes('--http');
|
|
192
|
+
const port = parseInt(process.env.MCP_PORT || '3000', 10);
|
|
193
|
+
// Validate API key - in stdio mode, we can't prompt interactively
|
|
194
|
+
if (!SOCKET_API_KEY) {
|
|
195
|
+
if (useHttp) {
|
|
196
|
+
// In HTTP mode, we can prompt for the API key
|
|
197
|
+
logger.error("SOCKET_API_KEY environment variable is not set");
|
|
198
|
+
SOCKET_API_KEY = await getApiKeyInteractively();
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
// In stdio mode, we must have the API key as an environment variable
|
|
202
|
+
logger.error("SOCKET_API_KEY environment variable is required in stdio mode");
|
|
203
|
+
logger.error("Please set the SOCKET_API_KEY environment variable and try again");
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// Now that we have the API key, set up the headers
|
|
208
|
+
const SOCKET_HEADERS = {
|
|
209
|
+
"user-agent": `socket-mcp/${VERSION}`,
|
|
210
|
+
"accept": "application/x-ndjson",
|
|
211
|
+
"content-type": "application/json",
|
|
212
|
+
"authorization": `Bearer ${SOCKET_API_KEY}`
|
|
213
|
+
};
|
|
214
|
+
if (useHttp) {
|
|
215
|
+
// HTTP mode with Server-Sent Events
|
|
216
|
+
logger.info(`Starting HTTP server on port ${port}`);
|
|
217
|
+
const httpServer = createServer(async (req, res) => {
|
|
218
|
+
// Enable CORS
|
|
219
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
220
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
|
|
221
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, mcp-session-id');
|
|
222
|
+
if (req.method === 'OPTIONS') {
|
|
223
|
+
res.writeHead(200);
|
|
224
|
+
res.end();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const url = new URL(req.url, `http://localhost:${port}`);
|
|
228
|
+
if (url.pathname === '/mcp') {
|
|
229
|
+
if (req.method === 'POST') {
|
|
230
|
+
// Handle JSON-RPC messages
|
|
231
|
+
let body = '';
|
|
232
|
+
req.on('data', chunk => body += chunk);
|
|
233
|
+
req.on('end', async () => {
|
|
234
|
+
try {
|
|
235
|
+
const jsonData = JSON.parse(body);
|
|
236
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
237
|
+
let transport;
|
|
238
|
+
if (sessionId && transports[sessionId]) {
|
|
239
|
+
// Reuse existing transport
|
|
240
|
+
transport = transports[sessionId];
|
|
241
|
+
}
|
|
242
|
+
else if (!sessionId && isInitializeRequest(jsonData)) {
|
|
243
|
+
// New initialization request
|
|
244
|
+
transport = new StreamableHTTPServerTransport({
|
|
245
|
+
sessionIdGenerator: () => randomUUID(),
|
|
246
|
+
onsessioninitialized: (id) => {
|
|
247
|
+
transports[id] = transport;
|
|
248
|
+
logger.info(`Session initialized: ${id}`);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
transport.onclose = () => {
|
|
252
|
+
const sid = transport.sessionId;
|
|
253
|
+
if (sid && transports[sid]) {
|
|
254
|
+
delete transports[sid];
|
|
255
|
+
logger.info(`Session closed: ${sid}`);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
await server.connect(transport);
|
|
259
|
+
await transport.handleRequest(req, res, jsonData);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
// Invalid request
|
|
264
|
+
res.writeHead(400);
|
|
265
|
+
res.end(JSON.stringify({
|
|
266
|
+
jsonrpc: '2.0',
|
|
267
|
+
error: { code: -32000, message: 'Bad Request: No valid session ID' },
|
|
268
|
+
id: null
|
|
269
|
+
}));
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
// Handle request with existing transport
|
|
273
|
+
await transport.handleRequest(req, res, jsonData);
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
logger.error(`Error processing POST request: ${error}`);
|
|
277
|
+
if (!res.headersSent) {
|
|
278
|
+
res.writeHead(500);
|
|
279
|
+
res.end(JSON.stringify({
|
|
280
|
+
jsonrpc: '2.0',
|
|
281
|
+
error: { code: -32603, message: 'Internal server error' },
|
|
282
|
+
id: null
|
|
283
|
+
}));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
else if (req.method === 'GET') {
|
|
289
|
+
// Handle SSE streams
|
|
290
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
291
|
+
if (!sessionId || !transports[sessionId]) {
|
|
292
|
+
res.writeHead(400);
|
|
293
|
+
res.end('Invalid or missing session ID');
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
const transport = transports[sessionId];
|
|
297
|
+
await transport.handleRequest(req, res);
|
|
298
|
+
}
|
|
299
|
+
else if (req.method === 'DELETE') {
|
|
300
|
+
// Handle session termination
|
|
301
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
302
|
+
if (!sessionId || !transports[sessionId]) {
|
|
303
|
+
res.writeHead(400);
|
|
304
|
+
res.end('Invalid or missing session ID');
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const transport = transports[sessionId];
|
|
308
|
+
await transport.handleRequest(req, res);
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
res.writeHead(405);
|
|
312
|
+
res.end('Method not allowed');
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
res.writeHead(404);
|
|
317
|
+
res.end('Not found');
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
httpServer.listen(port, () => {
|
|
321
|
+
logger.info(`Socket MCP HTTP server version ${VERSION} started successfully on port ${port}`);
|
|
322
|
+
logger.info(`Connect to: http://localhost:${port}/mcp`);
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
// Stdio mode (default)
|
|
327
|
+
logger.info("Starting in stdio mode");
|
|
328
|
+
const transport = new StdioServerTransport();
|
|
329
|
+
server.connect(transport)
|
|
330
|
+
.then(() => {
|
|
331
|
+
logger.info(`Socket MCP server version ${VERSION} started successfully`);
|
|
332
|
+
})
|
|
333
|
+
.catch((error) => {
|
|
334
|
+
logger.error(`Failed to start Socket MCP server: ${error.message}`);
|
|
335
|
+
process.exit(1);
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@socketsecurity/mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"socket-mcp": "./build/index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"build": "tsc && chmod 755 ./build/index.js",
|
|
11
|
-
"
|
|
10
|
+
"build": "tsc && chmod 755 ./build/index.js && (chmod 755 ./build/mock-client/*.js 2>/dev/null || true)",
|
|
11
|
+
"debug:stdio": "node ./build/mock-client/debug-client.js",
|
|
12
|
+
"debug:sdk": "node ./build/mock-client/stdio-client.js",
|
|
13
|
+
"debug:http": "node ./build/mock-client/http-client.js",
|
|
14
|
+
"server:stdio": "SOCKET_API_KEY=${SOCKET_API_KEY} ./build/index.js",
|
|
15
|
+
"server:http": "MCP_HTTP_MODE=true SOCKET_API_KEY=${SOCKET_API_KEY} ./build/index.js"
|
|
12
16
|
},
|
|
13
17
|
"files": [
|
|
14
18
|
"build"
|
|
@@ -16,12 +20,17 @@
|
|
|
16
20
|
"keywords": [],
|
|
17
21
|
"author": "Alexandros Kapravelos",
|
|
18
22
|
"description": "Socket MCP server for scanning dependencies",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/SocketDev/socket-mcp"
|
|
26
|
+
},
|
|
19
27
|
"dependencies": {
|
|
20
28
|
"@modelcontextprotocol/sdk": "^1.11.3",
|
|
21
29
|
"@types/node-fetch": "^2.6.12",
|
|
22
30
|
"node-fetch": "^3.3.2",
|
|
31
|
+
"pino": "^9.7.0",
|
|
32
|
+
"pino-pretty": "^13.0.0",
|
|
23
33
|
"semver": "^7.7.2",
|
|
24
|
-
"winston": "^3.17.0",
|
|
25
34
|
"zod": "^3.24.4"
|
|
26
35
|
},
|
|
27
36
|
"devDependencies": {
|