@gethelio/proxy 0.1.1 → 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/cli.js +82 -51
- package/dist/index.d.ts +1 -0
- package/dist/index.js +45 -14
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -47,7 +47,8 @@ var upstreamSchema = z.object({
|
|
|
47
47
|
args: z.array(z.string()).optional(),
|
|
48
48
|
connect_timeout: durationSchema.default("10s"),
|
|
49
49
|
request_timeout: durationSchema.default("30s"),
|
|
50
|
-
forward_headers: z.array(z.string().min(1)).default([])
|
|
50
|
+
forward_headers: z.array(z.string().min(1)).default([]),
|
|
51
|
+
headers: z.record(z.string(), z.string()).default({})
|
|
51
52
|
}).refine((data) => data.transport !== "stdio" || data.command !== void 0, {
|
|
52
53
|
message: '"command" is required when transport is "stdio"',
|
|
53
54
|
path: ["command"]
|
|
@@ -61,6 +62,22 @@ var upstreamSchema = z.object({
|
|
|
61
62
|
});
|
|
62
63
|
}
|
|
63
64
|
}
|
|
65
|
+
const reserved = /* @__PURE__ */ new Set([
|
|
66
|
+
"mcp-session-id",
|
|
67
|
+
"mcp-protocol-version",
|
|
68
|
+
"content-type",
|
|
69
|
+
"content-length",
|
|
70
|
+
"host"
|
|
71
|
+
]);
|
|
72
|
+
for (const name of Object.keys(data.headers)) {
|
|
73
|
+
if (reserved.has(name.toLowerCase())) {
|
|
74
|
+
ctx.addIssue({
|
|
75
|
+
code: "custom",
|
|
76
|
+
path: ["headers", name],
|
|
77
|
+
message: `upstream.headers must not set reserved header "${name}"`
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
64
81
|
});
|
|
65
82
|
var listenSchema = z.object({
|
|
66
83
|
port: z.number().int().min(1).max(65535).default(3e3),
|
|
@@ -1237,6 +1254,20 @@ function describeUnreachableUpstream(error, url) {
|
|
|
1237
1254
|
);
|
|
1238
1255
|
}
|
|
1239
1256
|
|
|
1257
|
+
// src/upstream/merge-headers.ts
|
|
1258
|
+
function mergeUpstreamHeaders(base, forwarded, staticHeaders) {
|
|
1259
|
+
const out = {};
|
|
1260
|
+
const apply = (headers) => {
|
|
1261
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
1262
|
+
out[name.toLowerCase()] = value;
|
|
1263
|
+
}
|
|
1264
|
+
};
|
|
1265
|
+
apply(base);
|
|
1266
|
+
apply(forwarded);
|
|
1267
|
+
apply(staticHeaders);
|
|
1268
|
+
return out;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1240
1271
|
// src/upstream/forwarder.ts
|
|
1241
1272
|
var UpstreamForwarder = class {
|
|
1242
1273
|
url;
|
|
@@ -1248,13 +1279,14 @@ var UpstreamForwarder = class {
|
|
|
1248
1279
|
this.requestTimeoutMs = options.requestTimeoutMs ?? 3e4;
|
|
1249
1280
|
}
|
|
1250
1281
|
async forward(request) {
|
|
1251
|
-
const
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1282
|
+
const headers = mergeUpstreamHeaders(
|
|
1283
|
+
{
|
|
1284
|
+
"content-type": "application/json",
|
|
1285
|
+
accept: "application/json, text/event-stream"
|
|
1286
|
+
},
|
|
1287
|
+
request.headers ?? {},
|
|
1288
|
+
this.staticHeaders
|
|
1289
|
+
);
|
|
1258
1290
|
if (request.sessionId) {
|
|
1259
1291
|
headers["mcp-session-id"] = request.sessionId;
|
|
1260
1292
|
}
|
|
@@ -1460,12 +1492,11 @@ var SseUpstreamForwarder = class {
|
|
|
1460
1492
|
};
|
|
1461
1493
|
if (request.id !== void 0) body["id"] = request.id;
|
|
1462
1494
|
if (request.params !== void 0) body["params"] = request.params;
|
|
1463
|
-
const
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
};
|
|
1495
|
+
const headers = mergeUpstreamHeaders(
|
|
1496
|
+
{ "content-type": "application/json" },
|
|
1497
|
+
request.headers ?? {},
|
|
1498
|
+
this.staticHeaders
|
|
1499
|
+
);
|
|
1469
1500
|
if (request.sessionId) {
|
|
1470
1501
|
headers["mcp-session-id"] = request.sessionId;
|
|
1471
1502
|
}
|
|
@@ -1777,6 +1808,40 @@ var StdioForwarder = class {
|
|
|
1777
1808
|
}
|
|
1778
1809
|
};
|
|
1779
1810
|
|
|
1811
|
+
// src/cli-forwarder.ts
|
|
1812
|
+
async function createForwarderFromConfig(config) {
|
|
1813
|
+
switch (config.upstream.transport) {
|
|
1814
|
+
case "streamable-http": {
|
|
1815
|
+
return {
|
|
1816
|
+
forwarder: new UpstreamForwarder({
|
|
1817
|
+
url: config.upstream.url,
|
|
1818
|
+
headers: config.upstream.headers,
|
|
1819
|
+
requestTimeoutMs: parseDuration(config.upstream.request_timeout)
|
|
1820
|
+
})
|
|
1821
|
+
};
|
|
1822
|
+
}
|
|
1823
|
+
case "sse": {
|
|
1824
|
+
const sse = new SseUpstreamForwarder({
|
|
1825
|
+
url: config.upstream.url,
|
|
1826
|
+
headers: config.upstream.headers,
|
|
1827
|
+
connectTimeoutMs: parseDuration(config.upstream.connect_timeout),
|
|
1828
|
+
requestTimeoutMs: parseDuration(config.upstream.request_timeout)
|
|
1829
|
+
});
|
|
1830
|
+
await sse.connect();
|
|
1831
|
+
return { forwarder: sse, close: () => sse.close() };
|
|
1832
|
+
}
|
|
1833
|
+
case "stdio": {
|
|
1834
|
+
const stdio = new StdioForwarder({
|
|
1835
|
+
command: config.upstream.command,
|
|
1836
|
+
args: config.upstream.args,
|
|
1837
|
+
requestTimeoutMs: parseDuration(config.upstream.request_timeout)
|
|
1838
|
+
});
|
|
1839
|
+
await stdio.start();
|
|
1840
|
+
return { forwarder: stdio, close: () => stdio.close() };
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1780
1845
|
// src/policy/matchers.ts
|
|
1781
1846
|
var ANNOTATION_DEFAULTS = {
|
|
1782
1847
|
readOnlyHint: false,
|
|
@@ -5880,6 +5945,8 @@ upstream:
|
|
|
5880
5945
|
url: "http://localhost:8080/mcp"
|
|
5881
5946
|
# Transport: streamable-http (default), sse, or stdio
|
|
5882
5947
|
transport: streamable-http
|
|
5948
|
+
# headers:
|
|
5949
|
+
# Authorization: "Bearer \${UPSTREAM_TOKEN}"
|
|
5883
5950
|
|
|
5884
5951
|
# Operator dashboard + approval REST API. Bound to 127.0.0.1 by default \u2014 do
|
|
5885
5952
|
# not change to 0.0.0.0 without putting an authenticating reverse proxy in
|
|
@@ -6027,43 +6094,7 @@ async function startCommand(configPath, options) {
|
|
|
6027
6094
|
);
|
|
6028
6095
|
process.exit(1);
|
|
6029
6096
|
}
|
|
6030
|
-
|
|
6031
|
-
let closeForwarder;
|
|
6032
|
-
switch (config.upstream.transport) {
|
|
6033
|
-
case "streamable-http": {
|
|
6034
|
-
forwarder = new UpstreamForwarder({
|
|
6035
|
-
url: config.upstream.url,
|
|
6036
|
-
requestTimeoutMs: parseDuration(config.upstream.request_timeout)
|
|
6037
|
-
});
|
|
6038
|
-
break;
|
|
6039
|
-
}
|
|
6040
|
-
case "stdio": {
|
|
6041
|
-
if (!config.upstream.command) {
|
|
6042
|
-
console.error('Error: "command" is required for stdio transport');
|
|
6043
|
-
process.exit(1);
|
|
6044
|
-
}
|
|
6045
|
-
const stdio = new StdioForwarder({
|
|
6046
|
-
command: config.upstream.command,
|
|
6047
|
-
args: config.upstream.args,
|
|
6048
|
-
requestTimeoutMs: parseDuration(config.upstream.request_timeout)
|
|
6049
|
-
});
|
|
6050
|
-
await stdio.start();
|
|
6051
|
-
forwarder = stdio;
|
|
6052
|
-
closeForwarder = () => stdio.close();
|
|
6053
|
-
break;
|
|
6054
|
-
}
|
|
6055
|
-
case "sse": {
|
|
6056
|
-
const sse = new SseUpstreamForwarder({
|
|
6057
|
-
url: config.upstream.url,
|
|
6058
|
-
connectTimeoutMs: parseDuration(config.upstream.connect_timeout),
|
|
6059
|
-
requestTimeoutMs: parseDuration(config.upstream.request_timeout)
|
|
6060
|
-
});
|
|
6061
|
-
await sse.connect();
|
|
6062
|
-
forwarder = sse;
|
|
6063
|
-
closeForwarder = () => sse.close();
|
|
6064
|
-
break;
|
|
6065
|
-
}
|
|
6066
|
-
}
|
|
6097
|
+
const { forwarder, close: closeForwarder } = await createForwarderFromConfig(config);
|
|
6067
6098
|
const { policy, warnings } = compilePolicies(config.policies);
|
|
6068
6099
|
for (const w of warnings) {
|
|
6069
6100
|
const label = w.ruleName ? `rule "${w.ruleName}"` : `rule ${String(w.ruleIndex)}`;
|
package/dist/index.d.ts
CHANGED
|
@@ -113,6 +113,7 @@ declare const helioConfigSchema: z.ZodObject<{
|
|
|
113
113
|
connect_timeout: z.ZodDefault<z.ZodString>;
|
|
114
114
|
request_timeout: z.ZodDefault<z.ZodString>;
|
|
115
115
|
forward_headers: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
116
|
+
headers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
116
117
|
}, z.core.$strip>;
|
|
117
118
|
listen: z.ZodPrefault<z.ZodObject<{
|
|
118
119
|
port: z.ZodDefault<z.ZodNumber>;
|
package/dist/index.js
CHANGED
|
@@ -37,7 +37,8 @@ var upstreamSchema = z.object({
|
|
|
37
37
|
args: z.array(z.string()).optional(),
|
|
38
38
|
connect_timeout: durationSchema.default("10s"),
|
|
39
39
|
request_timeout: durationSchema.default("30s"),
|
|
40
|
-
forward_headers: z.array(z.string().min(1)).default([])
|
|
40
|
+
forward_headers: z.array(z.string().min(1)).default([]),
|
|
41
|
+
headers: z.record(z.string(), z.string()).default({})
|
|
41
42
|
}).refine((data) => data.transport !== "stdio" || data.command !== void 0, {
|
|
42
43
|
message: '"command" is required when transport is "stdio"',
|
|
43
44
|
path: ["command"]
|
|
@@ -51,6 +52,22 @@ var upstreamSchema = z.object({
|
|
|
51
52
|
});
|
|
52
53
|
}
|
|
53
54
|
}
|
|
55
|
+
const reserved = /* @__PURE__ */ new Set([
|
|
56
|
+
"mcp-session-id",
|
|
57
|
+
"mcp-protocol-version",
|
|
58
|
+
"content-type",
|
|
59
|
+
"content-length",
|
|
60
|
+
"host"
|
|
61
|
+
]);
|
|
62
|
+
for (const name of Object.keys(data.headers)) {
|
|
63
|
+
if (reserved.has(name.toLowerCase())) {
|
|
64
|
+
ctx.addIssue({
|
|
65
|
+
code: "custom",
|
|
66
|
+
path: ["headers", name],
|
|
67
|
+
message: `upstream.headers must not set reserved header "${name}"`
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
54
71
|
});
|
|
55
72
|
var listenSchema = z.object({
|
|
56
73
|
port: z.number().int().min(1).max(65535).default(3e3),
|
|
@@ -1131,6 +1148,20 @@ function describeUnreachableUpstream(error, url) {
|
|
|
1131
1148
|
);
|
|
1132
1149
|
}
|
|
1133
1150
|
|
|
1151
|
+
// src/upstream/merge-headers.ts
|
|
1152
|
+
function mergeUpstreamHeaders(base, forwarded, staticHeaders) {
|
|
1153
|
+
const out = {};
|
|
1154
|
+
const apply = (headers) => {
|
|
1155
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
1156
|
+
out[name.toLowerCase()] = value;
|
|
1157
|
+
}
|
|
1158
|
+
};
|
|
1159
|
+
apply(base);
|
|
1160
|
+
apply(forwarded);
|
|
1161
|
+
apply(staticHeaders);
|
|
1162
|
+
return out;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1134
1165
|
// src/upstream/forwarder.ts
|
|
1135
1166
|
var UpstreamForwarder = class {
|
|
1136
1167
|
url;
|
|
@@ -1142,13 +1173,14 @@ var UpstreamForwarder = class {
|
|
|
1142
1173
|
this.requestTimeoutMs = options.requestTimeoutMs ?? 3e4;
|
|
1143
1174
|
}
|
|
1144
1175
|
async forward(request) {
|
|
1145
|
-
const
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1176
|
+
const headers = mergeUpstreamHeaders(
|
|
1177
|
+
{
|
|
1178
|
+
"content-type": "application/json",
|
|
1179
|
+
accept: "application/json, text/event-stream"
|
|
1180
|
+
},
|
|
1181
|
+
request.headers ?? {},
|
|
1182
|
+
this.staticHeaders
|
|
1183
|
+
);
|
|
1152
1184
|
if (request.sessionId) {
|
|
1153
1185
|
headers["mcp-session-id"] = request.sessionId;
|
|
1154
1186
|
}
|
|
@@ -1354,12 +1386,11 @@ var SseUpstreamForwarder = class {
|
|
|
1354
1386
|
};
|
|
1355
1387
|
if (request.id !== void 0) body["id"] = request.id;
|
|
1356
1388
|
if (request.params !== void 0) body["params"] = request.params;
|
|
1357
|
-
const
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
};
|
|
1389
|
+
const headers = mergeUpstreamHeaders(
|
|
1390
|
+
{ "content-type": "application/json" },
|
|
1391
|
+
request.headers ?? {},
|
|
1392
|
+
this.staticHeaders
|
|
1393
|
+
);
|
|
1363
1394
|
if (request.sessionId) {
|
|
1364
1395
|
headers["mcp-session-id"] = request.sessionId;
|
|
1365
1396
|
}
|