@solongate/proxy 0.1.24 → 0.1.26

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 (2) hide show
  1. package/dist/index.js +97 -0
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3084,6 +3084,7 @@ var SolonGate = class {
3084
3084
  });
3085
3085
  if (!options.policySet && !config.policySet && apiKey.startsWith("sg_live_")) {
3086
3086
  this.fetchCloudPolicyOnce();
3087
+ this.startPolicyPolling();
3087
3088
  }
3088
3089
  this.tokenIssuer = config.tokenSecret ? new TokenIssuer({
3089
3090
  secret: config.tokenSecret,
@@ -3153,6 +3154,40 @@ var SolonGate = class {
3153
3154
  }).catch(() => {
3154
3155
  });
3155
3156
  }
3157
+ /**
3158
+ * Poll for policy updates from dashboard every 60 seconds.
3159
+ */
3160
+ startPolicyPolling() {
3161
+ const apiUrl = this.config.apiUrl ?? "https://api.solongate.com";
3162
+ let currentVersion = 0;
3163
+ setInterval(async () => {
3164
+ try {
3165
+ const res = await fetch(`${apiUrl}/api/v1/policies/default`, {
3166
+ headers: { "Authorization": `Bearer ${this.apiKey}` },
3167
+ signal: AbortSignal.timeout(1e4)
3168
+ });
3169
+ if (!res.ok) return;
3170
+ const data = await res.json();
3171
+ const version = Number(data._version ?? 0);
3172
+ const rulesCount = Array.isArray(data.rules) ? data.rules.length : 0;
3173
+ if (version !== currentVersion && version > 0) {
3174
+ const policySet = {
3175
+ id: String(data.id ?? "cloud"),
3176
+ name: String(data.name ?? "Cloud Policy"),
3177
+ description: String(data.description ?? ""),
3178
+ version,
3179
+ rules: data.rules ?? [],
3180
+ createdAt: String(data._created_at ?? ""),
3181
+ updatedAt: ""
3182
+ };
3183
+ this.policyEngine.loadPolicySet(policySet);
3184
+ currentVersion = version;
3185
+ console.warn(`[SolonGate] Policy updated from dashboard: ${policySet.name} v${version} (${rulesCount} rules)`);
3186
+ }
3187
+ } catch {
3188
+ }
3189
+ }, 6e4);
3190
+ }
3156
3191
  /**
3157
3192
  * Send audit log to SolonGate Cloud API (fire-and-forget).
3158
3193
  */
@@ -3327,6 +3362,7 @@ var SolonGateProxy = class {
3327
3362
  }
3328
3363
  }
3329
3364
  }
3365
+ this.gate.loadPolicy(this.config.policy);
3330
3366
  log(`Policy: ${this.config.policy.name} (${this.config.policy.rules.length} rules)`);
3331
3367
  const transport = this.config.upstream.transport ?? "stdio";
3332
3368
  if (transport === "stdio") {
@@ -3336,8 +3372,10 @@ var SolonGateProxy = class {
3336
3372
  }
3337
3373
  await this.connectUpstream();
3338
3374
  await this.discoverTools();
3375
+ this.registerToolsToCloud();
3339
3376
  this.createServer();
3340
3377
  await this.serve();
3378
+ this.startPolicyPolling();
3341
3379
  }
3342
3380
  /**
3343
3381
  * Connect to the upstream MCP server.
@@ -3497,6 +3535,65 @@ var SolonGateProxy = class {
3497
3535
  });
3498
3536
  });
3499
3537
  }
3538
+ /**
3539
+ * Register discovered tools to the SolonGate Cloud API.
3540
+ * This makes tools visible on the Dashboard (/tools page).
3541
+ */
3542
+ registerToolsToCloud() {
3543
+ if (!this.config.apiKey || this.config.apiKey.startsWith("sg_test_")) return;
3544
+ const apiUrl = this.config.apiUrl ?? "https://api.solongate.com";
3545
+ for (const tool of this.upstreamTools) {
3546
+ fetch(`${apiUrl}/api/v1/tools`, {
3547
+ method: "POST",
3548
+ headers: {
3549
+ "Authorization": `Bearer ${this.config.apiKey}`,
3550
+ "Content-Type": "application/json"
3551
+ },
3552
+ body: JSON.stringify({
3553
+ name: tool.name,
3554
+ description: tool.description ?? "",
3555
+ input_schema: tool.inputSchema,
3556
+ permissions: this.guessPermissions(tool.name),
3557
+ enabled: true
3558
+ })
3559
+ }).catch(() => {
3560
+ });
3561
+ }
3562
+ log(`Registered ${this.upstreamTools.length} tools to dashboard`);
3563
+ }
3564
+ /**
3565
+ * Guess tool permissions from tool name.
3566
+ */
3567
+ guessPermissions(toolName) {
3568
+ const name = toolName.toLowerCase();
3569
+ if (name.includes("exec") || name.includes("shell") || name.includes("run") || name.includes("eval")) {
3570
+ return ["EXECUTE"];
3571
+ }
3572
+ if (name.includes("write") || name.includes("create") || name.includes("delete") || name.includes("update") || name.includes("set")) {
3573
+ return ["WRITE"];
3574
+ }
3575
+ return ["READ"];
3576
+ }
3577
+ /**
3578
+ * Poll for policy updates from dashboard every 60 seconds.
3579
+ * When user changes policy in dashboard, proxy picks it up automatically.
3580
+ */
3581
+ startPolicyPolling() {
3582
+ if (!this.config.apiKey || this.config.apiKey.startsWith("sg_test_")) return;
3583
+ const apiUrl = this.config.apiUrl ?? "https://api.solongate.com";
3584
+ const POLL_INTERVAL = 6e4;
3585
+ setInterval(async () => {
3586
+ try {
3587
+ const newPolicy = await fetchCloudPolicy(this.config.apiKey, apiUrl);
3588
+ if (newPolicy.version !== this.config.policy.version || newPolicy.rules.length !== this.config.policy.rules.length) {
3589
+ this.config.policy = newPolicy;
3590
+ this.gate.loadPolicy(newPolicy);
3591
+ log(`Policy updated from dashboard: ${newPolicy.name} v${newPolicy.version} (${newPolicy.rules.length} rules)`);
3592
+ }
3593
+ } catch {
3594
+ }
3595
+ }, POLL_INTERVAL);
3596
+ }
3500
3597
  /**
3501
3598
  * Start serving downstream.
3502
3599
  * If --port is set, serves via StreamableHTTP on that port.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "description": "MCP security proxy \u00e2\u20ac\u201d protect any MCP server with policies, input validation, rate limiting, and audit logging. Zero code changes required.",
5
5
  "type": "module",
6
6
  "bin": {