@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.
- package/dist/index.js +110 -4
- 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(
|
|
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
|
-
|
|
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
|
-
}).
|
|
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(`
|
|
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.
|
|
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": {
|