@mcp-audit-gateway/core 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.
Files changed (145) hide show
  1. package/.github/workflows/npm-publish.yml +21 -0
  2. package/CONTRIBUTING.md +41 -0
  3. package/LICENSE +21 -0
  4. package/README.md +156 -0
  5. package/dist/attestation/audit-log.d.ts +25 -0
  6. package/dist/attestation/audit-log.d.ts.map +1 -0
  7. package/dist/attestation/audit-log.js +95 -0
  8. package/dist/attestation/audit-log.js.map +1 -0
  9. package/dist/attestation/bugfix.test.d.ts +2 -0
  10. package/dist/attestation/bugfix.test.d.ts.map +1 -0
  11. package/dist/attestation/bugfix.test.js +103 -0
  12. package/dist/attestation/bugfix.test.js.map +1 -0
  13. package/dist/attestation/chain.test.d.ts +2 -0
  14. package/dist/attestation/chain.test.d.ts.map +1 -0
  15. package/dist/attestation/chain.test.js +153 -0
  16. package/dist/attestation/chain.test.js.map +1 -0
  17. package/dist/attestation/keygen.d.ts +5 -0
  18. package/dist/attestation/keygen.d.ts.map +1 -0
  19. package/dist/attestation/keygen.js +12 -0
  20. package/dist/attestation/keygen.js.map +1 -0
  21. package/dist/attestation/signer.d.ts +25 -0
  22. package/dist/attestation/signer.d.ts.map +1 -0
  23. package/dist/attestation/signer.js +93 -0
  24. package/dist/attestation/signer.js.map +1 -0
  25. package/dist/attestation/signer.test.d.ts +2 -0
  26. package/dist/attestation/signer.test.d.ts.map +1 -0
  27. package/dist/attestation/signer.test.js +62 -0
  28. package/dist/attestation/signer.test.js.map +1 -0
  29. package/dist/attestation/verify.d.ts +26 -0
  30. package/dist/attestation/verify.d.ts.map +1 -0
  31. package/dist/attestation/verify.js +104 -0
  32. package/dist/attestation/verify.js.map +1 -0
  33. package/dist/attestation/verify.test.d.ts +2 -0
  34. package/dist/attestation/verify.test.d.ts.map +1 -0
  35. package/dist/attestation/verify.test.js +79 -0
  36. package/dist/attestation/verify.test.js.map +1 -0
  37. package/dist/cli.d.ts +3 -0
  38. package/dist/cli.d.ts.map +1 -0
  39. package/dist/cli.js +364 -0
  40. package/dist/cli.js.map +1 -0
  41. package/dist/index.d.ts +12 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +12 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/integration.test.d.ts +2 -0
  46. package/dist/integration.test.d.ts.map +1 -0
  47. package/dist/integration.test.js +88 -0
  48. package/dist/integration.test.js.map +1 -0
  49. package/dist/policy/engine.d.ts +24 -0
  50. package/dist/policy/engine.d.ts.map +1 -0
  51. package/dist/policy/engine.js +130 -0
  52. package/dist/policy/engine.js.map +1 -0
  53. package/dist/policy/engine.test.d.ts +2 -0
  54. package/dist/policy/engine.test.d.ts.map +1 -0
  55. package/dist/policy/engine.test.js +80 -0
  56. package/dist/policy/engine.test.js.map +1 -0
  57. package/dist/proxy/gateway.d.ts +54 -0
  58. package/dist/proxy/gateway.d.ts.map +1 -0
  59. package/dist/proxy/gateway.js +245 -0
  60. package/dist/proxy/gateway.js.map +1 -0
  61. package/dist/proxy/gateway.test.d.ts +2 -0
  62. package/dist/proxy/gateway.test.d.ts.map +1 -0
  63. package/dist/proxy/gateway.test.js +93 -0
  64. package/dist/proxy/gateway.test.js.map +1 -0
  65. package/dist/proxy/mcp-server-adapter.d.ts +21 -0
  66. package/dist/proxy/mcp-server-adapter.d.ts.map +1 -0
  67. package/dist/proxy/mcp-server-adapter.js +98 -0
  68. package/dist/proxy/mcp-server-adapter.js.map +1 -0
  69. package/dist/proxy/mcp-server-adapter.test.d.ts +2 -0
  70. package/dist/proxy/mcp-server-adapter.test.d.ts.map +1 -0
  71. package/dist/proxy/mcp-server-adapter.test.js +160 -0
  72. package/dist/proxy/mcp-server-adapter.test.js.map +1 -0
  73. package/dist/proxy/upstream-manager.d.ts +49 -0
  74. package/dist/proxy/upstream-manager.d.ts.map +1 -0
  75. package/dist/proxy/upstream-manager.js +246 -0
  76. package/dist/proxy/upstream-manager.js.map +1 -0
  77. package/dist/proxy/upstream-manager.test.d.ts +2 -0
  78. package/dist/proxy/upstream-manager.test.d.ts.map +1 -0
  79. package/dist/proxy/upstream-manager.test.js +413 -0
  80. package/dist/proxy/upstream-manager.test.js.map +1 -0
  81. package/dist/telemetry/logger.d.ts +26 -0
  82. package/dist/telemetry/logger.d.ts.map +1 -0
  83. package/dist/telemetry/logger.js +48 -0
  84. package/dist/telemetry/logger.js.map +1 -0
  85. package/dist/telemetry/logger.test.d.ts +2 -0
  86. package/dist/telemetry/logger.test.d.ts.map +1 -0
  87. package/dist/telemetry/logger.test.js +124 -0
  88. package/dist/telemetry/logger.test.js.map +1 -0
  89. package/dist/telemetry/metrics.d.ts +41 -0
  90. package/dist/telemetry/metrics.d.ts.map +1 -0
  91. package/dist/telemetry/metrics.js +64 -0
  92. package/dist/telemetry/metrics.js.map +1 -0
  93. package/dist/telemetry/metrics.test.d.ts +2 -0
  94. package/dist/telemetry/metrics.test.d.ts.map +1 -0
  95. package/dist/telemetry/metrics.test.js +93 -0
  96. package/dist/telemetry/metrics.test.js.map +1 -0
  97. package/dist/telemetry/tracer.d.ts +16 -0
  98. package/dist/telemetry/tracer.d.ts.map +1 -0
  99. package/dist/telemetry/tracer.js +71 -0
  100. package/dist/telemetry/tracer.js.map +1 -0
  101. package/dist/types.d.ts +477 -0
  102. package/dist/types.d.ts.map +1 -0
  103. package/dist/types.js +68 -0
  104. package/dist/types.js.map +1 -0
  105. package/dist/wrap/proxy.d.ts +2 -0
  106. package/dist/wrap/proxy.d.ts.map +1 -0
  107. package/dist/wrap/proxy.js +152 -0
  108. package/dist/wrap/proxy.js.map +1 -0
  109. package/dist/wrap/proxy.test.d.ts +2 -0
  110. package/dist/wrap/proxy.test.d.ts.map +1 -0
  111. package/dist/wrap/proxy.test.js +155 -0
  112. package/dist/wrap/proxy.test.js.map +1 -0
  113. package/examples/README.md +33 -0
  114. package/examples/claude-desktop.json +50 -0
  115. package/examples/multi-agent-production.json +72 -0
  116. package/gateway.config.example.json +61 -0
  117. package/package.json +49 -0
  118. package/src/attestation/audit-log.ts +126 -0
  119. package/src/attestation/bugfix.test.ts +113 -0
  120. package/src/attestation/chain.test.ts +188 -0
  121. package/src/attestation/keygen.ts +18 -0
  122. package/src/attestation/signer.test.ts +75 -0
  123. package/src/attestation/signer.ts +105 -0
  124. package/src/attestation/verify.test.ts +86 -0
  125. package/src/attestation/verify.ts +131 -0
  126. package/src/cli.ts +407 -0
  127. package/src/index.ts +21 -0
  128. package/src/integration.test.ts +108 -0
  129. package/src/policy/engine.test.ts +95 -0
  130. package/src/policy/engine.ts +162 -0
  131. package/src/proxy/gateway.test.ts +108 -0
  132. package/src/proxy/gateway.ts +328 -0
  133. package/src/proxy/mcp-server-adapter.test.ts +197 -0
  134. package/src/proxy/mcp-server-adapter.ts +140 -0
  135. package/src/proxy/upstream-manager.test.ts +534 -0
  136. package/src/proxy/upstream-manager.ts +326 -0
  137. package/src/telemetry/logger.test.ts +139 -0
  138. package/src/telemetry/logger.ts +72 -0
  139. package/src/telemetry/metrics.test.ts +125 -0
  140. package/src/telemetry/metrics.ts +83 -0
  141. package/src/telemetry/tracer.ts +90 -0
  142. package/src/types.ts +113 -0
  143. package/src/wrap/proxy.test.ts +175 -0
  144. package/src/wrap/proxy.ts +192 -0
  145. package/tsconfig.json +18 -0
