@vengtoo/mcp-gateway 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.
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.startGateway = void 0;
4
+ const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
5
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
6
+ const index_js_2 = require("@modelcontextprotocol/sdk/client/index.js");
7
+ const stdio_js_2 = require("@modelcontextprotocol/sdk/client/stdio.js");
8
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
9
+ const authorize_1 = require("./authorize");
10
+ const audit_1 = require("./audit");
11
+ const sync_1 = require("./sync");
12
+ async function startGateway(config) {
13
+ process.on("unhandledRejection", (err) => {
14
+ console.error("[vengtoo-gateway] unhandled rejection:", err);
15
+ });
16
+ const downstreams = await spawnDownstreams(config.servers);
17
+ const toolIndex = buildToolIndex(downstreams);
18
+ const audit = new audit_1.AuditForwarder(config);
19
+ let blockedTools = new Set();
20
+ (0, sync_1.syncAllServers)(config, downstreams)
21
+ .then((blocked) => { blockedTools = blocked; })
22
+ .catch((err) => console.error("[vengtoo-gateway] tool sync warning:", err instanceof Error ? err.message : err));
23
+ const shutdown = async () => {
24
+ for (const ds of downstreams) {
25
+ try {
26
+ await ds.transport.close();
27
+ }
28
+ catch { /* best effort */ }
29
+ }
30
+ await audit.shutdown();
31
+ process.exit(0);
32
+ };
33
+ process.on("SIGINT", shutdown);
34
+ process.on("SIGTERM", shutdown);
35
+ const server = new index_js_1.Server({ name: "vengtoo-mcp-gateway", version: "0.1.0" }, { capabilities: { tools: {} } });
36
+ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
37
+ const tools = [];
38
+ for (const ds of downstreams) {
39
+ for (const tool of ds.tools) {
40
+ tools.push({
41
+ name: qualifiedName(ds.name, tool.name),
42
+ description: `[${ds.name}] ${tool.description ?? ""}`.trim(),
43
+ inputSchema: tool.inputSchema,
44
+ });
45
+ }
46
+ }
47
+ return { tools };
48
+ });
49
+ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
50
+ const fullName = request.params.name;
51
+ const args = (request.params.arguments ?? {});
52
+ const requestId = (0, audit_1.generateRequestId)();
53
+ const entry = toolIndex.get(fullName);
54
+ if (!entry) {
55
+ return {
56
+ content: [{ type: "text", text: `Unknown tool: ${fullName}` }],
57
+ isError: true,
58
+ };
59
+ }
60
+ if (blockedTools.has(fullName)) {
61
+ audit.record({
62
+ requestId,
63
+ subject: config.subject,
64
+ tool: fullName,
65
+ args,
66
+ allowed: false,
67
+ reason: "schema drift detected — tool blocked until admin re-approves",
68
+ latencyMs: 0,
69
+ timestamp: new Date().toISOString(),
70
+ });
71
+ return {
72
+ content: [{ type: "text", text: `[Vengtoo] Tool "${fullName}" is blocked — schema drift detected. An admin must re-approve this tool in the Vengtoo console.` }],
73
+ isError: true,
74
+ };
75
+ }
76
+ const start = performance.now();
77
+ const result = await (0, authorize_1.authorize)(config, config.subject, fullName, args);
78
+ const latencyMs = Math.round(performance.now() - start);
79
+ if (!result.allowed) {
80
+ const reason = result.reason ?? "denied by policy";
81
+ audit.record({
82
+ requestId,
83
+ subject: config.subject,
84
+ tool: fullName,
85
+ args,
86
+ allowed: false,
87
+ reason,
88
+ latencyMs,
89
+ timestamp: new Date().toISOString(),
90
+ });
91
+ return {
92
+ content: [{ type: "text", text: `[Vengtoo] Access denied: tool "${fullName}" was blocked by Vengtoo authorization policy. Reason: ${reason}` }],
93
+ isError: true,
94
+ };
95
+ }
96
+ try {
97
+ const result = await entry.downstream.client.callTool({
98
+ name: entry.originalName,
99
+ arguments: args,
100
+ });
101
+ audit.record({
102
+ requestId,
103
+ subject: config.subject,
104
+ tool: fullName,
105
+ args,
106
+ allowed: true,
107
+ latencyMs,
108
+ timestamp: new Date().toISOString(),
109
+ });
110
+ return result;
111
+ }
112
+ catch (err) {
113
+ const message = err instanceof Error ? err.message : String(err);
114
+ audit.record({
115
+ requestId,
116
+ subject: config.subject,
117
+ tool: fullName,
118
+ args,
119
+ allowed: true,
120
+ reason: `downstream error: ${message}`,
121
+ latencyMs,
122
+ timestamp: new Date().toISOString(),
123
+ });
124
+ return {
125
+ content: [{ type: "text", text: `Downstream server error for "${fullName}": ${message}` }],
126
+ isError: true,
127
+ };
128
+ }
129
+ });
130
+ const transport = new stdio_js_1.StdioServerTransport();
131
+ await server.connect(transport);
132
+ console.error(`[vengtoo-gateway] running — ${downstreams.length} server(s), ${toolIndex.size} tool(s)`);
133
+ }
134
+ exports.startGateway = startGateway;
135
+ async function spawnDownstreams(servers) {
136
+ const results = [];
137
+ for (const [name, cfg] of Object.entries(servers)) {
138
+ console.error(`[vengtoo-gateway] connecting to ${name}: ${cfg.command} ${(cfg.args ?? []).join(" ")}`);
139
+ const transport = new stdio_js_2.StdioClientTransport({
140
+ command: cfg.command,
141
+ args: cfg.args,
142
+ env: { ...process.env, ...(cfg.env ?? {}) },
143
+ });
144
+ const client = new index_js_2.Client({ name: `vengtoo-gateway/${name}`, version: "0.1.0" }, { capabilities: {} });
145
+ await client.connect(transport);
146
+ const { tools } = await client.listTools();
147
+ console.error(`[vengtoo-gateway] ${name}: ${tools.length} tool(s) registered`);
148
+ results.push({
149
+ name,
150
+ client,
151
+ transport,
152
+ tools: tools.map((t) => ({
153
+ name: t.name,
154
+ description: t.description,
155
+ inputSchema: t.inputSchema,
156
+ })),
157
+ });
158
+ }
159
+ return results;
160
+ }
161
+ function buildToolIndex(downstreams) {
162
+ const index = new Map();
163
+ for (const ds of downstreams) {
164
+ for (const tool of ds.tools) {
165
+ const qn = qualifiedName(ds.name, tool.name);
166
+ index.set(qn, { downstream: ds, originalName: tool.name });
167
+ }
168
+ }
169
+ return index;
170
+ }
171
+ function qualifiedName(serverName, toolName) {
172
+ return `${serverName}__${toolName}`;
173
+ }
@@ -0,0 +1,2 @@
1
+ import type { GatewayConfig } from "./types";
2
+ export declare function generatePolicy(config: GatewayConfig, outputPath: string): Promise<void>;
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generatePolicy = void 0;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const utils_1 = require("./utils");
7
+ const classify_1 = require("./classify");
8
+ async function generatePolicy(config, outputPath) {
9
+ const tools = [];
10
+ for (const [name, cfg] of Object.entries(config.servers)) {
11
+ try {
12
+ const { tools: discovered, transport } = await (0, utils_1.discoverTools)(name, cfg);
13
+ for (const t of discovered) {
14
+ tools.push({
15
+ ...t,
16
+ trust: (0, classify_1.classifyTool)(t.qualifiedName),
17
+ });
18
+ }
19
+ await transport.close();
20
+ }
21
+ catch (err) {
22
+ const message = err instanceof Error ? err.message : String(err);
23
+ console.error(`[!] Failed to connect to "${name}": ${message}`);
24
+ }
25
+ }
26
+ if (tools.length === 0) {
27
+ console.error("No tools discovered — cannot generate policy.");
28
+ process.exit(1);
29
+ }
30
+ const rego = buildRego(tools);
31
+ const dest = (0, path_1.resolve)(outputPath);
32
+ (0, fs_1.writeFileSync)(dest, rego, "utf-8");
33
+ const low = tools.filter((t) => t.trust === "low").length;
34
+ const med = tools.filter((t) => t.trust === "medium").length;
35
+ const high = tools.filter((t) => t.trust === "high").length;
36
+ console.log(`Generated trust-classified policy with ${tools.length} tool(s) → ${dest}`);
37
+ console.log(` LOW: ${low} MEDIUM: ${med} HIGH: ${high}`);
38
+ console.log(`\nEdit the file to customize which tools to allow or block.`);
39
+ console.log(`Then start the agent:\n`);
40
+ console.log(` vengtoo-agent --policy ${outputPath}\n`);
41
+ process.exit(0);
42
+ }
43
+ exports.generatePolicy = generatePolicy;
44
+ function buildRego(tools) {
45
+ const servers = [...new Set(tools.map((t) => t.server))];
46
+ const lines = [];
47
+ const now = new Date().toISOString();
48
+ const total = tools.length;
49
+ lines.push("# Auto-generated by Vengtoo MCP Gateway");
50
+ lines.push(`# ${servers.length} server(s), ${total} tool(s) discovered`);
51
+ lines.push(`# Generated: ${now}`);
52
+ lines.push("#");
53
+ lines.push("# Trust levels:");
54
+ lines.push("# LOW — read-only operations, allowed by default");
55
+ lines.push("# MEDIUM — write/execute operations, requires explicit subject approval");
56
+ lines.push("# HIGH — destructive operations, blocked by default");
57
+ lines.push("#");
58
+ lines.push("# Edit this file to customize your policy.");
59
+ lines.push("# Run: vengtoo-agent --policy ./policy.rego");
60
+ lines.push("");
61
+ lines.push("package vengtoo.mcp");
62
+ lines.push("");
63
+ lines.push("default allow := false");
64
+ const low = tools.filter((t) => t.trust === "low").sort(byName);
65
+ const medium = tools.filter((t) => t.trust === "medium").sort(byName);
66
+ const high = tools.filter((t) => t.trust === "high").sort(byName);
67
+ if (low.length > 0) {
68
+ lines.push("");
69
+ lines.push("# ── LOW trust (allowed for all approved agents) ──────────────");
70
+ lines.push("");
71
+ for (const t of low) {
72
+ const warning = toolWarning(t);
73
+ if (warning)
74
+ lines.push(`# ${warning}`);
75
+ lines.push(`allow if {`);
76
+ lines.push(` input.resource.name == "${t.qualifiedName}"`);
77
+ lines.push(`}`);
78
+ lines.push("");
79
+ }
80
+ }
81
+ if (medium.length > 0) {
82
+ lines.push("# ── MEDIUM trust (allowed for specific agents) ───────────────");
83
+ lines.push("# Replace subject values with your actual agent identities");
84
+ lines.push("");
85
+ for (const t of medium) {
86
+ const warning = toolWarning(t);
87
+ if (warning)
88
+ lines.push(`# ${warning}`);
89
+ lines.push(`allow if {`);
90
+ lines.push(` input.resource.name == "${t.qualifiedName}"`);
91
+ lines.push(` input.subject.id in {"agent:cursor", "agent:claude"}`);
92
+ lines.push(`}`);
93
+ lines.push("");
94
+ }
95
+ }
96
+ if (high.length > 0) {
97
+ lines.push("# ── HIGH trust (blocked by default — uncomment to allow) ─────");
98
+ lines.push("# DANGER: these are destructive operations.");
99
+ lines.push("# Uncomment and specify exact subjects before enabling.");
100
+ lines.push("");
101
+ for (const t of high) {
102
+ lines.push(`# allow if {`);
103
+ lines.push(`# input.resource.name == "${t.qualifiedName}"`);
104
+ lines.push(`# input.subject.id == "agent:claude" # be explicit`);
105
+ lines.push(`# }`);
106
+ lines.push("");
107
+ }
108
+ }
109
+ return lines.join("\n");
110
+ }
111
+ function byName(a, b) {
112
+ return a.qualifiedName.localeCompare(b.qualifiedName);
113
+ }
114
+ function toolWarning(t) {
115
+ const name = t.qualifiedName.toLowerCase();
116
+ if (/execute/.test(name)) {
117
+ return `WARNING: ${t.qualifiedName} can run destructive operations — review carefully`;
118
+ }
119
+ if (/query/.test(name)) {
120
+ return `WARNING: ${t.qualifiedName} — raw queries can be destructive, consider moving to MEDIUM`;
121
+ }
122
+ return null;
123
+ }
package/dist/hash.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export declare function computeToolsHash(tools: Array<{
2
+ name: string;
3
+ description?: string;
4
+ inputSchema: unknown;
5
+ }>): string;
6
+ export declare function hasChanged(serverName: string, currentHash: string): boolean;
7
+ export declare function saveHash(serverName: string, hash: string): void;
package/dist/hash.js ADDED
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.saveHash = exports.hasChanged = exports.computeToolsHash = void 0;
4
+ const crypto_1 = require("crypto");
5
+ const fs_1 = require("fs");
6
+ const path_1 = require("path");
7
+ const HASH_DIR = (0, path_1.join)(process.cwd(), ".vengtoo", "hashes");
8
+ function computeToolsHash(tools) {
9
+ const sorted = [...tools].sort((a, b) => a.name.localeCompare(b.name));
10
+ const payload = sorted.map((t) => ({
11
+ name: t.name,
12
+ description: t.description ?? "",
13
+ inputSchema: t.inputSchema,
14
+ }));
15
+ return (0, crypto_1.createHash)("sha256").update(JSON.stringify(payload)).digest("hex");
16
+ }
17
+ exports.computeToolsHash = computeToolsHash;
18
+ function hasChanged(serverName, currentHash) {
19
+ const path = hashPath(serverName);
20
+ if (!(0, fs_1.existsSync)(path))
21
+ return true;
22
+ try {
23
+ return (0, fs_1.readFileSync)(path, "utf-8").trim() !== currentHash;
24
+ }
25
+ catch {
26
+ return true;
27
+ }
28
+ }
29
+ exports.hasChanged = hasChanged;
30
+ function saveHash(serverName, hash) {
31
+ if (!(0, fs_1.existsSync)(HASH_DIR)) {
32
+ (0, fs_1.mkdirSync)(HASH_DIR, { recursive: true });
33
+ }
34
+ (0, fs_1.writeFileSync)(hashPath(serverName), hash, "utf-8");
35
+ }
36
+ exports.saveHash = saveHash;
37
+ function hashPath(serverName) {
38
+ return (0, path_1.join)(HASH_DIR, `${serverName}.hash`);
39
+ }
@@ -0,0 +1,3 @@
1
+ export { startGateway } from "./gateway";
2
+ export { AuditForwarder } from "./audit";
3
+ export type { GatewayConfig, ServerConfig, AuthorizeResult } from "./types";
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AuditForwarder = exports.startGateway = void 0;
4
+ var gateway_1 = require("./gateway");
5
+ Object.defineProperty(exports, "startGateway", { enumerable: true, get: function () { return gateway_1.startGateway; } });
6
+ var audit_1 = require("./audit");
7
+ Object.defineProperty(exports, "AuditForwarder", { enumerable: true, get: function () { return audit_1.AuditForwarder; } });
@@ -0,0 +1,2 @@
1
+ import type { GatewayConfig } from "./types";
2
+ export declare function listTools(config: GatewayConfig): Promise<void>;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listTools = void 0;
4
+ const utils_1 = require("./utils");
5
+ async function listTools(config) {
6
+ const entries = [];
7
+ for (const [name, cfg] of Object.entries(config.servers)) {
8
+ try {
9
+ const { tools, transport } = await (0, utils_1.discoverTools)(name, cfg);
10
+ entries.push(...tools);
11
+ await transport.close();
12
+ }
13
+ catch (err) {
14
+ const message = err instanceof Error ? err.message : String(err);
15
+ console.error(`[!] Failed to connect to "${name}": ${message}`);
16
+ }
17
+ }
18
+ if (entries.length === 0) {
19
+ console.log("No tools discovered from downstream servers.");
20
+ process.exit(0);
21
+ }
22
+ console.log(`\nDiscovered ${entries.length} tool(s) across ${Object.keys(config.servers).length} server(s):\n`);
23
+ console.log("─".repeat(70));
24
+ let currentServer = "";
25
+ for (const e of entries) {
26
+ if (e.server !== currentServer) {
27
+ currentServer = e.server;
28
+ console.log(`\n Server: ${currentServer}`);
29
+ console.log(" " + "─".repeat(40));
30
+ }
31
+ console.log(`\n ${e.qualifiedName}`);
32
+ if (e.description) {
33
+ console.log(` ${e.description}`);
34
+ }
35
+ if (e.inputFields.length > 0) {
36
+ console.log(` inputs: ${e.inputFields.join(", ")}`);
37
+ }
38
+ }
39
+ console.log("\n" + "─".repeat(70));
40
+ console.log("\nUse these qualified names in your Rego policy. Example:\n");
41
+ console.log(" allow if {");
42
+ console.log(` input.resource.name == "${entries[0].qualifiedName}"`);
43
+ console.log(" }");
44
+ if (entries[0].inputFields.length > 0) {
45
+ const field = entries[0].inputFields[0];
46
+ console.log(`\n # Access tool arguments via input.resource.attributes.${field}`);
47
+ }
48
+ console.log("");
49
+ process.exit(0);
50
+ }
51
+ exports.listTools = listTools;
@@ -0,0 +1,3 @@
1
+ import type { ServerSnapshot, ToolSnapshot } from "./types";
2
+ export declare function saveSnapshot(serverName: string, tools: ToolSnapshot[], hash: string): void;
3
+ export declare function loadSnapshot(serverName: string): ServerSnapshot | null;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadSnapshot = exports.saveSnapshot = void 0;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const SNAPSHOT_DIR = (0, path_1.join)(process.cwd(), ".vengtoo", "snapshots");
7
+ function saveSnapshot(serverName, tools, hash) {
8
+ if (!(0, fs_1.existsSync)(SNAPSHOT_DIR)) {
9
+ (0, fs_1.mkdirSync)(SNAPSHOT_DIR, { recursive: true });
10
+ }
11
+ const snapshot = {
12
+ server: serverName,
13
+ hash,
14
+ tools: [...tools].sort((a, b) => a.name.localeCompare(b.name)),
15
+ capturedAt: new Date().toISOString(),
16
+ };
17
+ (0, fs_1.writeFileSync)(snapshotPath(serverName), JSON.stringify(snapshot, null, 2), "utf-8");
18
+ }
19
+ exports.saveSnapshot = saveSnapshot;
20
+ function loadSnapshot(serverName) {
21
+ const path = snapshotPath(serverName);
22
+ if (!(0, fs_1.existsSync)(path))
23
+ return null;
24
+ try {
25
+ return JSON.parse((0, fs_1.readFileSync)(path, "utf-8"));
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ }
31
+ exports.loadSnapshot = loadSnapshot;
32
+ function snapshotPath(serverName) {
33
+ return (0, path_1.join)(SNAPSHOT_DIR, `${serverName}.json`);
34
+ }
package/dist/sync.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ import type { GatewayConfig } from "./types";
2
+ interface DownstreamLike {
3
+ name: string;
4
+ tools: Array<{
5
+ name: string;
6
+ description?: string;
7
+ inputSchema: unknown;
8
+ }>;
9
+ }
10
+ export declare function syncAllServers(config: GatewayConfig, downstreams: DownstreamLike[]): Promise<Set<string>>;
11
+ export {};
package/dist/sync.js ADDED
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.syncAllServers = void 0;
4
+ const classify_1 = require("./classify");
5
+ const hash_1 = require("./hash");
6
+ const snapshot_1 = require("./snapshot");
7
+ const drift_1 = require("./drift");
8
+ const gateway_id_1 = require("./gateway-id");
9
+ const SYNC_TIMEOUT_MS = 10_000;
10
+ const DEFAULT_CLOUD_BASE = "https://api.vengtoo.com";
11
+ async function syncAllServers(config, downstreams) {
12
+ const gatewayId = (0, gateway_id_1.getOrCreateGatewayId)();
13
+ const blockedTools = new Set();
14
+ console.error(`[vengtoo-gateway] gateway id: ${gatewayId}`);
15
+ for (const ds of downstreams) {
16
+ const hash = (0, hash_1.computeToolsHash)(ds.tools);
17
+ const toolSnapshots = ds.tools.map((t) => ({
18
+ name: t.name,
19
+ description: t.description ?? "",
20
+ inputSchema: t.inputSchema,
21
+ }));
22
+ let driftEvents = [];
23
+ const oldSnapshot = (0, snapshot_1.loadSnapshot)(ds.name);
24
+ if (oldSnapshot && (0, hash_1.hasChanged)(ds.name, hash)) {
25
+ driftEvents = (0, drift_1.detectDrift)(ds.name, oldSnapshot, toolSnapshots);
26
+ if (driftEvents.length > 0) {
27
+ (0, drift_1.logDriftEvents)(driftEvents);
28
+ }
29
+ }
30
+ if (!(0, hash_1.hasChanged)(ds.name, hash) && oldSnapshot) {
31
+ console.error(`[vengtoo-gateway] tools unchanged for '${ds.name}' — skipping sync`);
32
+ continue;
33
+ }
34
+ const schemas = new Map();
35
+ const discovered = ds.tools.map((t) => {
36
+ const qn = `${ds.name}__${t.name}`;
37
+ schemas.set(qn, t.inputSchema);
38
+ return {
39
+ qualifiedName: qn,
40
+ server: ds.name,
41
+ originalName: t.name,
42
+ description: t.description ?? "",
43
+ inputFields: [],
44
+ };
45
+ });
46
+ const classified = (0, classify_1.classifyTools)(discovered, schemas);
47
+ if (config.vengtoo.apiKey) {
48
+ try {
49
+ const result = await syncToolsToCloud(config, gatewayId, ds.name, classified, hash, driftEvents);
50
+ if (result) {
51
+ (0, hash_1.saveHash)(ds.name, hash);
52
+ (0, snapshot_1.saveSnapshot)(ds.name, toolSnapshots, hash);
53
+ console.error(`[vengtoo-gateway] synced ${result.synced} tools for '${ds.name}' (${result.created} created, ${result.updated} updated, ${result.unchanged} unchanged)`);
54
+ if (result.drifted_count && result.drifted_count > 0) {
55
+ console.error(`[vengtoo-gateway] ${result.drifted_count} tool(s) drifted from approved baseline for '${ds.name}'`);
56
+ }
57
+ if (result.blocked_tools) {
58
+ for (const tool of result.blocked_tools) {
59
+ blockedTools.add(`${ds.name}__${tool}`);
60
+ }
61
+ }
62
+ }
63
+ }
64
+ catch (err) {
65
+ const msg = err instanceof Error ? err.message : String(err);
66
+ console.error(`[vengtoo-gateway] cloud sync failed for '${ds.name}' — ${msg}`);
67
+ }
68
+ }
69
+ else {
70
+ (0, hash_1.saveHash)(ds.name, hash);
71
+ (0, snapshot_1.saveSnapshot)(ds.name, toolSnapshots, hash);
72
+ if (config.vengtoo.blockOnDrift && driftEvents.length > 0) {
73
+ for (const e of driftEvents) {
74
+ if (e.severity === "CRITICAL") {
75
+ blockedTools.add(`${ds.name}__${e.toolName}`);
76
+ }
77
+ }
78
+ console.error(`[vengtoo-gateway] blockOnDrift enabled — ${blockedTools.size} tool(s) blocked locally`);
79
+ }
80
+ }
81
+ }
82
+ return blockedTools;
83
+ }
84
+ exports.syncAllServers = syncAllServers;
85
+ async function syncToolsToCloud(config, gatewayId, serverName, tools, hash, driftEvents) {
86
+ const baseUrl = resolveBaseUrl(config);
87
+ const url = `${baseUrl}/v1/gateways/${gatewayId}/tools/sync`;
88
+ const body = {
89
+ server: serverName,
90
+ tools: tools.map((t) => ({
91
+ name: t.originalName,
92
+ trust: t.trust,
93
+ description: t.description,
94
+ schema: t.schema,
95
+ })),
96
+ hash,
97
+ drift_events: driftEvents.length > 0 ? driftEvents : undefined,
98
+ };
99
+ const headers = {
100
+ "Content-Type": "application/json",
101
+ Accept: "application/json",
102
+ };
103
+ const auth = resolveAuth(config);
104
+ if (auth)
105
+ headers["Authorization"] = auth;
106
+ const controller = new AbortController();
107
+ const timeout = setTimeout(() => controller.abort(), SYNC_TIMEOUT_MS);
108
+ try {
109
+ const res = await fetch(url, {
110
+ method: "POST",
111
+ headers,
112
+ body: JSON.stringify(body),
113
+ signal: controller.signal,
114
+ });
115
+ if (!res.ok) {
116
+ const text = await res.text().catch(() => "");
117
+ console.error(`[vengtoo-gateway] sync returned ${res.status}: ${text}`);
118
+ return null;
119
+ }
120
+ return (await res.json());
121
+ }
122
+ finally {
123
+ clearTimeout(timeout);
124
+ }
125
+ }
126
+ function resolveBaseUrl(config) {
127
+ if (config.vengtoo.cloudUrl) {
128
+ const url = new URL(config.vengtoo.cloudUrl);
129
+ return `${url.protocol}//${url.host}`;
130
+ }
131
+ return DEFAULT_CLOUD_BASE;
132
+ }
133
+ function resolveAuth(config) {
134
+ if (config.vengtoo.apiKey)
135
+ return `Bearer ${config.vengtoo.apiKey}`;
136
+ if (config.vengtoo.clientId && config.vengtoo.clientSecret) {
137
+ return `Basic ${Buffer.from(`${config.vengtoo.clientId}:${config.vengtoo.clientSecret}`).toString("base64")}`;
138
+ }
139
+ return undefined;
140
+ }
@@ -0,0 +1,51 @@
1
+ export interface GatewayConfig {
2
+ vengtoo: {
3
+ agentUrl?: string;
4
+ cloudUrl?: string;
5
+ apiKey?: string;
6
+ clientId?: string;
7
+ clientSecret?: string;
8
+ timeoutMs?: number;
9
+ blockOnDrift?: boolean;
10
+ };
11
+ subject: string;
12
+ subjectType?: string;
13
+ resourceType?: string;
14
+ servers: Record<string, ServerConfig>;
15
+ audit?: {
16
+ forwardUrl?: string;
17
+ tenantId?: string;
18
+ };
19
+ }
20
+ export interface ServerConfig {
21
+ command: string;
22
+ args?: string[];
23
+ env?: Record<string, string>;
24
+ }
25
+ export interface AuthorizeResult {
26
+ allowed: boolean;
27
+ reason?: string;
28
+ }
29
+ export type DriftSeverity = "CRITICAL" | "WARNING" | "INFO";
30
+ export type DriftChangeType = "tool_removed" | "tool_added" | "parameter_removed" | "parameter_added" | "type_changed" | "description_changed";
31
+ export interface DriftEvent {
32
+ toolName: string;
33
+ serverName: string;
34
+ severity: DriftSeverity;
35
+ changeType: DriftChangeType;
36
+ field?: string;
37
+ oldValue?: unknown;
38
+ newValue?: unknown;
39
+ message: string;
40
+ }
41
+ export interface ToolSnapshot {
42
+ name: string;
43
+ description: string;
44
+ inputSchema: unknown;
45
+ }
46
+ export interface ServerSnapshot {
47
+ server: string;
48
+ hash: string;
49
+ tools: ToolSnapshot[];
50
+ capturedAt: string;
51
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });