@node9/proxy 1.34.0 → 1.35.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/cli.js +144 -1
- package/dist/cli.mjs +144 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5889,6 +5889,57 @@ function validateApiUrl(raw) {
|
|
|
5889
5889
|
}
|
|
5890
5890
|
return null;
|
|
5891
5891
|
}
|
|
5892
|
+
function auditLocalAllow(toolName, args, checkedBy, creds, meta, dlpInfo, containsSensitiveArgs = false, riskMetadata) {
|
|
5893
|
+
const validated = validateApiUrl(creds.apiUrl);
|
|
5894
|
+
if (!validated) {
|
|
5895
|
+
try {
|
|
5896
|
+
import_fs10.default.appendFileSync(
|
|
5897
|
+
HOOK_DEBUG_LOG,
|
|
5898
|
+
`[audit] refused to send: invalid apiUrl scheme/host (got "${String(creds.apiUrl).slice(0, 200)}")
|
|
5899
|
+
`
|
|
5900
|
+
);
|
|
5901
|
+
} catch {
|
|
5902
|
+
}
|
|
5903
|
+
return Promise.resolve();
|
|
5904
|
+
}
|
|
5905
|
+
const safeArgs = containsSensitiveArgs ? { tool: toolName, redacted: true } : args;
|
|
5906
|
+
const dlpSample = dlpInfo && typeof dlpInfo.redactedSample === "string" ? dlpInfo.redactedSample.slice(0, DLP_SAMPLE_MAX_LEN) : void 0;
|
|
5907
|
+
const dlpPattern = dlpInfo && typeof dlpInfo.pattern === "string" ? dlpInfo.pattern.slice(0, DLP_PATTERN_MAX_LEN) : void 0;
|
|
5908
|
+
const safeCheckedBy = KNOWN_CHECKED_BY.has(checkedBy) ? checkedBy : "unknown";
|
|
5909
|
+
const cleanedRiskMetadata = riskMetadata ? Object.fromEntries(
|
|
5910
|
+
Object.entries(riskMetadata).filter(
|
|
5911
|
+
([, v]) => typeof v === "string" && v.length > 0 || typeof v === "number" && Number.isFinite(v)
|
|
5912
|
+
)
|
|
5913
|
+
) : void 0;
|
|
5914
|
+
const hasRiskMetadata = cleanedRiskMetadata && Object.keys(cleanedRiskMetadata).length > 0;
|
|
5915
|
+
return fetch(`${validated.toString().replace(/\/$/, "")}/audit`, {
|
|
5916
|
+
method: "POST",
|
|
5917
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${creds.apiKey}` },
|
|
5918
|
+
body: JSON.stringify({
|
|
5919
|
+
toolName,
|
|
5920
|
+
args: safeArgs,
|
|
5921
|
+
checkedBy: safeCheckedBy,
|
|
5922
|
+
...dlpInfo && { dlpPattern, dlpSample },
|
|
5923
|
+
...hasRiskMetadata && { riskMetadata: cleanedRiskMetadata },
|
|
5924
|
+
// session_id (Claude Code + Gemini CLI) groups all audit rows from one
|
|
5925
|
+
// agent run; transcript_path is the authoritative pointer to the
|
|
5926
|
+
// session log (survives Gemini resume drift). Both optional —
|
|
5927
|
+
// unsupported agents (MCP-mediated) leave them undefined.
|
|
5928
|
+
...meta?.sessionId && { runId: meta.sessionId },
|
|
5929
|
+
...meta?.transcriptPath && { transcriptPath: meta.transcriptPath },
|
|
5930
|
+
context: {
|
|
5931
|
+
agent: meta?.agent,
|
|
5932
|
+
mcpServer: meta?.mcpServer,
|
|
5933
|
+
hostname: import_os9.default.hostname(),
|
|
5934
|
+
cwd: process.cwd(),
|
|
5935
|
+
platform: import_os9.default.platform()
|
|
5936
|
+
}
|
|
5937
|
+
}),
|
|
5938
|
+
signal: AbortSignal.timeout(5e3)
|
|
5939
|
+
}).then(() => {
|
|
5940
|
+
}).catch(() => {
|
|
5941
|
+
});
|
|
5942
|
+
}
|
|
5892
5943
|
async function initNode9SaaS(toolName, args, creds, meta, riskMetadata, agentPolicy, forceReview) {
|
|
5893
5944
|
const controller = new AbortController();
|
|
5894
5945
|
const timeout = setTimeout(() => controller.abort(), 1e4);
|
|
@@ -6012,7 +6063,7 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
|
|
|
6012
6063
|
);
|
|
6013
6064
|
}
|
|
6014
6065
|
}
|
|
6015
|
-
var import_fs10, import_os9, import_path12;
|
|
6066
|
+
var import_fs10, import_os9, import_path12, DLP_SAMPLE_MAX_LEN, DLP_PATTERN_MAX_LEN, KNOWN_CHECKED_BY;
|
|
6016
6067
|
var init_cloud = __esm({
|
|
6017
6068
|
"src/auth/cloud.ts"() {
|
|
6018
6069
|
"use strict";
|
|
@@ -6020,6 +6071,39 @@ var init_cloud = __esm({
|
|
|
6020
6071
|
import_os9 = __toESM(require("os"));
|
|
6021
6072
|
import_path12 = __toESM(require("path"));
|
|
6022
6073
|
init_audit();
|
|
6074
|
+
DLP_SAMPLE_MAX_LEN = 200;
|
|
6075
|
+
DLP_PATTERN_MAX_LEN = 100;
|
|
6076
|
+
KNOWN_CHECKED_BY = /* @__PURE__ */ new Set([
|
|
6077
|
+
"dlp-block",
|
|
6078
|
+
"observe-mode-dlp-would-block",
|
|
6079
|
+
"dlp-review-flagged",
|
|
6080
|
+
"loop-detected",
|
|
6081
|
+
"audit-mode",
|
|
6082
|
+
"local-policy",
|
|
6083
|
+
"smart-rule-block",
|
|
6084
|
+
// Smart-rule block was downgraded to review because the daemon was
|
|
6085
|
+
// running and we're not in CI. The block attempt is still recorded;
|
|
6086
|
+
// the user got a popup. Distinct from 'smart-rule-block' so the
|
|
6087
|
+
// dashboard can show "block rule overridden" separately from a hard
|
|
6088
|
+
// block that fired with no human in the loop.
|
|
6089
|
+
"smart-rule-block-override",
|
|
6090
|
+
"persistent",
|
|
6091
|
+
"trust",
|
|
6092
|
+
"observe-mode",
|
|
6093
|
+
"observe-mode-would-block",
|
|
6094
|
+
// MCP supply-chain: the gateway pinned a server's tool definitions and they
|
|
6095
|
+
// changed since (possible tool poisoning / rug pull). Emitted as a synthetic
|
|
6096
|
+
// audit row so the SaaS surfaces it as a blocked event. The firewall maps
|
|
6097
|
+
// this checkedBy to AUTO_BLOCKED. See doc/roadmap/active/saas-value-first.md
|
|
6098
|
+
// (workstream B-Tier2).
|
|
6099
|
+
"mcp-pin-mismatch",
|
|
6100
|
+
// MCP visibility (B-Tier2, informational — NOT blocks): the gateway
|
|
6101
|
+
// discovered a server's tool inventory (mcp-discovered) or saw an oversized
|
|
6102
|
+
// tool response that bloats the context window (mcp-large-response). Stored
|
|
6103
|
+
// AUTO_ALLOWED; carries mcpToolCount / mcpResponseBytes in riskMetadata.
|
|
6104
|
+
"mcp-discovered",
|
|
6105
|
+
"mcp-large-response"
|
|
6106
|
+
]);
|
|
6023
6107
|
}
|
|
6024
6108
|
});
|
|
6025
6109
|
|
|
@@ -22047,6 +22131,8 @@ var import_chalk19 = __toESM(require("chalk"));
|
|
|
22047
22131
|
var import_child_process10 = require("child_process");
|
|
22048
22132
|
var import_execa3 = require("execa");
|
|
22049
22133
|
init_orchestrator();
|
|
22134
|
+
init_cloud();
|
|
22135
|
+
init_config();
|
|
22050
22136
|
init_provenance();
|
|
22051
22137
|
init_mcp_pin();
|
|
22052
22138
|
init_mcp_tools();
|
|
@@ -22075,6 +22161,60 @@ function normalizeClientName(name) {
|
|
|
22075
22161
|
const sanitized = sanitize4(name).slice(0, 40);
|
|
22076
22162
|
return sanitized.length > 0 ? sanitized : void 0;
|
|
22077
22163
|
}
|
|
22164
|
+
function reportPinMismatchToCloud(serverKey, agent) {
|
|
22165
|
+
try {
|
|
22166
|
+
const creds = getCredentials();
|
|
22167
|
+
if (!creds) return;
|
|
22168
|
+
void auditLocalAllow(
|
|
22169
|
+
`mcp-server:${serverKey}`,
|
|
22170
|
+
{ serverKey, reason: "tool-pin-mismatch" },
|
|
22171
|
+
"mcp-pin-mismatch",
|
|
22172
|
+
creds,
|
|
22173
|
+
{ mcpServer: serverKey, agent },
|
|
22174
|
+
void 0,
|
|
22175
|
+
false,
|
|
22176
|
+
{
|
|
22177
|
+
ruleName: "MCP tool definitions changed (possible rug pull)",
|
|
22178
|
+
ruleDescription: `The MCP server "${serverKey}" changed its tool definitions since they were pinned. This can indicate a supply-chain attack (tool poisoning). The session was quarantined. Review with: node9 mcp pin update ${serverKey}`
|
|
22179
|
+
}
|
|
22180
|
+
);
|
|
22181
|
+
} catch {
|
|
22182
|
+
}
|
|
22183
|
+
}
|
|
22184
|
+
function reportInventoryToCloud(serverKey, toolCount, agent) {
|
|
22185
|
+
try {
|
|
22186
|
+
const creds = getCredentials();
|
|
22187
|
+
if (!creds) return;
|
|
22188
|
+
void auditLocalAllow(
|
|
22189
|
+
`mcp-server:${serverKey}`,
|
|
22190
|
+
{ serverKey, toolCount },
|
|
22191
|
+
"mcp-discovered",
|
|
22192
|
+
creds,
|
|
22193
|
+
{ mcpServer: serverKey, agent },
|
|
22194
|
+
void 0,
|
|
22195
|
+
false,
|
|
22196
|
+
{ mcpToolCount: toolCount }
|
|
22197
|
+
);
|
|
22198
|
+
} catch {
|
|
22199
|
+
}
|
|
22200
|
+
}
|
|
22201
|
+
function reportLargeResponseToCloud(serverKey, responseBytes, agent) {
|
|
22202
|
+
try {
|
|
22203
|
+
const creds = getCredentials();
|
|
22204
|
+
if (!creds) return;
|
|
22205
|
+
void auditLocalAllow(
|
|
22206
|
+
`mcp-server:${serverKey}`,
|
|
22207
|
+
{ serverKey, responseBytes },
|
|
22208
|
+
"mcp-large-response",
|
|
22209
|
+
creds,
|
|
22210
|
+
{ mcpServer: serverKey, agent },
|
|
22211
|
+
void 0,
|
|
22212
|
+
false,
|
|
22213
|
+
{ mcpResponseBytes: responseBytes }
|
|
22214
|
+
);
|
|
22215
|
+
} catch {
|
|
22216
|
+
}
|
|
22217
|
+
}
|
|
22078
22218
|
function tokenize4(cmd) {
|
|
22079
22219
|
const tokens = [];
|
|
22080
22220
|
let current = "";
|
|
@@ -22335,6 +22475,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
22335
22475
|
const currentHash = hashToolDefinitions(tools);
|
|
22336
22476
|
const pinStatus = checkPin(serverKey, currentHash, gatewayCwd);
|
|
22337
22477
|
const token = getInternalToken();
|
|
22478
|
+
reportInventoryToCloud(serverKey, tools.length, clientName);
|
|
22338
22479
|
if (isDaemonRunning() && process.env.NODE9_TESTING !== "1") {
|
|
22339
22480
|
const toolSummary = tools.map((t) => ({ name: t.name, description: t.description }));
|
|
22340
22481
|
fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/mcp/discovered`, {
|
|
@@ -22399,6 +22540,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
22399
22540
|
console.error(import_chalk19.default.red(" Session quarantined \u2014 all tool calls blocked."));
|
|
22400
22541
|
console.error(import_chalk19.default.yellow(` Run: node9 mcp pin update ${serverKey}
|
|
22401
22542
|
`));
|
|
22543
|
+
reportPinMismatchToCloud(serverKey, clientName);
|
|
22402
22544
|
const errorResponse = {
|
|
22403
22545
|
jsonrpc: "2.0",
|
|
22404
22546
|
id: parsed.id,
|
|
@@ -22444,6 +22586,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
22444
22586
|
`\u26A1 Node9: Large MCP response from '${toolName}' (${(line.length / 1024).toFixed(0)}KB) \u2014 context window enlarged`
|
|
22445
22587
|
)
|
|
22446
22588
|
);
|
|
22589
|
+
reportLargeResponseToCloud(serverKey, line.length, clientName);
|
|
22447
22590
|
if (isDaemonRunning() && process.env.NODE9_TESTING !== "1") {
|
|
22448
22591
|
const token = getInternalToken();
|
|
22449
22592
|
fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/mcp/large-response`, {
|
package/dist/cli.mjs
CHANGED
|
@@ -5869,6 +5869,57 @@ function validateApiUrl(raw) {
|
|
|
5869
5869
|
}
|
|
5870
5870
|
return null;
|
|
5871
5871
|
}
|
|
5872
|
+
function auditLocalAllow(toolName, args, checkedBy, creds, meta, dlpInfo, containsSensitiveArgs = false, riskMetadata) {
|
|
5873
|
+
const validated = validateApiUrl(creds.apiUrl);
|
|
5874
|
+
if (!validated) {
|
|
5875
|
+
try {
|
|
5876
|
+
fs10.appendFileSync(
|
|
5877
|
+
HOOK_DEBUG_LOG,
|
|
5878
|
+
`[audit] refused to send: invalid apiUrl scheme/host (got "${String(creds.apiUrl).slice(0, 200)}")
|
|
5879
|
+
`
|
|
5880
|
+
);
|
|
5881
|
+
} catch {
|
|
5882
|
+
}
|
|
5883
|
+
return Promise.resolve();
|
|
5884
|
+
}
|
|
5885
|
+
const safeArgs = containsSensitiveArgs ? { tool: toolName, redacted: true } : args;
|
|
5886
|
+
const dlpSample = dlpInfo && typeof dlpInfo.redactedSample === "string" ? dlpInfo.redactedSample.slice(0, DLP_SAMPLE_MAX_LEN) : void 0;
|
|
5887
|
+
const dlpPattern = dlpInfo && typeof dlpInfo.pattern === "string" ? dlpInfo.pattern.slice(0, DLP_PATTERN_MAX_LEN) : void 0;
|
|
5888
|
+
const safeCheckedBy = KNOWN_CHECKED_BY.has(checkedBy) ? checkedBy : "unknown";
|
|
5889
|
+
const cleanedRiskMetadata = riskMetadata ? Object.fromEntries(
|
|
5890
|
+
Object.entries(riskMetadata).filter(
|
|
5891
|
+
([, v]) => typeof v === "string" && v.length > 0 || typeof v === "number" && Number.isFinite(v)
|
|
5892
|
+
)
|
|
5893
|
+
) : void 0;
|
|
5894
|
+
const hasRiskMetadata = cleanedRiskMetadata && Object.keys(cleanedRiskMetadata).length > 0;
|
|
5895
|
+
return fetch(`${validated.toString().replace(/\/$/, "")}/audit`, {
|
|
5896
|
+
method: "POST",
|
|
5897
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${creds.apiKey}` },
|
|
5898
|
+
body: JSON.stringify({
|
|
5899
|
+
toolName,
|
|
5900
|
+
args: safeArgs,
|
|
5901
|
+
checkedBy: safeCheckedBy,
|
|
5902
|
+
...dlpInfo && { dlpPattern, dlpSample },
|
|
5903
|
+
...hasRiskMetadata && { riskMetadata: cleanedRiskMetadata },
|
|
5904
|
+
// session_id (Claude Code + Gemini CLI) groups all audit rows from one
|
|
5905
|
+
// agent run; transcript_path is the authoritative pointer to the
|
|
5906
|
+
// session log (survives Gemini resume drift). Both optional —
|
|
5907
|
+
// unsupported agents (MCP-mediated) leave them undefined.
|
|
5908
|
+
...meta?.sessionId && { runId: meta.sessionId },
|
|
5909
|
+
...meta?.transcriptPath && { transcriptPath: meta.transcriptPath },
|
|
5910
|
+
context: {
|
|
5911
|
+
agent: meta?.agent,
|
|
5912
|
+
mcpServer: meta?.mcpServer,
|
|
5913
|
+
hostname: os9.hostname(),
|
|
5914
|
+
cwd: process.cwd(),
|
|
5915
|
+
platform: os9.platform()
|
|
5916
|
+
}
|
|
5917
|
+
}),
|
|
5918
|
+
signal: AbortSignal.timeout(5e3)
|
|
5919
|
+
}).then(() => {
|
|
5920
|
+
}).catch(() => {
|
|
5921
|
+
});
|
|
5922
|
+
}
|
|
5872
5923
|
async function initNode9SaaS(toolName, args, creds, meta, riskMetadata, agentPolicy, forceReview) {
|
|
5873
5924
|
const controller = new AbortController();
|
|
5874
5925
|
const timeout = setTimeout(() => controller.abort(), 1e4);
|
|
@@ -5992,10 +6043,44 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
|
|
|
5992
6043
|
);
|
|
5993
6044
|
}
|
|
5994
6045
|
}
|
|
6046
|
+
var DLP_SAMPLE_MAX_LEN, DLP_PATTERN_MAX_LEN, KNOWN_CHECKED_BY;
|
|
5995
6047
|
var init_cloud = __esm({
|
|
5996
6048
|
"src/auth/cloud.ts"() {
|
|
5997
6049
|
"use strict";
|
|
5998
6050
|
init_audit();
|
|
6051
|
+
DLP_SAMPLE_MAX_LEN = 200;
|
|
6052
|
+
DLP_PATTERN_MAX_LEN = 100;
|
|
6053
|
+
KNOWN_CHECKED_BY = /* @__PURE__ */ new Set([
|
|
6054
|
+
"dlp-block",
|
|
6055
|
+
"observe-mode-dlp-would-block",
|
|
6056
|
+
"dlp-review-flagged",
|
|
6057
|
+
"loop-detected",
|
|
6058
|
+
"audit-mode",
|
|
6059
|
+
"local-policy",
|
|
6060
|
+
"smart-rule-block",
|
|
6061
|
+
// Smart-rule block was downgraded to review because the daemon was
|
|
6062
|
+
// running and we're not in CI. The block attempt is still recorded;
|
|
6063
|
+
// the user got a popup. Distinct from 'smart-rule-block' so the
|
|
6064
|
+
// dashboard can show "block rule overridden" separately from a hard
|
|
6065
|
+
// block that fired with no human in the loop.
|
|
6066
|
+
"smart-rule-block-override",
|
|
6067
|
+
"persistent",
|
|
6068
|
+
"trust",
|
|
6069
|
+
"observe-mode",
|
|
6070
|
+
"observe-mode-would-block",
|
|
6071
|
+
// MCP supply-chain: the gateway pinned a server's tool definitions and they
|
|
6072
|
+
// changed since (possible tool poisoning / rug pull). Emitted as a synthetic
|
|
6073
|
+
// audit row so the SaaS surfaces it as a blocked event. The firewall maps
|
|
6074
|
+
// this checkedBy to AUTO_BLOCKED. See doc/roadmap/active/saas-value-first.md
|
|
6075
|
+
// (workstream B-Tier2).
|
|
6076
|
+
"mcp-pin-mismatch",
|
|
6077
|
+
// MCP visibility (B-Tier2, informational — NOT blocks): the gateway
|
|
6078
|
+
// discovered a server's tool inventory (mcp-discovered) or saw an oversized
|
|
6079
|
+
// tool response that bloats the context window (mcp-large-response). Stored
|
|
6080
|
+
// AUTO_ALLOWED; carries mcpToolCount / mcpResponseBytes in riskMetadata.
|
|
6081
|
+
"mcp-discovered",
|
|
6082
|
+
"mcp-large-response"
|
|
6083
|
+
]);
|
|
5999
6084
|
}
|
|
6000
6085
|
});
|
|
6001
6086
|
|
|
@@ -22014,6 +22099,8 @@ function registerUndoCommand(program2) {
|
|
|
22014
22099
|
|
|
22015
22100
|
// src/mcp-gateway/index.ts
|
|
22016
22101
|
init_orchestrator();
|
|
22102
|
+
init_cloud();
|
|
22103
|
+
init_config();
|
|
22017
22104
|
import readline4 from "readline";
|
|
22018
22105
|
import chalk19 from "chalk";
|
|
22019
22106
|
import { spawn as spawn7 } from "child_process";
|
|
@@ -22046,6 +22133,60 @@ function normalizeClientName(name) {
|
|
|
22046
22133
|
const sanitized = sanitize4(name).slice(0, 40);
|
|
22047
22134
|
return sanitized.length > 0 ? sanitized : void 0;
|
|
22048
22135
|
}
|
|
22136
|
+
function reportPinMismatchToCloud(serverKey, agent) {
|
|
22137
|
+
try {
|
|
22138
|
+
const creds = getCredentials();
|
|
22139
|
+
if (!creds) return;
|
|
22140
|
+
void auditLocalAllow(
|
|
22141
|
+
`mcp-server:${serverKey}`,
|
|
22142
|
+
{ serverKey, reason: "tool-pin-mismatch" },
|
|
22143
|
+
"mcp-pin-mismatch",
|
|
22144
|
+
creds,
|
|
22145
|
+
{ mcpServer: serverKey, agent },
|
|
22146
|
+
void 0,
|
|
22147
|
+
false,
|
|
22148
|
+
{
|
|
22149
|
+
ruleName: "MCP tool definitions changed (possible rug pull)",
|
|
22150
|
+
ruleDescription: `The MCP server "${serverKey}" changed its tool definitions since they were pinned. This can indicate a supply-chain attack (tool poisoning). The session was quarantined. Review with: node9 mcp pin update ${serverKey}`
|
|
22151
|
+
}
|
|
22152
|
+
);
|
|
22153
|
+
} catch {
|
|
22154
|
+
}
|
|
22155
|
+
}
|
|
22156
|
+
function reportInventoryToCloud(serverKey, toolCount, agent) {
|
|
22157
|
+
try {
|
|
22158
|
+
const creds = getCredentials();
|
|
22159
|
+
if (!creds) return;
|
|
22160
|
+
void auditLocalAllow(
|
|
22161
|
+
`mcp-server:${serverKey}`,
|
|
22162
|
+
{ serverKey, toolCount },
|
|
22163
|
+
"mcp-discovered",
|
|
22164
|
+
creds,
|
|
22165
|
+
{ mcpServer: serverKey, agent },
|
|
22166
|
+
void 0,
|
|
22167
|
+
false,
|
|
22168
|
+
{ mcpToolCount: toolCount }
|
|
22169
|
+
);
|
|
22170
|
+
} catch {
|
|
22171
|
+
}
|
|
22172
|
+
}
|
|
22173
|
+
function reportLargeResponseToCloud(serverKey, responseBytes, agent) {
|
|
22174
|
+
try {
|
|
22175
|
+
const creds = getCredentials();
|
|
22176
|
+
if (!creds) return;
|
|
22177
|
+
void auditLocalAllow(
|
|
22178
|
+
`mcp-server:${serverKey}`,
|
|
22179
|
+
{ serverKey, responseBytes },
|
|
22180
|
+
"mcp-large-response",
|
|
22181
|
+
creds,
|
|
22182
|
+
{ mcpServer: serverKey, agent },
|
|
22183
|
+
void 0,
|
|
22184
|
+
false,
|
|
22185
|
+
{ mcpResponseBytes: responseBytes }
|
|
22186
|
+
);
|
|
22187
|
+
} catch {
|
|
22188
|
+
}
|
|
22189
|
+
}
|
|
22049
22190
|
function tokenize4(cmd) {
|
|
22050
22191
|
const tokens = [];
|
|
22051
22192
|
let current = "";
|
|
@@ -22306,6 +22447,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
22306
22447
|
const currentHash = hashToolDefinitions(tools);
|
|
22307
22448
|
const pinStatus = checkPin(serverKey, currentHash, gatewayCwd);
|
|
22308
22449
|
const token = getInternalToken();
|
|
22450
|
+
reportInventoryToCloud(serverKey, tools.length, clientName);
|
|
22309
22451
|
if (isDaemonRunning() && process.env.NODE9_TESTING !== "1") {
|
|
22310
22452
|
const toolSummary = tools.map((t) => ({ name: t.name, description: t.description }));
|
|
22311
22453
|
fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/mcp/discovered`, {
|
|
@@ -22370,6 +22512,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
22370
22512
|
console.error(chalk19.red(" Session quarantined \u2014 all tool calls blocked."));
|
|
22371
22513
|
console.error(chalk19.yellow(` Run: node9 mcp pin update ${serverKey}
|
|
22372
22514
|
`));
|
|
22515
|
+
reportPinMismatchToCloud(serverKey, clientName);
|
|
22373
22516
|
const errorResponse = {
|
|
22374
22517
|
jsonrpc: "2.0",
|
|
22375
22518
|
id: parsed.id,
|
|
@@ -22415,6 +22558,7 @@ async function runMcpGateway(upstreamCommand) {
|
|
|
22415
22558
|
`\u26A1 Node9: Large MCP response from '${toolName}' (${(line.length / 1024).toFixed(0)}KB) \u2014 context window enlarged`
|
|
22416
22559
|
)
|
|
22417
22560
|
);
|
|
22561
|
+
reportLargeResponseToCloud(serverKey, line.length, clientName);
|
|
22418
22562
|
if (isDaemonRunning() && process.env.NODE9_TESTING !== "1") {
|
|
22419
22563
|
const token = getInternalToken();
|
|
22420
22564
|
fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/mcp/large-response`, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node9/proxy",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.35.0",
|
|
4
4
|
"description": "The Sudo Command for AI Agents. Execution Security for Claude Code, Codex, Gemini, Cursor, Opencode, Pi, and any MCP server.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|