@nuguardai/nuguard 0.5.7

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,89 @@
1
+ # @nuguardai/nuguard
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@nuguardai/nuguard)](https://www.npmjs.com/package/@nuguardai/nuguard)
4
+ [![PyPI version](https://img.shields.io/pypi/v/nuguard)](https://pypi.org/project/nuguard/)
5
+ [![License](https://img.shields.io/badge/license-Apache--2.0-blue)](https://github.com/NuGuardAI/nuguard/blob/main/LICENSE)
6
+
7
+ NuGuard is an open-source AI application security CLI. This npm package provides the `nuguard-mcp` launcher, which wires NuGuard as a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server so AI-native tools (Claude Desktop, VS Code, Cursor, etc.) can invoke NuGuard capabilities directly.
8
+
9
+ The underlying CLI is a Python package. This launcher automatically locates or installs it so you don't have to manage the Python environment manually.
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npx @nuguardai/nuguard
15
+ ```
16
+
17
+ Or install globally:
18
+
19
+ ```bash
20
+ npm install -g @nuguardai/nuguard
21
+ nuguard-mcp
22
+ ```
23
+
24
+ ## What the launcher does
25
+
26
+ `nuguard-mcp` tries the following in order, using whichever succeeds first:
27
+
28
+ 1. `nuguard-mcp` already on `PATH` (pip-installed globally or in an active venv)
29
+ 2. `python3 -m nuguard.mcp` (nuguard installed but scripts not on `PATH`)
30
+ 3. `uvx --from nuguard[mcp] nuguard-mcp` (uv available)
31
+ 4. `pip install nuguard[mcp]` then `python -m nuguard.mcp` (one-time bootstrap)
32
+
33
+ ## MCP configuration
34
+
35
+ ### Claude Desktop
36
+
37
+ Add to `claude_desktop_config.json`:
38
+
39
+ ```json
40
+ {
41
+ "mcpServers": {
42
+ "nuguard": {
43
+ "command": "npx",
44
+ "args": ["-y", "@nuguardai/nuguard"]
45
+ }
46
+ }
47
+ }
48
+ ```
49
+
50
+ ### VS Code (`.vscode/mcp.json`)
51
+
52
+ ```json
53
+ {
54
+ "servers": {
55
+ "nuguard": {
56
+ "command": "npx",
57
+ "args": ["-y", "@nuguardai/nuguard"]
58
+ }
59
+ }
60
+ }
61
+ ```
62
+
63
+ ## Requirements
64
+
65
+ - Node.js 18+
66
+ - Python 3.12+ (or `uv` for zero-setup bootstrap)
67
+
68
+ ## CLI capabilities
69
+
70
+ Once connected, the MCP server exposes NuGuard's full pipeline:
71
+
72
+ | Command | Description |
73
+ |---|---|
74
+ | `nuguard sbom` | Generate an AI Bill of Materials from source code |
75
+ | `nuguard analyze` | Static analysis of an AI-SBOM for security risks |
76
+ | `nuguard policy` | Validate a cognitive policy document against a scan |
77
+ | `nuguard behavior` | Behavioral testing against a live AI application |
78
+ | `nuguard redteam` | Adversarial red-teaming with scenario-driven attacks |
79
+
80
+ ## Links
81
+
82
+ - [GitHub](https://github.com/NuGuardAI/nuguard)
83
+ - [Documentation](https://nuguardai.github.io/nuguard/)
84
+ - [PyPI](https://pypi.org/project/nuguard/)
85
+ - [Plugin guide](https://nuguardai.github.io/nuguard/plugin-guide.html)
86
+
87
+ ## License
88
+
89
+ Apache-2.0
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Launcher for nuguard-mcp. Tries runners in order of least friction:
4
+ * 1. nuguard-mcp already on PATH (pip-installed globally or in active venv)
5
+ * 2. python/python3 -m nuguard.mcp (nuguard installed but scripts not on PATH)
6
+ * 3. uvx --from nuguard[mcp] nuguard-mcp (uv available)
7
+ * 4. pip install nuguard[mcp] then python -m nuguard.mcp (one-time setup)
8
+ */
9
+ "use strict";
10
+
11
+ const { spawn, spawnSync } = require("child_process");
12
+
13
+ function which(cmd) {
14
+ const isWin = process.platform === "win32";
15
+ const r = spawnSync(isWin ? "where" : "which", [cmd], { encoding: "utf8", stdio: "pipe" });
16
+ return r.status === 0 && r.stdout.trim().length > 0;
17
+ }
18
+
19
+ function pyHasNuguardMcp(python) {
20
+ const r = spawnSync(python, ["-c", "import nuguard.mcp"], { encoding: "utf8", stdio: "pipe" });
21
+ return r.status === 0;
22
+ }
23
+
24
+ function run(command, args) {
25
+ const child = spawn(command, [...args], { stdio: "inherit" });
26
+ child.on("error", (err) => {
27
+ process.stderr.write(`nuguard-mcp: failed to start '${command}': ${err.message}\n`);
28
+ process.exit(1);
29
+ });
30
+ child.on("exit", (code) => process.exit(code ?? 0));
31
+ }
32
+
33
+ const extra = process.argv.slice(2);
34
+
35
+ // 1. Already pip-installed and on PATH
36
+ if (which("nuguard-mcp")) {
37
+ return run("nuguard-mcp", extra);
38
+ }
39
+
40
+ // 2. Python with nuguard already installed
41
+ const python = which("python3") ? "python3" : which("python") ? "python" : null;
42
+ if (python && pyHasNuguardMcp(python)) {
43
+ return run(python, ["-m", "nuguard.mcp", ...extra]);
44
+ }
45
+
46
+ // 3. uvx
47
+ if (which("uvx")) {
48
+ return run("uvx", ["--from", "nuguard[mcp]", "nuguard-mcp", ...extra]);
49
+ }
50
+
51
+ // 4. pip install then run
52
+ if (python) {
53
+ process.stderr.write("nuguard-mcp: installing via pip (one-time setup)...\n");
54
+ const install = spawnSync(
55
+ python, ["-m", "pip", "install", "--quiet", "nuguard[mcp]"],
56
+ { stdio: "inherit" }
57
+ );
58
+ if (install.status === 0) {
59
+ return run(python, ["-m", "nuguard.mcp", ...extra]);
60
+ }
61
+ process.stderr.write("nuguard-mcp: pip install failed — try: pip install nuguard[mcp]\n");
62
+ process.exit(1);
63
+ }
64
+
65
+ process.stderr.write(
66
+ "nuguard-mcp: no suitable Python runtime found.\n" +
67
+ "Install via one of:\n" +
68
+ " pip install nuguard[mcp]\n" +
69
+ " uvx --from nuguard[mcp] nuguard-mcp\n" +
70
+ "See https://github.com/NuGuardAI/nuguard for details.\n"
71
+ );
72
+ process.exit(1);
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Tests for nuguard-mcp.js runner detection logic.
4
+ *
5
+ * Run with: node npm/bin/nuguard-mcp.test.js
6
+ *
7
+ * Uses Node's built-in assert module — no external test framework needed.
8
+ */
9
+ "use strict";
10
+
11
+ const assert = require("assert");
12
+ const { spawnSync } = require("child_process");
13
+ const path = require("path");
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Helpers
17
+ // ---------------------------------------------------------------------------
18
+
19
+ const WRAPPER = path.resolve(__dirname, "nuguard-mcp.js");
20
+
21
+ /**
22
+ * Run the wrapper with a mocked environment via a child node process.
23
+ * We inject a tiny shim that overrides `which`, `pyHasNuguardMcp`, and
24
+ * `spawnSync` (for pip install) before the real module logic runs.
25
+ *
26
+ * Returns { stdout, stderr, exitCode }.
27
+ */
28
+ function runWithMocks({ whichResults = {}, pyHasNuguard = false, pipOk = true } = {}) {
29
+ const shim = `
30
+ "use strict";
31
+ const cp = require("child_process");
32
+ const original_spawnSync = cp.spawnSync.bind(cp);
33
+
34
+ // Intercept spawnSync used by which() and pip install
35
+ cp.spawnSync = function(cmd, args, opts) {
36
+ const isWin = process.platform === "win32";
37
+ const whichCmd = isWin ? "where" : "which";
38
+
39
+ if (cmd === whichCmd) {
40
+ const target = args[0];
41
+ const found = ${JSON.stringify(whichResults)}[target] === true;
42
+ return { status: found ? 0 : 1, stdout: found ? target : "", stderr: "" };
43
+ }
44
+
45
+ // python -c "import nuguard.mcp"
46
+ if (args && args[0] === "-c" && args[1] === "import nuguard.mcp") {
47
+ return { status: ${pyHasNuguard ? 0 : 1} };
48
+ }
49
+
50
+ // pip install
51
+ if (args && args[0] === "-m" && args[1] === "pip") {
52
+ return { status: ${pipOk ? 0 : 1} };
53
+ }
54
+
55
+ return { status: 1, stdout: "", stderr: "" };
56
+ };
57
+
58
+ // Intercept spawn used by run() — capture what would be launched
59
+ const cp2 = require("child_process");
60
+ const original_spawn = cp2.spawn.bind(cp2);
61
+ cp2.spawn = function(cmd, args, opts) {
62
+ // Print what would be launched and exit cleanly
63
+ process.stdout.write(JSON.stringify({ cmd, args }) + "\\n");
64
+ return {
65
+ on: function(event, cb) {
66
+ if (event === "exit") cb(0);
67
+ if (event === "error") {}
68
+ return this;
69
+ }
70
+ };
71
+ };
72
+
73
+ require(${JSON.stringify(WRAPPER)});
74
+ `;
75
+
76
+ const result = spawnSync(process.execPath, ["--eval", shim], {
77
+ encoding: "utf8",
78
+ stdio: "pipe",
79
+ timeout: 5000,
80
+ });
81
+
82
+ let launched = null;
83
+ if (result.stdout && result.stdout.trim()) {
84
+ try { launched = JSON.parse(result.stdout.trim()); } catch (_) {}
85
+ }
86
+
87
+ return {
88
+ launched,
89
+ stderr: result.stderr || "",
90
+ exitCode: result.status,
91
+ };
92
+ }
93
+
94
+ // ---------------------------------------------------------------------------
95
+ // Tests
96
+ // ---------------------------------------------------------------------------
97
+
98
+ let passed = 0;
99
+ let failed = 0;
100
+
101
+ function test(name, fn) {
102
+ try {
103
+ fn();
104
+ console.log(` ✓ ${name}`);
105
+ passed++;
106
+ } catch (err) {
107
+ console.error(` ✗ ${name}`);
108
+ console.error(` ${err.message}`);
109
+ failed++;
110
+ }
111
+ }
112
+
113
+ console.log("\nnuguard-mcp.js runner detection\n");
114
+
115
+ // 1. nuguard-mcp already on PATH → run it directly
116
+ test("uses nuguard-mcp directly when it is on PATH", () => {
117
+ const { launched } = runWithMocks({ whichResults: { "nuguard-mcp": true } });
118
+ assert.ok(launched, "should have launched something");
119
+ assert.strictEqual(launched.cmd, "nuguard-mcp");
120
+ assert.deepStrictEqual(launched.args, []);
121
+ });
122
+
123
+ // 2. python3 with nuguard installed → python3 -m nuguard.mcp
124
+ test("uses python3 -m nuguard.mcp when nuguard-mcp not on PATH but python3 has it", () => {
125
+ const { launched } = runWithMocks({
126
+ whichResults: { python3: true },
127
+ pyHasNuguard: true,
128
+ });
129
+ assert.ok(launched);
130
+ assert.strictEqual(launched.cmd, "python3");
131
+ assert.ok(launched.args.includes("-m"), "should include -m flag");
132
+ assert.ok(launched.args.includes("nuguard.mcp"), "should target nuguard.mcp");
133
+ });
134
+
135
+ // 3. python (not python3) with nuguard installed
136
+ test("falls back to python (not python3) when python3 not available", () => {
137
+ const { launched } = runWithMocks({
138
+ whichResults: { python: true },
139
+ pyHasNuguard: true,
140
+ });
141
+ assert.ok(launched);
142
+ assert.strictEqual(launched.cmd, "python");
143
+ });
144
+
145
+ // 4. uvx available, nuguard not pip-installed
146
+ test("uses uvx when nuguard not pip-installed but uvx is available", () => {
147
+ const { launched } = runWithMocks({
148
+ whichResults: { uvx: true },
149
+ pyHasNuguard: false,
150
+ });
151
+ assert.ok(launched);
152
+ assert.strictEqual(launched.cmd, "uvx");
153
+ assert.ok(launched.args.includes("--from"));
154
+ assert.ok(launched.args.includes("nuguard[mcp]"));
155
+ assert.ok(launched.args.includes("nuguard-mcp"));
156
+ });
157
+
158
+ // 5. pip install fallback when python available but nuguard not installed
159
+ test("pip-installs nuguard then launches python -m nuguard.mcp when only python present", () => {
160
+ const { launched, stderr } = runWithMocks({
161
+ whichResults: { python3: true },
162
+ pyHasNuguard: false,
163
+ pipOk: true,
164
+ });
165
+ assert.ok(launched, "should launch after pip install");
166
+ assert.strictEqual(launched.cmd, "python3");
167
+ assert.ok(launched.args.includes("nuguard.mcp"));
168
+ assert.ok(stderr.includes("installing via pip"), "should mention pip install");
169
+ });
170
+
171
+ // 6. No Python, no uvx → exit 1 with helpful message
172
+ test("exits with code 1 and helpful message when no runtime found", () => {
173
+ const { launched, stderr, exitCode } = runWithMocks({
174
+ whichResults: {},
175
+ pyHasNuguard: false,
176
+ });
177
+ assert.strictEqual(launched, null, "should not launch anything");
178
+ assert.ok(stderr.includes("pip install") || stderr.includes("Python"), "stderr should contain install hint");
179
+ });
180
+
181
+ // 7. pip install fails → exit 1
182
+ test("exits with code 1 when pip install fails", () => {
183
+ const { launched } = runWithMocks({
184
+ whichResults: { python3: true },
185
+ pyHasNuguard: false,
186
+ pipOk: false,
187
+ });
188
+ assert.strictEqual(launched, null, "should not launch after failed pip install");
189
+ });
190
+
191
+ // 8. nuguard-mcp takes precedence over python + uvx
192
+ test("nuguard-mcp on PATH takes precedence over python and uvx", () => {
193
+ const { launched } = runWithMocks({
194
+ whichResults: { "nuguard-mcp": true, python3: true, uvx: true },
195
+ pyHasNuguard: true,
196
+ });
197
+ assert.ok(launched);
198
+ assert.strictEqual(launched.cmd, "nuguard-mcp", "nuguard-mcp should win over python/uvx");
199
+ });
200
+
201
+ // ---------------------------------------------------------------------------
202
+ // Summary
203
+ // ---------------------------------------------------------------------------
204
+
205
+ console.log(`\n${passed + failed} tests: ${passed} passed, ${failed} failed\n`);
206
+ process.exit(failed > 0 ? 1 : 0);
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@nuguardai/nuguard",
3
+ "version": "0.5.7",
4
+ "description": "AI Application Security — SBOM generation, static analysis, behavioral validation, and adversarial red-team testing for AI agents and LLM-powered applications.",
5
+ "keywords": [
6
+ "mcp",
7
+ "ai-security",
8
+ "sbom",
9
+ "red-team",
10
+ "llm",
11
+ "ai-agents"
12
+ ],
13
+ "homepage": "https://github.com/NuGuardAI/nuguard",
14
+ "bugs": {
15
+ "url": "https://github.com/NuGuardAI/nuguard/issues"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/NuGuardAI/nuguard.git"
20
+ },
21
+ "license": "Apache-2.0",
22
+ "author": "NuGuard <info@nuguard.ai>",
23
+ "bin": {
24
+ "nuguard-mcp": "bin/nuguard-mcp.js"
25
+ },
26
+ "files": [
27
+ "bin/",
28
+ "README.md"
29
+ ],
30
+ "engines": {
31
+ "node": ">=18"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public",
35
+ "registry": "https://registry.npmjs.org/"
36
+ }
37
+ }