mcp-license-audit 0.1.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 ADDED
@@ -0,0 +1,90 @@
1
+ # mcp-license-audit
2
+
3
+ MCP server that audits your project's dependency licenses for compatibility issues. Flags GPL/AGPL conflicts and generates compliance reports.
4
+
5
+ ## What It Does
6
+
7
+ - Parses a `package.json` file (dependencies + devDependencies)
8
+ - Fetches license info for each package from the npm registry
9
+ - Classifies licenses: permissive (MIT, Apache, BSD, ISC), copyleft (GPL, AGPL), weak-copyleft (LGPL, MPL), unknown
10
+ - Detects conflicts (e.g., GPL dependency in an MIT-licensed project)
11
+ - Returns a structured JSON report with risk level and summary
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm install -g mcp-license-audit
17
+ # or run directly:
18
+ npx mcp-license-audit
19
+ ```
20
+
21
+ ## Configure in Claude Code
22
+
23
+ Add to your `.claude/mcp.json` or `~/.claude/mcp.json`:
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "license-audit": {
29
+ "command": "npx",
30
+ "args": ["mcp-license-audit"]
31
+ }
32
+ }
33
+ }
34
+ ```
35
+
36
+ Or if installed globally:
37
+
38
+ ```json
39
+ {
40
+ "mcpServers": {
41
+ "license-audit": {
42
+ "command": "mcp-license-audit"
43
+ }
44
+ }
45
+ }
46
+ ```
47
+
48
+ ## Tool: `audit-licenses`
49
+
50
+ **Input:** `packageJson` — the full contents of a `package.json` file as a string.
51
+
52
+ **Output:** JSON report:
53
+
54
+ ```json
55
+ {
56
+ "totalDependencies": 15,
57
+ "analyzed": 15,
58
+ "licenses": {
59
+ "MIT": ["express", "lodash"],
60
+ "Apache-2.0": ["typescript"],
61
+ "GPL-3.0": ["some-package"],
62
+ "unknown": ["private-pkg"]
63
+ },
64
+ "conflicts": [
65
+ {
66
+ "package": "some-package",
67
+ "license": "GPL-3.0",
68
+ "issue": "GPL dependency in MIT project — must open-source your code if distributed"
69
+ }
70
+ ],
71
+ "riskLevel": "medium",
72
+ "summary": "15 deps analyzed. 1 GPL conflict found. 1 unknown license."
73
+ }
74
+ ```
75
+
76
+ **Risk levels:** `low` (no copyleft), `medium` (weak copyleft or many unknowns), `high` (GPL/AGPL found).
77
+
78
+ ## Limits
79
+
80
+ - Analyzes first 20 dependencies for speed
81
+ - Only supports npm packages (no pip/cargo/gem support yet)
82
+ - License data comes from the npm registry — private packages return "unknown"
83
+
84
+ ## Build from Source
85
+
86
+ ```bash
87
+ npm install
88
+ npm run build
89
+ node dist/index.js
90
+ ```
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,149 @@
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
+ import * as mcpcat from "mcpcat";
6
+ const PERMISSIVE = new Set(["MIT", "Apache-2.0", "BSD-2-Clause", "BSD-3-Clause", "ISC", "0BSD", "Unlicense", "CC0-1.0"]);
7
+ const COPYLEFT = new Set(["GPL-2.0", "GPL-3.0", "GPL-2.0-only", "GPL-3.0-only", "GPL-2.0-or-later", "GPL-3.0-or-later", "AGPL-3.0", "AGPL-3.0-only", "AGPL-3.0-or-later"]);
8
+ const WEAK_COPYLEFT = new Set(["LGPL-2.0", "LGPL-2.1", "LGPL-3.0", "LGPL-2.0-only", "LGPL-2.1-only", "LGPL-3.0-only", "MPL-2.0", "EUPL-1.1", "EUPL-1.2"]);
9
+ function categorize(license) {
10
+ if (!license || license === "UNKNOWN")
11
+ return "unknown";
12
+ const normalized = license.toUpperCase();
13
+ for (const l of PERMISSIVE)
14
+ if (l.toUpperCase() === normalized)
15
+ return "permissive";
16
+ for (const l of COPYLEFT)
17
+ if (l.toUpperCase() === normalized)
18
+ return "copyleft";
19
+ for (const l of WEAK_COPYLEFT)
20
+ if (l.toUpperCase() === normalized)
21
+ return "weak-copyleft";
22
+ if (normalized.includes("GPL") || normalized.includes("AGPL"))
23
+ return "copyleft";
24
+ if (normalized.includes("LGPL") || normalized.includes("MPL"))
25
+ return "weak-copyleft";
26
+ if (normalized.includes("MIT") || normalized.includes("BSD") || normalized.includes("APACHE") || normalized.includes("ISC"))
27
+ return "permissive";
28
+ return "unknown";
29
+ }
30
+ async function fetchLicense(pkg) {
31
+ try {
32
+ const res = await fetch(`https://registry.npmjs.org/${pkg}/latest`);
33
+ if (!res.ok)
34
+ return "unknown";
35
+ const data = await res.json();
36
+ if (!data.license)
37
+ return "unknown";
38
+ if (typeof data.license === "string")
39
+ return data.license;
40
+ if (typeof data.license === "object" && data.license.type)
41
+ return data.license.type;
42
+ return "unknown";
43
+ }
44
+ catch {
45
+ return "unknown";
46
+ }
47
+ }
48
+ const server = new McpServer({
49
+ name: "mcp-license-audit",
50
+ version: "0.1.0",
51
+ });
52
+ // MCPcat analytics
53
+ const MCPCAT_PROJECT_ID = process.env["MCPCAT_PROJECT_ID"] ?? undefined;
54
+ if (MCPCAT_PROJECT_ID)
55
+ mcpcat.track(server, MCPCAT_PROJECT_ID);
56
+ server.tool("audit-licenses", "Audit your project's dependency licenses for compatibility issues. Flags GPL/AGPL conflicts and generates compliance reports.", {
57
+ packageJson: z.string().describe("Contents of a package.json file as a string"),
58
+ }, async ({ packageJson }) => {
59
+ let parsed;
60
+ try {
61
+ parsed = JSON.parse(packageJson);
62
+ }
63
+ catch {
64
+ return {
65
+ content: [{ type: "text", text: JSON.stringify({ error: "Invalid package.json — could not parse JSON" }) }],
66
+ };
67
+ }
68
+ const deps = {
69
+ ...(parsed.dependencies ?? {}),
70
+ ...(parsed.devDependencies ?? {}),
71
+ };
72
+ const allPackages = Object.keys(deps);
73
+ const totalDependencies = allPackages.length;
74
+ const toAnalyze = allPackages.slice(0, 20);
75
+ const projectLicense = typeof parsed.license === "string" ? parsed.license : "unknown";
76
+ const projectCategory = categorize(projectLicense);
77
+ const licenseResults = await Promise.all(toAnalyze.map(async (pkg) => {
78
+ const license = await fetchLicense(pkg);
79
+ return { pkg, license, category: categorize(license) };
80
+ }));
81
+ const licenseMap = {};
82
+ for (const { pkg, license } of licenseResults) {
83
+ if (!licenseMap[license])
84
+ licenseMap[license] = [];
85
+ licenseMap[license].push(pkg);
86
+ }
87
+ const conflicts = [];
88
+ for (const { pkg, license, category } of licenseResults) {
89
+ if (category === "copyleft") {
90
+ const isSPDX = license.toUpperCase().includes("AGPL");
91
+ const licenseType = isSPDX ? "AGPL" : "GPL";
92
+ let issue = `${licenseType} dependency in ${projectLicense} project — must open-source your code if distributed`;
93
+ if (projectCategory === "permissive") {
94
+ conflicts.push({ package: pkg, license, issue });
95
+ }
96
+ else if (projectCategory === "unknown") {
97
+ conflicts.push({ package: pkg, license, issue: `${licenseType} dependency detected — verify compatibility with your project license` });
98
+ }
99
+ }
100
+ if (category === "weak-copyleft" && projectCategory === "permissive") {
101
+ conflicts.push({
102
+ package: pkg,
103
+ license,
104
+ issue: `Weak copyleft (${license}) — LGPL/MPL has linking requirements, review if you modify this library`,
105
+ });
106
+ }
107
+ }
108
+ const copyleftCount = licenseResults.filter((r) => r.category === "copyleft").length;
109
+ const weakCopyleftCount = licenseResults.filter((r) => r.category === "weak-copyleft").length;
110
+ const unknownCount = licenseResults.filter((r) => r.category === "unknown").length;
111
+ let riskLevel;
112
+ if (copyleftCount > 0) {
113
+ riskLevel = "high";
114
+ }
115
+ else if (weakCopyleftCount > 0 || unknownCount > 2) {
116
+ riskLevel = "medium";
117
+ }
118
+ else {
119
+ riskLevel = "low";
120
+ }
121
+ const parts = [`${toAnalyze.length} deps analyzed.`];
122
+ if (copyleftCount > 0)
123
+ parts.push(`${copyleftCount} GPL/AGPL conflict${copyleftCount > 1 ? "s" : ""} found.`);
124
+ if (weakCopyleftCount > 0)
125
+ parts.push(`${weakCopyleftCount} weak-copyleft dep${weakCopyleftCount > 1 ? "s" : ""}.`);
126
+ if (unknownCount > 0)
127
+ parts.push(`${unknownCount} unknown license${unknownCount > 1 ? "s" : ""}.`);
128
+ if (conflicts.length === 0)
129
+ parts.push("No conflicts found.");
130
+ const report = {
131
+ totalDependencies,
132
+ analyzed: toAnalyze.length,
133
+ licenses: licenseMap,
134
+ conflicts,
135
+ riskLevel,
136
+ summary: parts.join(" "),
137
+ };
138
+ return {
139
+ content: [{ type: "text", text: JSON.stringify(report, null, 2) }],
140
+ };
141
+ });
142
+ async function main() {
143
+ const transport = new StdioServerTransport();
144
+ await server.connect(transport);
145
+ }
146
+ main().catch((err) => {
147
+ process.stderr.write(`Fatal: ${err}\n`);
148
+ process.exit(1);
149
+ });
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "mcp-license-audit",
3
+ "version": "0.1.0",
4
+ "description": "MCP server that audits your project's dependency licenses for compatibility issues. Flags GPL/AGPL conflicts and generates compliance reports.",
5
+ "scripts": {
6
+ "build": "tsc",
7
+ "start": "node dist/index.js"
8
+ },
9
+ "files": ["dist"],
10
+ "keywords": ["mcp", "mcp-server", "license-audit", "compliance", "gpl", "dependency-check", "open-source", "license-checker"],
11
+ "author": "Timothy Cai",
12
+ "license": "MIT",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/linesatuni/agentic-waitlist.git",
16
+ "directory": "servers/mcp-license-audit"
17
+ },
18
+ "type": "module",
19
+ "bin": {
20
+ "mcp-license-audit": "./dist/index.js"
21
+ },
22
+ "dependencies": {
23
+ "@modelcontextprotocol/sdk": "^1.29.0",
24
+ "mcpcat": "^0.1.15",
25
+ "zod": "^4.3.6"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^25.5.2",
29
+ "typescript": "^6.0.2"
30
+ }
31
+ }