@solongate/proxy 0.1.26 → 0.2.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 (2) hide show
  1. package/dist/index.js +110 -4
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1198,8 +1198,9 @@ async function fetchCloudPolicy(apiKey, apiUrl, policyId) {
1198
1198
  };
1199
1199
  }
1200
1200
  async function sendAuditLog(apiKey, apiUrl, entry) {
1201
+ const url = `${apiUrl}/api/v1/audit-logs`;
1201
1202
  try {
1202
- await fetch(`${apiUrl}/api/v1/audit-logs`, {
1203
+ const res = await fetch(url, {
1203
1204
  method: "POST",
1204
1205
  headers: {
1205
1206
  "Authorization": `Bearer ${apiKey}`,
@@ -1207,7 +1208,14 @@ async function sendAuditLog(apiKey, apiUrl, entry) {
1207
1208
  },
1208
1209
  body: JSON.stringify(entry)
1209
1210
  });
1210
- } catch {
1211
+ if (!res.ok) {
1212
+ const body = await res.text().catch(() => "");
1213
+ process.stderr.write(`[SolonGate] Audit log failed (${res.status}): ${body}
1214
+ `);
1215
+ }
1216
+ } catch (err) {
1217
+ process.stderr.write(`[SolonGate] Audit log error: ${err instanceof Error ? err.message : String(err)}
1218
+ `);
1211
1219
  }
1212
1220
  }
1213
1221
  var PRESETS = {
@@ -3305,6 +3313,7 @@ var SolonGateProxy = class {
3305
3313
  this.config = config;
3306
3314
  this.gate = new SolonGate({
3307
3315
  name: config.name ?? "solongate-proxy",
3316
+ apiKey: "sg_test_proxy_internal_00000000",
3308
3317
  policySet: config.policy,
3309
3318
  config: {
3310
3319
  validateSchemas: config.validateInput ?? true,
@@ -3373,6 +3382,8 @@ var SolonGateProxy = class {
3373
3382
  await this.connectUpstream();
3374
3383
  await this.discoverTools();
3375
3384
  this.registerToolsToCloud();
3385
+ this.registerServerToCloud();
3386
+ this.syncPolicyToCloud();
3376
3387
  this.createServer();
3377
3388
  await this.serve();
3378
3389
  this.startPolicyPolling();
@@ -3483,6 +3494,7 @@ var SolonGateProxy = class {
3483
3494
  log(`Result: ${decision} (${evaluationTimeMs}ms)`);
3484
3495
  if (this.config.apiKey && !this.config.apiKey.startsWith("sg_test_")) {
3485
3496
  const apiUrl = this.config.apiUrl ?? "https://api.solongate.com";
3497
+ log(`Sending audit log: ${name} \u2192 ${decision} (key: ${this.config.apiKey.slice(0, 16)}...)`);
3486
3498
  sendAuditLog(this.config.apiKey, apiUrl, {
3487
3499
  tool: name,
3488
3500
  arguments: args ?? {},
@@ -3490,6 +3502,8 @@ var SolonGateProxy = class {
3490
3502
  reason: result.isError ? result.content[0]?.text ?? "denied" : "allowed",
3491
3503
  evaluationTimeMs
3492
3504
  });
3505
+ } else {
3506
+ log(`Skipping audit log (apiKey: ${this.config.apiKey ? "test key" : "not set"})`);
3493
3507
  }
3494
3508
  return {
3495
3509
  content: [...result.content],
@@ -3542,6 +3556,8 @@ var SolonGateProxy = class {
3542
3556
  registerToolsToCloud() {
3543
3557
  if (!this.config.apiKey || this.config.apiKey.startsWith("sg_test_")) return;
3544
3558
  const apiUrl = this.config.apiUrl ?? "https://api.solongate.com";
3559
+ let registered = 0;
3560
+ const total = this.upstreamTools.length;
3545
3561
  for (const tool of this.upstreamTools) {
3546
3562
  fetch(`${apiUrl}/api/v1/tools`, {
3547
3563
  method: "POST",
@@ -3556,10 +3572,18 @@ var SolonGateProxy = class {
3556
3572
  permissions: this.guessPermissions(tool.name),
3557
3573
  enabled: true
3558
3574
  })
3559
- }).catch(() => {
3575
+ }).then(async (res) => {
3576
+ if (res.ok || res.status === 409) {
3577
+ registered++;
3578
+ } else {
3579
+ const body = await res.text().catch(() => "");
3580
+ log(`Tool registration failed for "${tool.name}" (${res.status}): ${body}`);
3581
+ }
3582
+ }).catch((err) => {
3583
+ log(`Tool registration error for "${tool.name}": ${err instanceof Error ? err.message : String(err)}`);
3560
3584
  });
3561
3585
  }
3562
- log(`Registered ${this.upstreamTools.length} tools to dashboard`);
3586
+ log(`Registering ${total} tools to dashboard...`);
3563
3587
  }
3564
3588
  /**
3565
3589
  * Guess tool permissions from tool name.
@@ -3574,6 +3598,88 @@ var SolonGateProxy = class {
3574
3598
  }
3575
3599
  return ["READ"];
3576
3600
  }
3601
+ /**
3602
+ * Register the upstream MCP server to the SolonGate Cloud API.
3603
+ * This makes it visible on the Dashboard MCP Servers page.
3604
+ */
3605
+ registerServerToCloud() {
3606
+ if (!this.config.apiKey || this.config.apiKey.startsWith("sg_test_")) return;
3607
+ const apiUrl = this.config.apiUrl ?? "https://api.solongate.com";
3608
+ const transport = this.config.upstream.transport ?? "stdio";
3609
+ let serverName = this.config.name ?? "solongate-proxy";
3610
+ let serverUrl;
3611
+ let command;
3612
+ let args;
3613
+ if (transport === "stdio") {
3614
+ command = this.config.upstream.command;
3615
+ args = (this.config.upstream.args ?? []).join(" ");
3616
+ serverUrl = `stdio://${command}`;
3617
+ serverName = command || serverName;
3618
+ } else {
3619
+ serverUrl = this.config.upstream.url || "";
3620
+ try {
3621
+ const u = new URL(serverUrl);
3622
+ serverName = u.hostname || serverName;
3623
+ } catch {
3624
+ }
3625
+ }
3626
+ fetch(`${apiUrl}/api/v1/mcp-servers`, {
3627
+ method: "POST",
3628
+ headers: {
3629
+ "Authorization": `Bearer ${this.config.apiKey}`,
3630
+ "Content-Type": "application/json"
3631
+ },
3632
+ body: JSON.stringify({
3633
+ name: serverName,
3634
+ url: serverUrl,
3635
+ command: command || void 0,
3636
+ args: args || void 0
3637
+ })
3638
+ }).then(async (res) => {
3639
+ if (res.ok) {
3640
+ log(`Registered MCP server "${serverName}" to dashboard.`);
3641
+ } else if (res.status === 409) {
3642
+ log(`MCP server "${serverName}" already registered.`);
3643
+ } else {
3644
+ const body = await res.text().catch(() => "");
3645
+ log(`MCP server registration failed (${res.status}): ${body}`);
3646
+ }
3647
+ }).catch((err) => {
3648
+ log(`MCP server registration error: ${err instanceof Error ? err.message : String(err)}`);
3649
+ });
3650
+ }
3651
+ /**
3652
+ * Sync the active policy to the SolonGate Cloud API.
3653
+ * This makes it visible on the Dashboard Policies page.
3654
+ */
3655
+ syncPolicyToCloud() {
3656
+ if (!this.config.apiKey || this.config.apiKey.startsWith("sg_test_")) return;
3657
+ const apiUrl = this.config.apiUrl ?? "https://api.solongate.com";
3658
+ const policy = this.config.policy;
3659
+ fetch(`${apiUrl}/api/v1/policies`, {
3660
+ method: "POST",
3661
+ headers: {
3662
+ "Authorization": `Bearer ${this.config.apiKey}`,
3663
+ "Content-Type": "application/json"
3664
+ },
3665
+ body: JSON.stringify({
3666
+ id: policy.id || "default",
3667
+ name: policy.name || "Default Policy",
3668
+ description: policy.description || `Policy synced from proxy`,
3669
+ version: policy.version || 1,
3670
+ rules: policy.rules
3671
+ })
3672
+ }).then(async (res) => {
3673
+ if (res.ok) {
3674
+ log(`Synced policy "${policy.name}" to dashboard.`);
3675
+ } else {
3676
+ const body = await res.text().catch(() => "");
3677
+ log(`Policy sync failed (${res.status}): ${body}`);
3678
+ }
3679
+ }).catch((err) => {
3680
+ log(`Policy sync error: ${err instanceof Error ? err.message : String(err)}`);
3681
+ });
3682
+ }
3577
3683
  /**
3578
3684
  * Poll for policy updates from dashboard every 60 seconds.
3579
3685
  * When user changes policy in dashboard, proxy picks it up automatically.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.1.26",
3
+ "version": "0.2.0",
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": {