hound-mcp 0.2.0 → 0.2.2

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/dist/index.js CHANGED
@@ -6,45 +6,9 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
6
6
  // src/server.ts
7
7
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
8
 
9
- // src/prompts/index.ts
9
+ // src/prompts/package_evaluation.ts
10
10
  import { z } from "zod/v4";
11
- function registerPrompts(server) {
12
- server.registerPrompt(
13
- "security_audit",
14
- {
15
- description: "Run a full security audit on the current project's dependencies. Scans for vulnerabilities, license issues, and typosquat risks across your entire dependency tree.",
16
- argsSchema: {
17
- ecosystem: z.string().optional().describe("Package ecosystem (npm, pypi, cargo, etc). Auto-detected if omitted.")
18
- }
19
- },
20
- ({ ecosystem }) => {
21
- const ecoNote = ecosystem ? ` The project uses ${ecosystem}.` : "";
22
- return {
23
- messages: [
24
- {
25
- role: "user",
26
- content: {
27
- type: "text",
28
- text: `Please run a comprehensive security audit on this project's dependencies.${ecoNote}
29
-
30
- Follow these steps:
31
- 1. Use \`hound_popular\` to check the most commonly used packages in this ecosystem for known vulnerabilities \u2014 this gives a quick baseline.
32
- 2. For any specific packages you can identify in the project, use \`hound_vulns\` to check each one for CVEs and advisories.
33
- 3. Use \`hound_inspect\` on the 3-5 most critical dependencies to check their licenses, OpenSSF Scorecard, and GitHub health.
34
- 4. For any package names that look unusual or unfamiliar, use \`hound_typosquat\` to check for potential typosquatting.
35
- 5. If any vulnerabilities are found, use \`hound_advisories\` to get full details and fix guidance.
36
-
37
- Summarize findings as:
38
- - **Critical / High** vulnerabilities that need immediate attention
39
- - **License risks** (copyleft licenses, unknown licenses)
40
- - **Health concerns** (abandoned packages, low Scorecard scores)
41
- - **Recommended actions** with specific version upgrades where available`
42
- }
43
- }
44
- ]
45
- };
46
- }
47
- );
11
+ function packageEvaluationRegisterPrompt(server) {
48
12
  server.registerPrompt(
49
13
  "package_evaluation",
50
14
  {
@@ -80,12 +44,17 @@ Give me a clear **GO / NO-GO / CONDITIONAL** recommendation with reasoning. If c
80
44
  };
81
45
  }
82
46
  );
47
+ }
48
+
49
+ // src/prompts/pre_release_check.ts
50
+ import { z as z2 } from "zod/v4";
51
+ function preReleaseCheckRegisterPrompt(server) {
83
52
  server.registerPrompt(
84
53
  "pre_release_check",
85
54
  {
86
55
  description: "Run a pre-release dependency scan before shipping. Checks for vulnerabilities and license issues that could block a release.",
87
56
  argsSchema: {
88
- version: z.string().optional().describe("The version you are about to release, e.g. 1.2.0")
57
+ version: z2.string().optional().describe("The version you are about to release, e.g. 1.2.0")
89
58
  }
90
59
  },
91
60
  ({ version }) => {
@@ -119,8 +88,56 @@ End with a clear **SAFE TO RELEASE** or **BLOCKED \u2014 fix these issues first*
119
88
  );
120
89
  }
121
90
 
91
+ // src/prompts/security_audit.ts
92
+ import { z as z3 } from "zod/v4";
93
+ function securityAuditRegisterPrompt(server) {
94
+ server.registerPrompt(
95
+ "security_audit",
96
+ {
97
+ description: "Run a full security audit on the current project's dependencies. Scans for vulnerabilities, license issues, and typosquat risks across your entire dependency tree.",
98
+ argsSchema: {
99
+ ecosystem: z3.string().optional().describe("Package ecosystem (npm, pypi, cargo, etc). Auto-detected if omitted.")
100
+ }
101
+ },
102
+ ({ ecosystem }) => {
103
+ const ecoNote = ecosystem ? ` The project uses ${ecosystem}.` : "";
104
+ return {
105
+ messages: [
106
+ {
107
+ role: "user",
108
+ content: {
109
+ type: "text",
110
+ text: `Please run a comprehensive security audit on this project's dependencies.${ecoNote}
111
+
112
+ Follow these steps:
113
+ 1. Use \`hound_popular\` to check the most commonly used packages in this ecosystem for known vulnerabilities \u2014 this gives a quick baseline.
114
+ 2. For any specific packages you can identify in the project, use \`hound_vulns\` to check each one for CVEs and advisories.
115
+ 3. Use \`hound_inspect\` on the 3-5 most critical dependencies to check their licenses, OpenSSF Scorecard, and GitHub health.
116
+ 4. For any package names that look unusual or unfamiliar, use \`hound_typosquat\` to check for potential typosquatting.
117
+ 5. If any vulnerabilities are found, use \`hound_advisories\` to get full details and fix guidance.
118
+
119
+ Summarize findings as:
120
+ - **Critical / High** vulnerabilities that need immediate attention
121
+ - **License risks** (copyleft licenses, unknown licenses)
122
+ - **Health concerns** (abandoned packages, low Scorecard scores)
123
+ - **Recommended actions** with specific version upgrades where available`
124
+ }
125
+ }
126
+ ]
127
+ };
128
+ }
129
+ );
130
+ }
131
+
132
+ // src/prompts/index.ts
133
+ function registerPrompts(server) {
134
+ securityAuditRegisterPrompt(server);
135
+ packageEvaluationRegisterPrompt(server);
136
+ preReleaseCheckRegisterPrompt(server);
137
+ }
138
+
122
139
  // src/tools/advisories.ts
123
- import { z as z2 } from "zod/v4";
140
+ import { z as z4 } from "zod/v4";
124
141
 
125
142
  // src/api/depsdev.ts
126
143
  var BASE_URL = "https://api.deps.dev/v3";
@@ -307,7 +324,7 @@ function register(server) {
307
324
  {
308
325
  description: "Get full details for a security advisory by ID (GHSA, CVE, or OSV ID). Returns title, severity, affected versions, fix versions, and references.",
309
326
  inputSchema: {
310
- id: z2.string().describe("Advisory ID \u2014 e.g. GHSA-rv95-896h-c2vc, CVE-2024-29041, or any OSV ID")
327
+ id: z4.string().describe("Advisory ID \u2014 e.g. GHSA-rv95-896h-c2vc, CVE-2024-29041, or any OSV ID")
311
328
  }
312
329
  },
313
330
  async ({ id }) => {
@@ -402,7 +419,7 @@ function register(server) {
402
419
  }
403
420
 
404
421
  // src/tools/inspect.ts
405
- import { z as z3 } from "zod/v4";
422
+ import { z as z5 } from "zod/v4";
406
423
  var ECOSYSTEM_VALUES = ["npm", "pypi", "go", "maven", "cargo", "nuget", "rubygems"];
407
424
  function register2(server) {
408
425
  return server.registerTool(
@@ -410,9 +427,9 @@ function register2(server) {
410
427
  {
411
428
  description: "Get a comprehensive profile of a package version: licenses, vulnerabilities, OpenSSF scorecard, GitHub stats, and dependency count \u2014 all in one call.",
412
429
  inputSchema: {
413
- name: z3.string().describe("Package name"),
414
- version: z3.string().describe("Package version"),
415
- ecosystem: z3.enum(ECOSYSTEM_VALUES).default("npm").describe("Package ecosystem (default: npm)")
430
+ name: z5.string().describe("Package name"),
431
+ version: z5.string().describe("Package version"),
432
+ ecosystem: z5.enum(ECOSYSTEM_VALUES).default("npm").describe("Package ecosystem (default: npm)")
416
433
  }
417
434
  },
418
435
  async ({ name, version, ecosystem }) => {
@@ -504,7 +521,7 @@ function scorecardGrade(score) {
504
521
  }
505
522
 
506
523
  // src/tools/popular.ts
507
- import { z as z4 } from "zod/v4";
524
+ import { z as z6 } from "zod/v4";
508
525
  var ECOSYSTEM_VALUES2 = ["npm", "pypi", "go", "maven", "cargo", "nuget", "rubygems"];
509
526
  var POPULAR_DEFAULTS = {
510
527
  npm: [
@@ -561,8 +578,8 @@ function register3(server) {
561
578
  {
562
579
  description: "Scan a list of popular (or user-specified) packages for known vulnerabilities. Quickly surface which widely-used packages in an ecosystem have open security issues.",
563
580
  inputSchema: {
564
- ecosystem: z4.enum(ECOSYSTEM_VALUES2).default("npm").describe("Package ecosystem (default: npm)"),
565
- packages: z4.array(z4.string()).optional().describe(
581
+ ecosystem: z6.enum(ECOSYSTEM_VALUES2).default("npm").describe("Package ecosystem (default: npm)"),
582
+ packages: z6.array(z6.string()).optional().describe(
566
583
  "Specific package names to check. If omitted, uses a curated list of popular packages for the ecosystem."
567
584
  )
568
585
  }
@@ -640,7 +657,7 @@ function register3(server) {
640
657
  }
641
658
 
642
659
  // src/tools/tree.ts
643
- import { z as z5 } from "zod/v4";
660
+ import { z as z7 } from "zod/v4";
644
661
  var ECOSYSTEM_VALUES3 = ["npm", "pypi", "go", "maven", "cargo", "nuget", "rubygems"];
645
662
  function register4(server) {
646
663
  return server.registerTool(
@@ -648,10 +665,10 @@ function register4(server) {
648
665
  {
649
666
  description: "Show the full resolved dependency tree for a package version, including all transitive dependencies with their depth and relation type.",
650
667
  inputSchema: {
651
- name: z5.string().describe("Package name"),
652
- version: z5.string().describe("Package version"),
653
- ecosystem: z5.enum(ECOSYSTEM_VALUES3).default("npm").describe("Package ecosystem (default: npm)"),
654
- maxDepth: z5.number().int().min(1).max(10).default(3).describe("Maximum depth to display (default: 3, max: 10)")
668
+ name: z7.string().describe("Package name"),
669
+ version: z7.string().describe("Package version"),
670
+ ecosystem: z7.enum(ECOSYSTEM_VALUES3).default("npm").describe("Package ecosystem (default: npm)"),
671
+ maxDepth: z7.number().int().min(1).max(10).default(3).describe("Maximum depth to display (default: 3, max: 10)")
655
672
  }
656
673
  },
657
674
  async ({ name, version, ecosystem, maxDepth }) => {
@@ -670,8 +687,8 @@ function register4(server) {
670
687
  }
671
688
  const nodes = deps.nodes;
672
689
  const edges = deps.edges;
673
- const directCount = nodes.filter((n) => n.relationType === "DIRECT").length;
674
- const indirectCount = nodes.filter((n) => n.relationType === "INDIRECT").length;
690
+ const directCount = nodes.filter((n) => n.relation === "DIRECT").length;
691
+ const indirectCount = nodes.filter((n) => n.relation === "INDIRECT").length;
675
692
  const totalCount = directCount + indirectCount;
676
693
  const children = /* @__PURE__ */ new Map();
677
694
  for (const edge of edges) {
@@ -709,7 +726,7 @@ function renderNode(lines, nodes, children, nodeIndex, depth, maxDepth, visited)
709
726
  const isRoot = depth === 0;
710
727
  const prefix = isRoot ? "" : "\u251C\u2500\u2500 ";
711
728
  const errorSuffix = node.errors.length > 0 ? " \u26A0\uFE0F" : "";
712
- lines.push(`${indent}${prefix}${node.key.name}@${node.key.version}${errorSuffix}`);
729
+ lines.push(`${indent}${prefix}${node.versionKey.name}@${node.versionKey.version}${errorSuffix}`);
713
730
  if (depth >= maxDepth) {
714
731
  const childCount = children.get(nodeIndex)?.length ?? 0;
715
732
  if (childCount > 0) {
@@ -723,7 +740,7 @@ function renderNode(lines, nodes, children, nodeIndex, depth, maxDepth, visited)
723
740
  }
724
741
 
725
742
  // src/tools/typosquat.ts
726
- import { z as z6 } from "zod/v4";
743
+ import { z as z8 } from "zod/v4";
727
744
  var ECOSYSTEM_VALUES4 = ["npm", "pypi", "go", "maven", "cargo", "nuget", "rubygems"];
728
745
  function generateTypos(name) {
729
746
  const variants = /* @__PURE__ */ new Set();
@@ -756,8 +773,8 @@ function register5(server) {
756
773
  {
757
774
  description: "Check if a package name looks like a typosquat of a popular package. Generates likely typo variants and checks which ones exist in the registry.",
758
775
  inputSchema: {
759
- name: z6.string().describe("Package name to check"),
760
- ecosystem: z6.enum(ECOSYSTEM_VALUES4).default("npm").describe("Package ecosystem (default: npm)")
776
+ name: z8.string().describe("Package name to check"),
777
+ ecosystem: z8.enum(ECOSYSTEM_VALUES4).default("npm").describe("Package ecosystem (default: npm)")
761
778
  }
762
779
  },
763
780
  async ({ name, ecosystem }) => {
@@ -804,7 +821,7 @@ function register5(server) {
804
821
  }
805
822
 
806
823
  // src/tools/audit.ts
807
- import { z as z7 } from "zod/v4";
824
+ import { z as z9 } from "zod/v4";
808
825
 
809
826
  // src/parsers/index.ts
810
827
  function parseLockfile(filename, content) {
@@ -958,8 +975,8 @@ function register6(server) {
958
975
  {
959
976
  description: "Scan a project's lockfile for dependency risks. Parses package-lock.json, yarn.lock, pnpm-lock.yaml, requirements.txt, Cargo.lock, or go.sum and batch-queries OSV for vulnerabilities across all dependencies.",
960
977
  inputSchema: {
961
- lockfile_content: z7.string().describe("Full text content of the lockfile"),
962
- lockfile_name: z7.string().describe(
978
+ lockfile_content: z9.string().describe("Full text content of the lockfile"),
979
+ lockfile_name: z9.string().describe(
963
980
  "Filename to determine format: package-lock.json, yarn.lock, pnpm-lock.yaml, requirements.txt, Cargo.lock, go.sum"
964
981
  )
965
982
  }
@@ -1081,7 +1098,7 @@ ${icon} ${sev} (${group.length})`);
1081
1098
  }
1082
1099
 
1083
1100
  // src/tools/compare.ts
1084
- import { z as z8 } from "zod/v4";
1101
+ import { z as z10 } from "zod/v4";
1085
1102
  var ECOSYSTEM_VALUES5 = ["npm", "pypi", "go", "maven", "cargo", "nuget", "rubygems"];
1086
1103
  var COPYLEFT_LICENSES = /* @__PURE__ */ new Set([
1087
1104
  "GPL-2.0",
@@ -1162,9 +1179,9 @@ function register7(server) {
1162
1179
  {
1163
1180
  description: "Side-by-side comparison of two packages: vulnerabilities, OpenSSF Scorecard, GitHub stars, release recency, and license. Returns a recommendation.",
1164
1181
  inputSchema: {
1165
- package_a: z8.string().describe("First package name"),
1166
- package_b: z8.string().describe("Second package name"),
1167
- ecosystem: z8.enum(ECOSYSTEM_VALUES5).default("npm").describe("Package ecosystem (default: npm)")
1182
+ package_a: z10.string().describe("First package name"),
1183
+ package_b: z10.string().describe("Second package name"),
1184
+ ecosystem: z10.enum(ECOSYSTEM_VALUES5).default("npm").describe("Package ecosystem (default: npm)")
1168
1185
  }
1169
1186
  },
1170
1187
  async ({ package_a, package_b, ecosystem }) => {
@@ -1272,7 +1289,7 @@ function register7(server) {
1272
1289
  }
1273
1290
 
1274
1291
  // src/tools/license-check.ts
1275
- import { z as z9 } from "zod/v4";
1292
+ import { z as z11 } from "zod/v4";
1276
1293
  var COPYLEFT_LICENSES2 = /* @__PURE__ */ new Set([
1277
1294
  "GPL-2.0",
1278
1295
  "GPL-2.0-only",
@@ -1353,9 +1370,9 @@ function register8(server) {
1353
1370
  {
1354
1371
  description: "Scan a lockfile for license compliance. Resolves licenses for every dependency and flags packages that violate the chosen policy (permissive, copyleft, or none).",
1355
1372
  inputSchema: {
1356
- lockfile_content: z9.string().describe("Full text content of the lockfile"),
1357
- lockfile_name: z9.string().describe("Filename: package-lock.json, yarn.lock, pnpm-lock.yaml, requirements.txt, Cargo.lock, go.sum"),
1358
- policy: z9.enum(["permissive", "copyleft", "none"]).default("permissive").describe(
1373
+ lockfile_content: z11.string().describe("Full text content of the lockfile"),
1374
+ lockfile_name: z11.string().describe("Filename: package-lock.json, yarn.lock, pnpm-lock.yaml, requirements.txt, Cargo.lock, go.sum"),
1375
+ policy: z11.enum(["permissive", "copyleft", "none"]).default("permissive").describe(
1359
1376
  "License policy to enforce: 'permissive' (MIT/Apache/BSD only), 'copyleft' (allows GPL but not AGPL), 'none' (report only, no violations)"
1360
1377
  )
1361
1378
  }
@@ -1472,7 +1489,7 @@ Supported: package-lock.json, yarn.lock, pnpm-lock.yaml, requirements.txt, Cargo
1472
1489
  }
1473
1490
 
1474
1491
  // src/tools/preinstall.ts
1475
- import { z as z10 } from "zod/v4";
1492
+ import { z as z12 } from "zod/v4";
1476
1493
  var ECOSYSTEM_VALUES6 = ["npm", "pypi", "go", "maven", "cargo", "nuget", "rubygems"];
1477
1494
  var COPYLEFT_LICENSES3 = /* @__PURE__ */ new Set([
1478
1495
  "GPL-2.0",
@@ -1501,9 +1518,9 @@ function register9(server) {
1501
1518
  {
1502
1519
  description: "Safety check before installing a package. Checks known vulnerabilities, typosquatting risk, abandonment, and license concerns. Returns a go/no-go verdict.",
1503
1520
  inputSchema: {
1504
- name: z10.string().describe("Package name"),
1505
- version: z10.string().optional().describe("Package version (defaults to latest)"),
1506
- ecosystem: z10.enum(ECOSYSTEM_VALUES6).default("npm").describe("Package ecosystem (default: npm)")
1521
+ name: z12.string().describe("Package name"),
1522
+ version: z12.string().optional().describe("Package version (defaults to latest)"),
1523
+ ecosystem: z12.enum(ECOSYSTEM_VALUES6).default("npm").describe("Package ecosystem (default: npm)")
1507
1524
  }
1508
1525
  },
1509
1526
  async ({ name, version, ecosystem }) => {
@@ -1661,7 +1678,7 @@ function register9(server) {
1661
1678
  }
1662
1679
 
1663
1680
  // src/tools/score.ts
1664
- import { z as z11 } from "zod/v4";
1681
+ import { z as z13 } from "zod/v4";
1665
1682
  var ECOSYSTEM_VALUES7 = ["npm", "pypi", "go", "maven", "cargo", "nuget", "rubygems"];
1666
1683
  var COPYLEFT_LICENSES4 = /* @__PURE__ */ new Set([
1667
1684
  "GPL-2.0",
@@ -1711,9 +1728,9 @@ function register10(server) {
1711
1728
  {
1712
1729
  description: "Compute a 0-100 Hound Score for a package version combining vulnerability severity, OpenSSF Scorecard, release recency, and license risk. Returns a letter grade (A-F) with a breakdown.",
1713
1730
  inputSchema: {
1714
- name: z11.string().describe("Package name"),
1715
- version: z11.string().describe("Package version"),
1716
- ecosystem: z11.enum(ECOSYSTEM_VALUES7).default("npm").describe("Package ecosystem (default: npm)")
1731
+ name: z13.string().describe("Package name"),
1732
+ version: z13.string().describe("Package version"),
1733
+ ecosystem: z13.enum(ECOSYSTEM_VALUES7).default("npm").describe("Package ecosystem (default: npm)")
1717
1734
  }
1718
1735
  },
1719
1736
  async ({ name, version, ecosystem }) => {
@@ -1827,7 +1844,7 @@ function register10(server) {
1827
1844
  }
1828
1845
 
1829
1846
  // src/tools/upgrade.ts
1830
- import { z as z12 } from "zod/v4";
1847
+ import { z as z14 } from "zod/v4";
1831
1848
  var ECOSYSTEM_VALUES8 = ["npm", "pypi", "go", "maven", "cargo", "nuget", "rubygems"];
1832
1849
  function parseVersion(v) {
1833
1850
  return v.split(".").map((p) => parseInt(p.replace(/[^0-9]/g, ""), 10) || 0);
@@ -1847,9 +1864,9 @@ function register11(server) {
1847
1864
  {
1848
1865
  description: "Find the minimum version upgrade that resolves all known vulnerabilities for a package. Checks every published version and returns the nearest safe one.",
1849
1866
  inputSchema: {
1850
- name: z12.string().describe("Package name (e.g. express, lodash)"),
1851
- version: z12.string().describe("Current vulnerable version (e.g. 4.17.20)"),
1852
- ecosystem: z12.enum(ECOSYSTEM_VALUES8).default("npm").describe("Package ecosystem (default: npm)")
1867
+ name: z14.string().describe("Package name (e.g. express, lodash)"),
1868
+ version: z14.string().describe("Current vulnerable version (e.g. 4.17.20)"),
1869
+ ecosystem: z14.enum(ECOSYSTEM_VALUES8).default("npm").describe("Package ecosystem (default: npm)")
1853
1870
  }
1854
1871
  },
1855
1872
  async ({ name, version, ecosystem }) => {
@@ -1932,7 +1949,7 @@ This may be the latest version already.`
1932
1949
  }
1933
1950
 
1934
1951
  // src/tools/vulns.ts
1935
- import { z as z13 } from "zod/v4";
1952
+ import { z as z15 } from "zod/v4";
1936
1953
  var ECOSYSTEM_VALUES9 = ["npm", "pypi", "go", "maven", "cargo", "nuget", "rubygems"];
1937
1954
  var SEVERITY_ICON2 = {
1938
1955
  CRITICAL: "\u{1F534}",
@@ -1947,9 +1964,9 @@ function register12(server) {
1947
1964
  {
1948
1965
  description: "List all known vulnerabilities for a specific package version, grouped by severity with fix versions and advisory links.",
1949
1966
  inputSchema: {
1950
- name: z13.string().describe("Package name (e.g. express, lodash)"),
1951
- version: z13.string().describe("Package version (e.g. 4.18.2)"),
1952
- ecosystem: z13.enum(ECOSYSTEM_VALUES9).default("npm").describe("Package ecosystem (default: npm)")
1967
+ name: z15.string().describe("Package name (e.g. express, lodash)"),
1968
+ version: z15.string().describe("Package version (e.g. 4.18.2)"),
1969
+ ecosystem: z15.enum(ECOSYSTEM_VALUES9).default("npm").describe("Package ecosystem (default: npm)")
1953
1970
  }
1954
1971
  },
1955
1972
  async ({ name, version, ecosystem }) => {