curatedmcp 2.0.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.
Files changed (97) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +175 -0
  3. package/dist/audit/catalog.d.ts +5 -0
  4. package/dist/audit/catalog.d.ts.map +1 -0
  5. package/dist/audit/catalog.js +69 -0
  6. package/dist/audit/index.d.ts +10 -0
  7. package/dist/audit/index.d.ts.map +1 -0
  8. package/dist/audit/index.js +32 -0
  9. package/dist/audit/report.d.ts +6 -0
  10. package/dist/audit/report.d.ts.map +1 -0
  11. package/dist/audit/report.js +79 -0
  12. package/dist/audit/risk.d.ts +3 -0
  13. package/dist/audit/risk.d.ts.map +1 -0
  14. package/dist/audit/risk.js +68 -0
  15. package/dist/audit/scanner.d.ts +8 -0
  16. package/dist/audit/scanner.d.ts.map +1 -0
  17. package/dist/audit/scanner.js +69 -0
  18. package/dist/audit/types.d.ts +29 -0
  19. package/dist/audit/types.d.ts.map +1 -0
  20. package/dist/audit/types.js +2 -0
  21. package/dist/auth.d.ts +23 -0
  22. package/dist/auth.d.ts.map +1 -0
  23. package/dist/auth.js +52 -0
  24. package/dist/cli/add.d.ts +18 -0
  25. package/dist/cli/add.d.ts.map +1 -0
  26. package/dist/cli/add.js +114 -0
  27. package/dist/cli/audit.d.ts +2 -0
  28. package/dist/cli/audit.d.ts.map +1 -0
  29. package/dist/cli/audit.js +58 -0
  30. package/dist/cli/guard.d.ts +2 -0
  31. package/dist/cli/guard.d.ts.map +1 -0
  32. package/dist/cli/guard.js +58 -0
  33. package/dist/cli/init.d.ts +2 -0
  34. package/dist/cli/init.d.ts.map +1 -0
  35. package/dist/cli/init.js +44 -0
  36. package/dist/cli/list.d.ts +5 -0
  37. package/dist/cli/list.d.ts.map +1 -0
  38. package/dist/cli/list.js +33 -0
  39. package/dist/cli/login.d.ts +6 -0
  40. package/dist/cli/login.d.ts.map +1 -0
  41. package/dist/cli/login.js +43 -0
  42. package/dist/cli/remove.d.ts +6 -0
  43. package/dist/cli/remove.d.ts.map +1 -0
  44. package/dist/cli/remove.js +15 -0
  45. package/dist/cli/sync.d.ts +2 -0
  46. package/dist/cli/sync.d.ts.map +1 -0
  47. package/dist/cli/sync.js +104 -0
  48. package/dist/cli.d.ts +10 -0
  49. package/dist/cli.d.ts.map +1 -0
  50. package/dist/cli.js +132 -0
  51. package/dist/guard/broker.d.ts +62 -0
  52. package/dist/guard/broker.d.ts.map +1 -0
  53. package/dist/guard/broker.js +147 -0
  54. package/dist/guard/dashboard.d.ts +14 -0
  55. package/dist/guard/dashboard.d.ts.map +1 -0
  56. package/dist/guard/dashboard.js +428 -0
  57. package/dist/guard/default-policy.json +33 -0
  58. package/dist/guard/index.d.ts +20 -0
  59. package/dist/guard/index.d.ts.map +1 -0
  60. package/dist/guard/index.js +61 -0
  61. package/dist/guard/logger.d.ts +30 -0
  62. package/dist/guard/logger.d.ts.map +1 -0
  63. package/dist/guard/logger.js +118 -0
  64. package/dist/guard/policy.d.ts +19 -0
  65. package/dist/guard/policy.d.ts.map +1 -0
  66. package/dist/guard/policy.js +108 -0
  67. package/dist/guard/proxy.d.ts +29 -0
  68. package/dist/guard/proxy.d.ts.map +1 -0
  69. package/dist/guard/proxy.js +109 -0
  70. package/dist/guard/types.d.ts +70 -0
  71. package/dist/guard/types.d.ts.map +1 -0
  72. package/dist/guard/types.js +2 -0
  73. package/dist/index.d.ts +3 -0
  74. package/dist/index.d.ts.map +1 -0
  75. package/dist/index.js +259 -0
  76. package/dist/proxy.d.ts +122 -0
  77. package/dist/proxy.d.ts.map +1 -0
  78. package/dist/proxy.js +165 -0
  79. package/dist/stack.d.ts +45 -0
  80. package/dist/stack.d.ts.map +1 -0
  81. package/dist/stack.js +93 -0
  82. package/dist/telemetry.d.ts +15 -0
  83. package/dist/telemetry.d.ts.map +1 -0
  84. package/dist/telemetry.js +71 -0
  85. package/dist/tools/get-details.d.ts +14 -0
  86. package/dist/tools/get-details.d.ts.map +1 -0
  87. package/dist/tools/get-details.js +27 -0
  88. package/dist/tools/install.d.ts +2 -0
  89. package/dist/tools/install.d.ts.map +1 -0
  90. package/dist/tools/install.js +74 -0
  91. package/dist/tools/list-categories.d.ts +2 -0
  92. package/dist/tools/list-categories.d.ts.map +1 -0
  93. package/dist/tools/list-categories.js +13 -0
  94. package/dist/tools/search.d.ts +16 -0
  95. package/dist/tools/search.d.ts.map +1 -0
  96. package/dist/tools/search.js +17 -0
  97. package/package.json +78 -0