@@ -0,0 +1,152 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createInterface } from "node:readline";
3
+ import { appendFile, mkdir, readFile, stat } from "node:fs/promises";
4
+ import { createHmac, randomBytes, createHash } from "node:crypto";
5
+ import { homedir } from "node:os";
6
+ import { join } from "node:path";
7
+ export async function runWrapProxy(command, args) {
8
+ const auditDir = join(homedir(), ".mcp-audit");
9
+ await mkdir(auditDir, { recursive: true });
10
+ const keyPath = join(auditDir, "key.hex");
11
+ const logPath = join(auditDir, "audit.jsonl");
12
+ const secret = await ensureKey(keyPath);
13
+ let lastHash = await restoreLastHash(logPath);
14
+ let writeQueue = Promise.resolve();
15
+ const pending = new Map();
16
+ const child = spawn(command, args, {
17
+ stdio: ["pipe", "pipe", "inherit"],
18
+ env: process.env,
19
+ });
20
+ child.on("error", (err) => {
21
+ process.stderr.write(`[mcp-audit] failed to spawn: ${err.message}\n`);
22
+ process.exit(1);
23
+ });
24
+ child.on("exit", (code) => {
25
+ process.exit(code ?? 0);
26
+ });
27
+ const clientRl = createInterface({ input: process.stdin, crlfDelay: Infinity });
28
+ const serverRl = createInterface({ input: child.stdout, crlfDelay: Infinity });
29
+ clientRl.on("line", (line) => {
30
+ if (!line.trim())
31
+ return;
32
+ try {
33
+ const msg = JSON.parse(line);
34
+ if (msg.method === "tools/call" && msg.id !== undefined) {
35
+ pending.set(msg.id, {
36
+ toolName: msg.params?.name ?? "unknown",
37
+ args: msg.params?.arguments ?? {},
38
+ startTime: Date.now(),
39
+ });
40
+ }
41
+ }
42
+ catch { }
43
+ child.stdin.write(line + "\n");
44
+ });
45
+ serverRl.on("line", (line) => {
46
+ if (!line.trim())
47
+ return;
48
+ process.stdout.write(line + "\n");
49
+ let msg;
50
+ try {
51
+ msg = JSON.parse(line);
52
+ }
53
+ catch {
54
+ return;
55
+ }
56
+ if (msg.id !== undefined && pending.has(msg.id)) {
57
+ const call = pending.get(msg.id);
58
+ pending.delete(msg.id);
59
+ const durationMs = Date.now() - call.startTime;
60
+ const success = !msg.error;
61
+ writeQueue = writeQueue.then(async () => {
62
+ try {
63
+ const entry = createEntry(call.toolName, call.args, durationMs, success, lastHash);
64
+ const signed = sign(entry, secret);
65
+ lastHash = hashEntry(signed);
66
+ await appendFile(logPath, JSON.stringify(signed) + "\n");
67
+ }
68
+ catch (err) {
69
+ process.stderr.write(`[mcp-audit] log write failed: ${err}\n`);
70
+ }
71
+ });
72
+ }
73
+ });
74
+ clientRl.on("close", () => {
75
+ child.stdin.end();
76
+ });
77
+ process.on("SIGINT", () => {
78
+ child.kill("SIGINT");
79
+ });
80
+ process.on("SIGTERM", () => {
81
+ child.kill("SIGTERM");
82
+ });
83
+ }
84
+ function createEntry(toolName, args, durationMs, success, previousHash) {
85
+ return {
86
+ id: crypto.randomUUID(),
87
+ timestamp: new Date().toISOString(),
88
+ method: "tools/call",
89
+ toolName,
90
+ namespace: null,
91
+ upstream: null,
92
+ principal: null,
93
+ args,
94
+ durationMs,
95
+ success,
96
+ errorCode: null,
97
+ previousHash,
98
+ };
99
+ }
100
+ function sign(entry, secret) {
101
+ const canonical = JSON.stringify([
102
+ ["id", entry.id],
103
+ ["timestamp", entry.timestamp],
104
+ ["method", entry.method],
105
+ ["toolName", entry.toolName],
106
+ ["namespace", null],
107
+ ["upstream", null],
108
+ ["principal", null],
109
+ ["durationMs", entry.durationMs],
110
+ ["success", entry.success],
111
+ ["errorCode", null],
112
+ ["previousHash", entry.previousHash],
113
+ ]);
114
+ const hmac = createHmac("sha256", secret);
115
+ hmac.update(canonical);
116
+ const attestation = hmac.digest("hex");
117
+ return { ...entry, attestation };
118
+ }
119
+ function hashEntry(entry) {
120
+ return createHash("sha256").update(JSON.stringify(entry)).digest("hex");
121
+ }
122
+ async function ensureKey(keyPath) {
123
+ try {
124
+ const hex = await readFile(keyPath, "utf-8");
125
+ return Buffer.from(hex.trim(), "hex");
126
+ }
127
+ catch {
128
+ const key = randomBytes(32);
129
+ await appendFile(keyPath, key.toString("hex") + "\n");
130
+ process.stderr.write(`[mcp-audit] generated signing key: ${keyPath}\n`);
131
+ return key;
132
+ }
133
+ }
134
+ async function restoreLastHash(logPath) {
135
+ try {
136
+ const s = await stat(logPath);
137
+ if (s.size === 0)
138
+ return "genesis";
139
+ const content = await readFile(logPath, "utf-8");
140
+ const lines = content.trimEnd().split("\n");
141
+ for (let i = lines.length - 1; i >= 0; i--) {
142
+ try {
143
+ const entry = JSON.parse(lines[i]);
144
+ return hashEntry(entry);
145
+ }
146
+ catch { }
147
+ }
148
+ }
149
+ catch { }
150
+ return "genesis";
151
+ }
152
+ //# sourceMappingURL=proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../src/wrap/proxy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=proxy.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.test.d.ts","sourceRoot":"","sources":["../../src/wrap/proxy.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,155 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { spawn } from "node:child_process";
3
+ import { readFile, rm, mkdir } from "node:fs/promises";
4
+ import { join } from "node:path";
5
+ import { homedir } from "node:os";
6
+ const AUDIT_DIR = join(homedir(), ".mcp-audit");
7
+ const LOG_PATH = join(AUDIT_DIR, "audit.jsonl");
8
+ const CLI_PATH = join(import.meta.dirname, "../../dist/cli.js");
9
+ function sendJsonRpc(proc, msg) {
10
+ proc.stdin.write(JSON.stringify(msg) + "\n");
11
+ }
12
+ function collectLines(proc) {
13
+ return new Promise((resolve) => {
14
+ const lines = [];
15
+ proc.stdout.on("data", (chunk) => {
16
+ const parts = chunk.toString().split("\n").filter((l) => l.trim());
17
+ lines.push(...parts);
18
+ });
19
+ proc.on("close", () => resolve(lines));
20
+ });
21
+ }
22
+ describe("wrap proxy", () => {
23
+ let originalLog = null;
24
+ beforeEach(async () => {
25
+ await mkdir(AUDIT_DIR, { recursive: true });
26
+ try {
27
+ originalLog = await readFile(LOG_PATH, "utf-8");
28
+ }
29
+ catch {
30
+ originalLog = null;
31
+ }
32
+ });
33
+ afterEach(async () => {
34
+ if (originalLog !== null) {
35
+ const { writeFile } = await import("node:fs/promises");
36
+ await writeFile(LOG_PATH, originalLog);
37
+ }
38
+ else {
39
+ try {
40
+ await rm(LOG_PATH);
41
+ }
42
+ catch { }
43
+ }
44
+ });
45
+ it("forwards non-tool messages transparently", async () => {
46
+ const proc = spawn("node", [CLI_PATH, "wrap", "--", "node", "-e", `
47
+ process.stdin.setEncoding('utf-8');
48
+ let buf = '';
49
+ process.stdin.on('data', d => {
50
+ buf += d;
51
+ const lines = buf.split('\\n');
52
+ buf = lines.pop();
53
+ for (const line of lines) {
54
+ if (!line.trim()) continue;
55
+ const req = JSON.parse(line);
56
+ if (req.method === 'initialize') {
57
+ process.stdout.write(JSON.stringify({jsonrpc:'2.0',id:req.id,result:{capabilities:{tools:{}}}}) + '\\n');
58
+ } else if (req.method === 'resources/list') {
59
+ process.stdout.write(JSON.stringify({jsonrpc:'2.0',id:req.id,result:{resources:[]}}) + '\\n');
60
+ }
61
+ }
62
+ });
63
+ `], { stdio: ["pipe", "pipe", "pipe"] });
64
+ const lines = collectLines(proc);
65
+ sendJsonRpc(proc, { jsonrpc: "2.0", id: 1, method: "initialize", params: {} });
66
+ sendJsonRpc(proc, { jsonrpc: "2.0", id: 2, method: "resources/list", params: {} });
67
+ await new Promise((r) => setTimeout(r, 500));
68
+ proc.stdin.end();
69
+ const output = await lines;
70
+ const parsed = output.map((l) => JSON.parse(l));
71
+ expect(parsed).toHaveLength(2);
72
+ expect(parsed[0].id).toBe(1);
73
+ expect(parsed[0].result.capabilities).toBeDefined();
74
+ expect(parsed[1].id).toBe(2);
75
+ expect(parsed[1].result.resources).toBeDefined();
76
+ });
77
+ it("logs tool calls with attestation", async () => {
78
+ const proc = spawn("node", [CLI_PATH, "wrap", "--", "node", "-e", `
79
+ process.stdin.setEncoding('utf-8');
80
+ let buf = '';
81
+ process.stdin.on('data', d => {
82
+ buf += d;
83
+ const lines = buf.split('\\n');
84
+ buf = lines.pop();
85
+ for (const line of lines) {
86
+ if (!line.trim()) continue;
87
+ const req = JSON.parse(line);
88
+ if (req.method === 'tools/call') {
89
+ process.stdout.write(JSON.stringify({
90
+ jsonrpc:'2.0', id:req.id,
91
+ result:{content:[{type:'text',text:'done'}]}
92
+ }) + '\\n');
93
+ }
94
+ }
95
+ });
96
+ `], { stdio: ["pipe", "pipe", "pipe"] });
97
+ const lines = collectLines(proc);
98
+ sendJsonRpc(proc, {
99
+ jsonrpc: "2.0", id: 1, method: "tools/call",
100
+ params: { name: "fs/read_file", arguments: { path: "/tmp/test" } },
101
+ });
102
+ await new Promise((r) => setTimeout(r, 500));
103
+ proc.stdin.end();
104
+ const output = await lines;
105
+ expect(output.length).toBeGreaterThanOrEqual(1);
106
+ const response = JSON.parse(output[0]);
107
+ expect(response.result.content[0].text).toBe("done");
108
+ await new Promise((r) => setTimeout(r, 200));
109
+ const logContent = await readFile(LOG_PATH, "utf-8");
110
+ const logLines = logContent.trimEnd().split("\n");
111
+ const lastEntry = JSON.parse(logLines[logLines.length - 1]);
112
+ expect(lastEntry.toolName).toBe("fs/read_file");
113
+ expect(lastEntry.success).toBe(true);
114
+ expect(lastEntry.attestation).toBeDefined();
115
+ expect(lastEntry.attestation.length).toBe(64);
116
+ expect(lastEntry.previousHash).toBeDefined();
117
+ });
118
+ it("maintains hash chain across calls", async () => {
119
+ const proc = spawn("node", [CLI_PATH, "wrap", "--", "node", "-e", `
120
+ process.stdin.setEncoding('utf-8');
121
+ let buf = '';
122
+ process.stdin.on('data', d => {
123
+ buf += d;
124
+ const lines = buf.split('\\n');
125
+ buf = lines.pop();
126
+ for (const line of lines) {
127
+ if (!line.trim()) continue;
128
+ const req = JSON.parse(line);
129
+ if (req.method === 'tools/call') {
130
+ process.stdout.write(JSON.stringify({
131
+ jsonrpc:'2.0', id:req.id,
132
+ result:{content:[{type:'text',text:'ok'}]}
133
+ }) + '\\n');
134
+ }
135
+ }
136
+ });
137
+ `], { stdio: ["pipe", "pipe", "pipe"] });
138
+ collectLines(proc);
139
+ sendJsonRpc(proc, { jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "tool_a", arguments: {} } });
140
+ await new Promise((r) => setTimeout(r, 300));
141
+ sendJsonRpc(proc, { jsonrpc: "2.0", id: 2, method: "tools/call", params: { name: "tool_b", arguments: {} } });
142
+ await new Promise((r) => setTimeout(r, 300));
143
+ sendJsonRpc(proc, { jsonrpc: "2.0", id: 3, method: "tools/call", params: { name: "tool_c", arguments: {} } });
144
+ await new Promise((r) => setTimeout(r, 500));
145
+ proc.stdin.end();
146
+ await new Promise((r) => setTimeout(r, 200));
147
+ const logContent = await readFile(LOG_PATH, "utf-8");
148
+ const logLines = logContent.trimEnd().split("\n");
149
+ const entries = logLines.slice(-3).map((l) => JSON.parse(l));
150
+ expect(entries[0].previousHash).toBeDefined();
151
+ expect(entries[1].previousHash).not.toBe(entries[0].previousHash);
152
+ expect(entries[2].previousHash).not.toBe(entries[1].previousHash);
153
+ });
154
+ });
155
+ //# sourceMappingURL=proxy.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.test.js","sourceRoot":"","sources":["../../src/wrap/proxy.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ # Examples
2
+
3
+ ## claude-desktop.json
4
+
5
+ Minimal config for wrapping Claude Desktop's MCP servers with audit logging. Uses HMAC-SHA256 (no key file needed, just a secret). Denies write operations by default.
6
+
7
+ Add to your `claude_desktop_config.json`:
8
+
9
+ ```json
10
+ {
11
+ "mcpServers": {
12
+ "audited": {
13
+ "command": "node",
14
+ "args": ["/path/to/mcp-audit-gateway/dist/cli.js", "serve", "/path/to/examples/claude-desktop.json"],
15
+ "env": {
16
+ "GITHUB_TOKEN": "ghp_...",
17
+ "AUDIT_SECRET": "your-64-char-hex-secret"
18
+ }
19
+ }
20
+ }
21
+ }
22
+ ```
23
+
24
+ ## multi-agent-production.json
25
+
26
+ Production deployment with multiple upstream services, strict deny-by-default policy, per-principal rate limits, Ed25519 signing, and OTel export. Shows the typical setup for a team running multiple AI agents against shared infrastructure.
27
+
28
+ Key patterns demonstrated:
29
+ - Default deny with explicit allow rules per agent identity
30
+ - Rate limiting on destructive operations (deploy)
31
+ - Separate namespaces isolating database, deploy, and monitoring tools
32
+ - OTel export to a collector for centralized observability
33
+ - Large rotation threshold for high-volume environments
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "claude-desktop-gateway",
3
+ "version": "0.1.0",
4
+ "listen": {
5
+ "transport": "stdio"
6
+ },
7
+ "upstreams": [
8
+ {
9
+ "name": "filesystem",
10
+ "namespace": "fs",
11
+ "transport": {
12
+ "type": "stdio",
13
+ "command": "npx",
14
+ "args": ["@modelcontextprotocol/server-filesystem", "/Users/you/projects"]
15
+ }
16
+ },
17
+ {
18
+ "name": "github",
19
+ "namespace": "gh",
20
+ "transport": {
21
+ "type": "stdio",
22
+ "command": "npx",
23
+ "args": ["@modelcontextprotocol/server-github"],
24
+ "env": {
25
+ "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
26
+ }
27
+ }
28
+ }
29
+ ],
30
+ "policy": {
31
+ "defaultEffect": "allow",
32
+ "rules": [
33
+ {
34
+ "effect": "deny",
35
+ "tools": ["fs/write_file", "fs/create_directory"],
36
+ "principals": ["*"]
37
+ }
38
+ ]
39
+ },
40
+ "attestation": {
41
+ "enabled": true,
42
+ "algorithm": "hmac-sha256",
43
+ "secret": "${AUDIT_SECRET}"
44
+ },
45
+ "auditLog": {
46
+ "enabled": true,
47
+ "path": "~/.mcp-audit/claude-desktop.jsonl",
48
+ "rotateAfterMb": 50
49
+ }
50
+ }
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "production-gateway",
3
+ "version": "0.1.0",
4
+ "listen": {
5
+ "transport": "streamable-http",
6
+ "port": 3100,
7
+ "host": "0.0.0.0"
8
+ },
9
+ "upstreams": [
10
+ {
11
+ "name": "database",
12
+ "namespace": "db",
13
+ "transport": {
14
+ "type": "streamable-http",
15
+ "url": "http://db-mcp-server:8080/mcp"
16
+ }
17
+ },
18
+ {
19
+ "name": "deploy-tools",
20
+ "namespace": "deploy",
21
+ "transport": {
22
+ "type": "streamable-http",
23
+ "url": "http://deploy-mcp-server:8080/mcp"
24
+ }
25
+ },
26
+ {
27
+ "name": "monitoring",
28
+ "namespace": "mon",
29
+ "transport": {
30
+ "type": "streamable-http",
31
+ "url": "http://monitoring-mcp-server:8080/mcp"
32
+ }
33
+ }
34
+ ],
35
+ "policy": {
36
+ "defaultEffect": "deny",
37
+ "rules": [
38
+ {
39
+ "effect": "allow",
40
+ "principals": ["agent:read-*"],
41
+ "tools": ["db/query", "mon/*"]
42
+ },
43
+ {
44
+ "effect": "allow",
45
+ "principals": ["agent:deploy-bot"],
46
+ "tools": ["deploy/*"],
47
+ "rateLimit": { "maxPerMinute": 5, "maxPerHour": 20 }
48
+ },
49
+ {
50
+ "effect": "allow",
51
+ "principals": ["agent:admin"],
52
+ "rateLimit": { "maxPerMinute": 60, "maxPerHour": 500 }
53
+ }
54
+ ]
55
+ },
56
+ "attestation": {
57
+ "enabled": true,
58
+ "algorithm": "ed25519",
59
+ "keyPath": "/etc/mcp-gateway/signing.key"
60
+ },
61
+ "telemetry": {
62
+ "enabled": true,
63
+ "serviceName": "mcp-audit-gateway",
64
+ "exporterEndpoint": "http://otel-collector:4318/v1/traces",
65
+ "sampleRate": 1.0
66
+ },
67
+ "auditLog": {
68
+ "enabled": true,
69
+ "path": "/var/log/mcp-audit/production.jsonl",
70
+ "rotateAfterMb": 500
71
+ }
72
+ }
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "mcp-audit-gateway",
3
+ "version": "0.1.0",
4
+ "listen": {
5
+ "transport": "streamable-http",
6
+ "port": 3100,
7
+ "host": "127.0.0.1"
8
+ },
9
+ "upstreams": [
10
+ {
11
+ "name": "filesystem-server",
12
+ "namespace": "fs",
13
+ "transport": {
14
+ "type": "stdio",
15
+ "command": "npx",
16
+ "args": ["@modelcontextprotocol/server-filesystem", "/tmp"]
17
+ }
18
+ },
19
+ {
20
+ "name": "github-server",
21
+ "namespace": "github",
22
+ "transport": {
23
+ "type": "streamable-http",
24
+ "url": "http://localhost:3001/mcp"
25
+ }
26
+ }
27
+ ],
28
+ "policy": {
29
+ "defaultEffect": "allow",
30
+ "rules": [
31
+ {
32
+ "effect": "deny",
33
+ "principals": ["agent:readonly-*"],
34
+ "tools": ["*/write_*", "*/delete_*", "*/create_*"]
35
+ },
36
+ {
37
+ "effect": "allow",
38
+ "principals": ["agent:admin"],
39
+ "rateLimit": {
40
+ "maxPerMinute": 60,
41
+ "maxPerHour": 1000
42
+ }
43
+ }
44
+ ]
45
+ },
46
+ "attestation": {
47
+ "enabled": true,
48
+ "algorithm": "ed25519"
49
+ },
50
+ "telemetry": {
51
+ "enabled": true,
52
+ "serviceName": "mcp-audit-gateway",
53
+ "exporterEndpoint": "http://localhost:4318/v1/traces",
54
+ "sampleRate": 1.0
55
+ },
56
+ "auditLog": {
57
+ "enabled": true,
58
+ "path": "./audit.jsonl",
59
+ "rotateAfterMb": 100
60
+ }
61
+ }
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@mcp-audit-gateway/core",
3
+ "version": "0.1.0",
4
+ "description": "MCP audit and attestation gateway — transparent proxy with cryptographic attestation, policy enforcement, and OTel export",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "mcp-audit": "dist/cli.js",
10
+ "mcp-audit-gateway": "dist/cli.js"
11
+ },
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "dev": "tsx watch src/cli.ts",
15
+ "start": "node dist/cli.js",
16
+ "test": "vitest",
17
+ "lint": "eslint src/"
18
+ },
19
+ "keywords": [
20
+ "mcp",
21
+ "model-context-protocol",
22
+ "gateway",
23
+ "audit",
24
+ "attestation",
25
+ "opentelemetry",
26
+ "proxy"
27
+ ],
28
+ "author": "Elankumaran Srinivasan",
29
+ "license": "MIT",
30
+ "dependencies": {
31
+ "@modelcontextprotocol/sdk": "^1.12.0",
32
+ "@opentelemetry/api": "^1.9.0",
33
+ "@opentelemetry/sdk-node": "^0.57.0",
34
+ "@opentelemetry/sdk-trace-node": "^1.30.0",
35
+ "@opentelemetry/exporter-trace-otlp-http": "^0.57.0",
36
+ "@opentelemetry/semantic-conventions": "^1.30.0",
37
+ "@noble/ed25519": "^2.1.0",
38
+ "zod": "^3.23.0"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^22.0.0",
42
+ "tsx": "^4.19.0",
43
+ "typescript": "^5.6.0",
44
+ "vitest": "^2.1.0"
45
+ },
46
+ "engines": {
47
+ "node": ">=20.0.0"
48
+ }
49
+ }