Skip to content

Commit ea77de8

Browse files
committed
init
1 parent 3e72cde commit ea77de8

File tree

6 files changed

+2207
-0
lines changed

6 files changed

+2207
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules/

build/index.js

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3+
import { z } from "zod";
4+
import fetch from 'node-fetch';
5+
const SOCKET_API_URL = "https://api.socket.dev/v0/purl?alerts=false&compact=false&fixable=false&licenseattrib=false&licensedetails=false";
6+
const SOCKET_API_KEY = process.env.SOCKET_API_KEY || "";
7+
if (!SOCKET_API_KEY) {
8+
console.error("Error: SOCKET_API_KEY environment variable is not set");
9+
process.exit(1);
10+
}
11+
const SOCKET_HEADERS = {
12+
"accept": "application/x-ndjson",
13+
"content-type": "application/json",
14+
"authorization": `Bearer ${SOCKET_API_KEY}`
15+
};
16+
// Create server instance
17+
const server = new McpServer({
18+
name: "socket",
19+
version: "0.0.1",
20+
description: "Socket MCP server",
21+
capabilities: {
22+
resources: {},
23+
tools: {},
24+
},
25+
});
26+
server.tool("debscore", "Get the dependency score of a package with the `debscore` tool from Socket. Use 'unknown' for version if not known.", {
27+
ecosystem: z.string().describe("The package ecosystem (e.g., npm, pypi)").default("npm"),
28+
depname: z.string().describe("The name of the dependency"),
29+
version: z.string().describe("The version of the dependency, use 'unknown' if not known").default("unknown"),
30+
}, async ({ ecosystem, depname, version }) => {
31+
console.log(`Received request for ${depname} (${version}) in ${ecosystem} ecosystem`);
32+
// cleanup version
33+
let purl;
34+
const cleanedVersion = version.replace(/[\^~]/g, ''); // Remove ^ and ~ from version
35+
if (cleanedVersion === "1.0.0" || cleanedVersion === "unknown" || !cleanedVersion) {
36+
purl = `pkg:${ecosystem}/${depname}`;
37+
}
38+
else {
39+
console.log(`Using version ${cleanedVersion} for ${depname}`);
40+
purl = `pkg:${ecosystem}/${depname}@${cleanedVersion}`;
41+
}
42+
try {
43+
// Make a POST request to the Socket API
44+
const response = await fetch(SOCKET_API_URL, {
45+
method: 'POST',
46+
headers: SOCKET_HEADERS,
47+
body: JSON.stringify({ components: [{ purl }] })
48+
});
49+
const responseText = await response.text();
50+
if (response.status !== 200 || !responseText.trim()) {
51+
const errorMsg = `Error processing ${purl}`;
52+
return {
53+
content: [{ type: "text", text: errorMsg }],
54+
isError: true
55+
};
56+
}
57+
try {
58+
// Handle NDJSON (multiple JSON objects, one per line)
59+
let jsonData;
60+
if ((response.headers.get('content-type') || '').includes('x-ndjson')) {
61+
const jsonLines = responseText.split('\n')
62+
.filter(line => line.trim())
63+
.map(line => JSON.parse(line));
64+
if (!jsonLines.length) {
65+
const errorMsg = `No valid JSON objects found in NDJSON response for ${purl}`;
66+
return {
67+
content: [{ type: "text", text: errorMsg }],
68+
isError: true
69+
};
70+
}
71+
jsonData = jsonLines[0];
72+
}
73+
else {
74+
jsonData = JSON.parse(responseText);
75+
}
76+
if (jsonData.score && jsonData.score.overall !== undefined) {
77+
// Unroll the jsonData.score object into key-value pairs
78+
const scoreEntries = Object.entries(jsonData.score)
79+
.filter(([key]) => key !== "overall")
80+
.map(([key, value]) => `${key}: ${value}`)
81+
.join(', ');
82+
return {
83+
content: [
84+
{
85+
type: "text",
86+
text: `Dependency scores for ${purl}: ${scoreEntries}`
87+
}
88+
]
89+
};
90+
}
91+
else {
92+
return {
93+
content: [
94+
{
95+
type: "text",
96+
text: `No score found for ${purl}`
97+
}
98+
]
99+
};
100+
}
101+
}
102+
catch (e) {
103+
const error = e;
104+
const errorMsg = `JSON parsing error for ${purl}: ${error.message} -- Response: ${responseText}`;
105+
console.error(errorMsg);
106+
const llmResponse = `Package ${purl} not found.`;
107+
return {
108+
content: [{ type: "text", text: llmResponse }],
109+
isError: true
110+
};
111+
}
112+
}
113+
catch (e) {
114+
const error = e;
115+
const errorMsg = `Error processing ${purl}: ${error.message}`;
116+
console.error(errorMsg);
117+
const llmResponse = `Package ${purl} not found.`;
118+
return {
119+
content: [{ type: "text", text: llmResponse }],
120+
isError: true
121+
};
122+
}
123+
});
124+
// Create a stdio transport and start the server
125+
const transport = new StdioServerTransport();
126+
server.connect(transport)
127+
.then(() => {
128+
console.log("Socket MCP server started successfully");
129+
})
130+
.catch((error) => {
131+
console.error(`Failed to start Socket MCP server: ${error.message}`);
132+
process.exit(1);
133+
});

0 commit comments

Comments
 (0)