package/dist/cli.js ADDED
@@ -0,0 +1,132 @@
1
+ /**
2
+ * CLI dispatcher.
3
+ *
4
+ * `npx @curatedmcp/launcher` (no args) → MCP server mode (handled in index.ts)
5
+ * `npx @curatedmcp/launcher <subcommand>` → run that subcommand and exit
6
+ */
7
+ import { createRequire } from "module";
8
+ import { addToStack, parseEnvFlags } from "./cli/add.js";
9
+ import { removeFromStack } from "./cli/remove.js";
10
+ import { listStack } from "./cli/list.js";
11
+ import { runInit } from "./cli/init.js";
12
+ import { runAuditCommand } from "./cli/audit.js";
13
+ import { runGuardCommand } from "./cli/guard.js";
14
+ import { runLogin } from "./cli/login.js";
15
+ import { runSync } from "./cli/sync.js";
16
+ const require = createRequire(import.meta.url);
17
+ // Read version from package.json so it never drifts from the published version.
18
+ const VERSION = require("../package.json").version;
19
+ const HELP = `
20
+ curatedmcp v${VERSION}
21
+ The CuratedMCP agent — discover, run, audit, and govern every MCP server your AI tools use.
22
+
23
+ USAGE
24
+ npx curatedmcp [SUBCOMMAND] [OPTIONS]
25
+
26
+ SUBCOMMANDS
27
+ (no subcommand) Run as an MCP hub server over stdio (used by AI clients).
28
+ audit Scan this machine for MCP servers and flag security risks.
29
+ add <slug> Add a server from the CuratedMCP catalog to your stack.
30
+ remove <slug> Remove a server from your stack.
31
+ list List the servers currently in your stack.
32
+ guard -- <cmd> Run the action firewall in front of a downstream MCP server.
33
+ login [token] Sign in to your CuratedMCP account (enables sync & alerts).
34
+ sync [--team <slug>] Pull your team's MCP allow-list and report a local audit.
35
+ init Show the one-line config snippet to add the agent to your AI client.
36
+ --version, -v Print version and exit.
37
+ --help, -h Print this help.
38
+
39
+ EXAMPLES
40
+ npx curatedmcp audit
41
+ npx curatedmcp add github --env GITHUB_TOKEN=ghp_xxx
42
+ npx curatedmcp guard -- npx -y @modelcontextprotocol/server-filesystem /tmp
43
+ npx curatedmcp login
44
+ npx curatedmcp sync
45
+
46
+ CONFIG
47
+ Stack lives at ~/.curatedmcp/stack.json (hand-editable JSON).
48
+ Auth token at ~/.curatedmcp/auth.json. Set CURATOR_API_URL to override the API base
49
+ (default: https://www.curatedmcp.com).
50
+
51
+ LEARN MORE
52
+ https://curatedmcp.com/launcher
53
+ `.trim();
54
+ /**
55
+ * True if argv contains a CLI subcommand or top-level flag.
56
+ * False means run as an MCP server (the default mode used by AI clients).
57
+ *
58
+ * NOTE: We deliberately exclude `--no-telemetry` here because Telemetry
59
+ * already handles that flag in MCP server mode.
60
+ */
61
+ export function isCliInvocation(argv) {
62
+ const args = argv.slice(2);
63
+ if (args.length === 0)
64
+ return false;
65
+ // First non-flag arg is the subcommand
66
+ const first = args[0];
67
+ const SUBCOMMANDS = new Set([
68
+ "add",
69
+ "remove",
70
+ "list",
71
+ "init",
72
+ "audit",
73
+ "guard",
74
+ "login",
75
+ "sync",
76
+ "--help",
77
+ "-h",
78
+ "--version",
79
+ "-v",
80
+ ]);
81
+ return SUBCOMMANDS.has(first);
82
+ }
83
+ export async function runCli(argv) {
84
+ const args = argv.slice(2);
85
+ const [cmd, ...rest] = args;
86
+ switch (cmd) {
87
+ case "--version":
88
+ case "-v":
89
+ console.log(VERSION);
90
+ return 0;
91
+ case "--help":
92
+ case "-h":
93
+ console.log(HELP);
94
+ return 0;
95
+ case "init":
96
+ return runInit();
97
+ case "audit":
98
+ return runAuditCommand(rest);
99
+ case "guard":
100
+ return runGuardCommand(rest);
101
+ case "login":
102
+ return runLogin(rest);
103
+ case "sync":
104
+ return runSync(rest);
105
+ case "list":
106
+ return listStack();
107
+ case "add": {
108
+ const slug = rest.find((a) => !a.startsWith("-"));
109
+ if (!slug) {
110
+ console.error("Usage: launcher add <slug> [--env KEY=value ...]");
111
+ return 1;
112
+ }
113
+ const env = parseEnvFlags(rest);
114
+ const result = await addToStack(slug, { env, nonInteractive: false });
115
+ console.log(result.summary);
116
+ return 0;
117
+ }
118
+ case "remove": {
119
+ const slug = rest.find((a) => !a.startsWith("-"));
120
+ if (!slug) {
121
+ console.error("Usage: launcher remove <slug>");
122
+ return 1;
123
+ }
124
+ return removeFromStack(slug);
125
+ }
126
+ default:
127
+ console.error(`Unknown subcommand: ${cmd}\n`);
128
+ console.error(HELP);
129
+ return 1;
130
+ }
131
+ }
132
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1,62 @@
1
+ export interface BrokerConfig {
2
+ registryUrl: string;
3
+ registryKey: string;
4
+ registrySlug: string;
5
+ }
6
+ export interface VerifyResult {
7
+ allowed: boolean;
8
+ agentIdentityId?: string;
9
+ jitTokenId?: string;
10
+ scopes?: string[];
11
+ reason?: string;
12
+ }
13
+ /**
14
+ * CuratedBroker — thin HTTP client that connects Sentinel to the
15
+ * curatedmcp.com identity and audit layer.
16
+ *
17
+ * Configure via env vars:
18
+ * CURATED_REGISTRY_URL (default: https://curatedmcp.com)
19
+ * CURATED_REGISTRY_KEY required — cmcp_reg_... API key from dashboard
20
+ * CURATED_REGISTRY_SLUG required — your org slug
21
+ */
22
+ export declare class CuratedBroker {
23
+ private baseUrl;
24
+ private key;
25
+ private slug;
26
+ private agentIdentityId;
27
+ private jitTokens;
28
+ constructor(config: BrokerConfig);
29
+ static fromEnv(): CuratedBroker | null;
30
+ private url;
31
+ private headers;
32
+ /** Stable fingerprint derived from machine-level entropy stored in ~/.sentinel */
33
+ static machineFingerprint(): string;
34
+ /**
35
+ * Register this Sentinel instance with the org's registry.
36
+ * Idempotent — safe to call on every startup.
37
+ */
38
+ register(name?: string): Promise<string | null>;
39
+ /**
40
+ * Issue a JIT token scoped to a specific MCP server.
41
+ * Tokens are cached in-memory for their lifetime.
42
+ */
43
+ getJitToken(serverSlug: string): Promise<string | null>;
44
+ /**
45
+ * Verify a JIT token before executing a tool call.
46
+ * Returns { allowed: true } if the call should proceed.
47
+ */
48
+ verify(token: string, serverSlug: string, toolName: string): Promise<VerifyResult>;
49
+ /**
50
+ * Log a completed tool invocation. Fire-and-forget: errors are swallowed.
51
+ */
52
+ logInvocation(opts: {
53
+ serverSlug: string;
54
+ toolName: string;
55
+ args: Record<string, unknown>;
56
+ outcome: "ALLOWED" | "BLOCKED" | "RATE_LIMITED" | "ERROR";
57
+ blockReason?: string;
58
+ latencyMs?: number;
59
+ jitTokenId?: string;
60
+ }): void;
61
+ }
62
+ //# sourceMappingURL=broker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"broker.d.ts","sourceRoot":"","sources":["../../src/guard/broker.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,SAAS,CAA2D;gBAEhE,MAAM,EAAE,YAAY;IAMhC,MAAM,CAAC,OAAO,IAAI,aAAa,GAAG,IAAI;IAWtC,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,OAAO;IAOf,kFAAkF;IAClF,MAAM,CAAC,kBAAkB,IAAI,MAAM;IAKnC;;;OAGG;IACG,QAAQ,CAAC,IAAI,SAAa,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAoBzD;;;OAGG;IACG,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA6B7D;;;OAGG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAexF;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC9B,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,cAAc,GAAG,OAAO,CAAC;QAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,IAAI;CAoBT"}
@@ -0,0 +1,147 @@
1
+ import { createHash } from "crypto";
2
+ /**
3
+ * CuratedBroker — thin HTTP client that connects Sentinel to the
4
+ * curatedmcp.com identity and audit layer.
5
+ *
6
+ * Configure via env vars:
7
+ * CURATED_REGISTRY_URL (default: https://curatedmcp.com)
8
+ * CURATED_REGISTRY_KEY required — cmcp_reg_... API key from dashboard
9
+ * CURATED_REGISTRY_SLUG required — your org slug
10
+ */
11
+ export class CuratedBroker {
12
+ constructor(config) {
13
+ this.agentIdentityId = null;
14
+ this.jitTokens = new Map();
15
+ this.baseUrl = config.registryUrl.replace(/\/$/, "");
16
+ this.key = config.registryKey;
17
+ this.slug = config.registrySlug;
18
+ }
19
+ static fromEnv() {
20
+ const key = process.env.CURATED_REGISTRY_KEY;
21
+ const slug = process.env.CURATED_REGISTRY_SLUG;
22
+ if (!key || !slug)
23
+ return null;
24
+ return new CuratedBroker({
25
+ registryUrl: process.env.CURATED_REGISTRY_URL ?? "https://curatedmcp.com",
26
+ registryKey: key,
27
+ registrySlug: slug,
28
+ });
29
+ }
30
+ url(path) {
31
+ return `${this.baseUrl}/api/v1/registry/${this.slug}${path}`;
32
+ }
33
+ headers() {
34
+ return {
35
+ Authorization: `Bearer ${this.key}`,
36
+ "Content-Type": "application/json",
37
+ };
38
+ }
39
+ /** Stable fingerprint derived from machine-level entropy stored in ~/.sentinel */
40
+ static machineFingerprint() {
41
+ const machineId = process.env.CURATED_MACHINE_ID ?? `${process.platform}-${process.arch}`;
42
+ return createHash("sha256").update(machineId).digest("hex");
43
+ }
44
+ /**
45
+ * Register this Sentinel instance with the org's registry.
46
+ * Idempotent — safe to call on every startup.
47
+ */
48
+ async register(name = "Sentinel") {
49
+ try {
50
+ const res = await fetch(this.url("/identity"), {
51
+ method: "POST",
52
+ headers: this.headers(),
53
+ body: JSON.stringify({
54
+ fingerprint: CuratedBroker.machineFingerprint(),
55
+ name,
56
+ description: `Sentinel v${process.env.npm_package_version ?? "0.x"} on ${process.platform}`,
57
+ }),
58
+ });
59
+ if (!res.ok)
60
+ return null;
61
+ const data = (await res.json());
62
+ this.agentIdentityId = data.identity?.id ?? null;
63
+ return this.agentIdentityId;
64
+ }
65
+ catch {
66
+ return null;
67
+ }
68
+ }
69
+ /**
70
+ * Issue a JIT token scoped to a specific MCP server.
71
+ * Tokens are cached in-memory for their lifetime.
72
+ */
73
+ async getJitToken(serverSlug) {
74
+ const cached = this.jitTokens.get(serverSlug);
75
+ if (cached && cached.expiresAt > Date.now() + 60000)
76
+ return cached.token;
77
+ if (!this.agentIdentityId)
78
+ return null;
79
+ try {
80
+ const res = await fetch(this.url("/jit-token"), {
81
+ method: "POST",
82
+ headers: this.headers(),
83
+ body: JSON.stringify({
84
+ agentIdentityId: this.agentIdentityId,
85
+ serverSlug,
86
+ scopes: [],
87
+ }),
88
+ });
89
+ if (!res.ok)
90
+ return null;
91
+ const data = (await res.json());
92
+ if (!data.token)
93
+ return null;
94
+ this.jitTokens.set(serverSlug, {
95
+ token: data.token,
96
+ expiresAt: data.expiresAt ? new Date(data.expiresAt).getTime() : Date.now() + 3600000,
97
+ });
98
+ return data.token;
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ }
104
+ /**
105
+ * Verify a JIT token before executing a tool call.
106
+ * Returns { allowed: true } if the call should proceed.
107
+ */
108
+ async verify(token, serverSlug, toolName) {
109
+ try {
110
+ const res = await fetch(this.url("/jit-token/verify"), {
111
+ method: "POST",
112
+ headers: this.headers(),
113
+ body: JSON.stringify({ token, serverSlug, toolName }),
114
+ });
115
+ if (!res.ok)
116
+ return { allowed: false, reason: "broker_error" };
117
+ return (await res.json());
118
+ }
119
+ catch {
120
+ // Fail open when broker is unreachable — local policy still applies
121
+ return { allowed: true, reason: "broker_unreachable" };
122
+ }
123
+ }
124
+ /**
125
+ * Log a completed tool invocation. Fire-and-forget: errors are swallowed.
126
+ */
127
+ logInvocation(opts) {
128
+ const argsHash = createHash("sha256")
129
+ .update(JSON.stringify(opts.args))
130
+ .digest("hex");
131
+ fetch(this.url("/tool-invocations"), {
132
+ method: "POST",
133
+ headers: this.headers(),
134
+ body: JSON.stringify({
135
+ agentIdentityId: this.agentIdentityId,
136
+ jitTokenId: opts.jitTokenId,
137
+ serverSlug: opts.serverSlug,
138
+ toolName: opts.toolName,
139
+ argsHash,
140
+ outcome: opts.outcome,
141
+ blockReason: opts.blockReason,
142
+ latencyMs: opts.latencyMs,
143
+ }),
144
+ }).catch(() => undefined);
145
+ }
146
+ }
147
+ //# sourceMappingURL=broker.js.map
@@ -0,0 +1,14 @@
1
+ import { ActionLogger } from "./logger.js";
2
+ import { PolicyEngine } from "./policy.js";
3
+ export declare class Dashboard {
4
+ private app;
5
+ private actionLogger;
6
+ private policyEngine;
7
+ private port;
8
+ constructor(actionLogger: ActionLogger, policyEngine: PolicyEngine, port?: number);
9
+ private setupRoutes;
10
+ private getDashboardData;
11
+ private renderHTML;
12
+ start(): Promise<void>;
13
+ }
14
+ //# sourceMappingURL=dashboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../src/guard/dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAO3C,qBAAa,SAAS;IACpB,OAAO,CAAC,GAAG,CAA2B;IACtC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,IAAI,CAAS;gBAGnB,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,YAAY,EAC1B,IAAI,GAAE,MAAa;IAOrB,OAAO,CAAC,WAAW;IA0CnB,OAAO,CAAC,gBAAgB;IAoBxB,OAAO,CAAC,UAAU;IAsWZ,KAAK;CAUZ"}