@wootsup/yt-builder-mcp 1.2.0 → 1.4.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/README.md +16 -12
- package/dist/catalog/build-catalog.d.ts +63 -0
- package/dist/catalog/build-catalog.d.ts.map +1 -0
- package/dist/catalog/build-catalog.js +166 -0
- package/dist/catalog/build-catalog.js.map +1 -0
- package/dist/cli/doctor-command.d.ts +21 -0
- package/dist/cli/doctor-command.d.ts.map +1 -0
- package/dist/cli/doctor-command.js +111 -0
- package/dist/cli/doctor-command.js.map +1 -0
- package/dist/cli/invocation-decision.d.ts.map +1 -1
- package/dist/cli/invocation-decision.js +2 -0
- package/dist/cli/invocation-decision.js.map +1 -1
- package/dist/client.d.ts +14 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +9 -0
- package/dist/client.js.map +1 -1
- package/dist/diagnostics/ca-trust.d.ts +16 -0
- package/dist/diagnostics/ca-trust.d.ts.map +1 -0
- package/dist/diagnostics/ca-trust.js +54 -0
- package/dist/diagnostics/ca-trust.js.map +1 -0
- package/dist/diagnostics/diagnose-network-error.d.ts +37 -0
- package/dist/diagnostics/diagnose-network-error.d.ts.map +1 -0
- package/dist/diagnostics/diagnose-network-error.js +158 -0
- package/dist/diagnostics/diagnose-network-error.js.map +1 -0
- package/dist/diagnostics/doctor.d.ts +62 -0
- package/dist/diagnostics/doctor.d.ts.map +1 -0
- package/dist/diagnostics/doctor.js +165 -0
- package/dist/diagnostics/doctor.js.map +1 -0
- package/dist/gateway/advanced-read-tool.d.ts +37 -0
- package/dist/gateway/advanced-read-tool.d.ts.map +1 -0
- package/dist/gateway/advanced-read-tool.js +156 -0
- package/dist/gateway/advanced-read-tool.js.map +1 -0
- package/dist/gateway/advanced-tool/domains.d.ts +18 -3
- package/dist/gateway/advanced-tool/domains.d.ts.map +1 -1
- package/dist/gateway/advanced-tool/domains.js +28 -9
- package/dist/gateway/advanced-tool/domains.js.map +1 -1
- package/dist/gateway/advanced-tool/register.d.ts.map +1 -1
- package/dist/gateway/advanced-tool/register.js +43 -11
- package/dist/gateway/advanced-tool/register.js.map +1 -1
- package/dist/gateway/essentials.d.ts +14 -4
- package/dist/gateway/essentials.d.ts.map +1 -1
- package/dist/gateway/essentials.js +46 -5
- package/dist/gateway/essentials.js.map +1 -1
- package/dist/gateway/test-support.d.ts +2 -2
- package/dist/gateway/test-support.js +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -1
- package/dist/install-skill.d.ts +1 -1
- package/dist/install-skill.js +2 -2
- package/dist/net/extra-headers.d.ts +66 -0
- package/dist/net/extra-headers.d.ts.map +1 -0
- package/dist/net/extra-headers.js +164 -0
- package/dist/net/extra-headers.js.map +1 -0
- package/dist/net/internal-host.d.ts +45 -0
- package/dist/net/internal-host.d.ts.map +1 -0
- package/dist/net/internal-host.js +164 -0
- package/dist/net/internal-host.js.map +1 -0
- package/dist/net/proxy-dispatcher.d.ts +40 -0
- package/dist/net/proxy-dispatcher.d.ts.map +1 -0
- package/dist/net/proxy-dispatcher.js +65 -0
- package/dist/net/proxy-dispatcher.js.map +1 -0
- package/dist/proxy/bridge.d.ts +43 -0
- package/dist/proxy/bridge.d.ts.map +1 -0
- package/dist/proxy/bridge.js +155 -0
- package/dist/proxy/bridge.js.map +1 -0
- package/dist/proxy/mode.d.ts +10 -0
- package/dist/proxy/mode.d.ts.map +1 -0
- package/dist/proxy/mode.js +20 -0
- package/dist/proxy/mode.js.map +1 -0
- package/dist/server.d.ts +5 -3
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +13 -3
- package/dist/server.js.map +1 -1
- package/dist/setup-ca-env.d.ts +40 -0
- package/dist/setup-ca-env.d.ts.map +1 -0
- package/dist/setup-ca-env.js +58 -0
- package/dist/setup-ca-env.js.map +1 -0
- package/dist/setup-ca-reprobe.d.ts +38 -0
- package/dist/setup-ca-reprobe.d.ts.map +1 -0
- package/dist/setup-ca-reprobe.js +68 -0
- package/dist/setup-ca-reprobe.js.map +1 -0
- package/dist/setup-cli.d.ts +37 -2
- package/dist/setup-cli.d.ts.map +1 -1
- package/dist/setup-cli.js +178 -4
- package/dist/setup-cli.js.map +1 -1
- package/dist/setup-npx-spec.d.ts +44 -0
- package/dist/setup-npx-spec.d.ts.map +1 -0
- package/dist/setup-npx-spec.js +66 -0
- package/dist/setup-npx-spec.js.map +1 -0
- package/dist/setup-prompts.d.ts +14 -1
- package/dist/setup-prompts.d.ts.map +1 -1
- package/dist/setup-prompts.js +53 -1
- package/dist/setup-prompts.js.map +1 -1
- package/dist/setup-wizard-defaults.d.ts.map +1 -1
- package/dist/setup-wizard-defaults.js +29 -5
- package/dist/setup-wizard-defaults.js.map +1 -1
- package/dist/setup-wizard-handshake.d.ts +4 -4
- package/dist/setup-wizard-handshake.d.ts.map +1 -1
- package/dist/setup-wizard-handshake.js +46 -12
- package/dist/setup-wizard-handshake.js.map +1 -1
- package/dist/setup-wizard-types.d.ts +81 -0
- package/dist/setup-wizard-types.d.ts.map +1 -1
- package/dist/setup-wizard.d.ts +2 -2
- package/dist/setup-wizard.d.ts.map +1 -1
- package/dist/setup-wizard.js +140 -18
- package/dist/setup-wizard.js.map +1 -1
- package/dist/sites/client-pool.d.ts +8 -1
- package/dist/sites/client-pool.d.ts.map +1 -1
- package/dist/sites/client-pool.js +34 -4
- package/dist/sites/client-pool.js.map +1 -1
- package/dist/sites/registry.d.ts +30 -0
- package/dist/sites/registry.d.ts.map +1 -1
- package/dist/sites/registry.js.map +1 -1
- package/dist/sites/store.d.ts +8 -1
- package/dist/sites/store.d.ts.map +1 -1
- package/dist/sites/store.js +17 -2
- package/dist/sites/store.js.map +1 -1
- package/dist/sites/tools/use-site.d.ts.map +1 -1
- package/dist/sites/tools/use-site.js +26 -6
- package/dist/sites/tools/use-site.js.map +1 -1
- package/dist/tools/bind-guidance.d.ts +80 -0
- package/dist/tools/bind-guidance.d.ts.map +1 -0
- package/dist/tools/bind-guidance.js +95 -0
- package/dist/tools/bind-guidance.js.map +1 -0
- package/dist/tools/elements/builders.d.ts.map +1 -1
- package/dist/tools/elements/builders.js +63 -13
- package/dist/tools/elements/builders.js.map +1 -1
- package/dist/tools/elements/handlers-write.d.ts +19 -2
- package/dist/tools/elements/handlers-write.d.ts.map +1 -1
- package/dist/tools/elements/handlers-write.js +179 -10
- package/dist/tools/elements/handlers-write.js.map +1 -1
- package/dist/tools/elements/handlers.d.ts +8 -2
- package/dist/tools/elements/handlers.d.ts.map +1 -1
- package/dist/tools/elements/handlers.js +76 -28
- package/dist/tools/elements/handlers.js.map +1 -1
- package/dist/tools/elements/layout-hierarchy-rules.d.ts +62 -0
- package/dist/tools/elements/layout-hierarchy-rules.d.ts.map +1 -0
- package/dist/tools/elements/layout-hierarchy-rules.js +154 -0
- package/dist/tools/elements/layout-hierarchy-rules.js.map +1 -0
- package/dist/tools/format/elements-format.d.ts +35 -0
- package/dist/tools/format/elements-format.d.ts.map +1 -1
- package/dist/tools/format/elements-format.js +124 -1
- package/dist/tools/format/elements-format.js.map +1 -1
- package/dist/tools/format/health-format.d.ts +3 -0
- package/dist/tools/format/health-format.d.ts.map +1 -1
- package/dist/tools/format/health-format.js +21 -4
- package/dist/tools/format/health-format.js.map +1 -1
- package/dist/tools/format/inspection-format.d.ts +18 -0
- package/dist/tools/format/inspection-format.d.ts.map +1 -1
- package/dist/tools/format/inspection-format.js +22 -1
- package/dist/tools/format/inspection-format.js.map +1 -1
- package/dist/tools/format/pages-format.d.ts.map +1 -1
- package/dist/tools/format/pages-format.js +7 -0
- package/dist/tools/format/pages-format.js.map +1 -1
- package/dist/tools/format/sources-format.d.ts +8 -0
- package/dist/tools/format/sources-format.d.ts.map +1 -1
- package/dist/tools/format/sources-format.js +10 -0
- package/dist/tools/format/sources-format.js.map +1 -1
- package/dist/tools/health.d.ts.map +1 -1
- package/dist/tools/health.js +56 -0
- package/dist/tools/health.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +4 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/inspection.d.ts +24 -0
- package/dist/tools/inspection.d.ts.map +1 -1
- package/dist/tools/inspection.js +140 -41
- package/dist/tools/inspection.js.map +1 -1
- package/dist/tools/layout-flatten.d.ts +11 -8
- package/dist/tools/layout-flatten.d.ts.map +1 -1
- package/dist/tools/layout-flatten.js +17 -6
- package/dist/tools/layout-flatten.js.map +1 -1
- package/dist/tools/multi-items/builders.d.ts.map +1 -1
- package/dist/tools/multi-items/builders.js +11 -2
- package/dist/tools/multi-items/builders.js.map +1 -1
- package/dist/tools/multi-items/inspect-handler.d.ts +13 -0
- package/dist/tools/multi-items/inspect-handler.d.ts.map +1 -1
- package/dist/tools/multi-items/inspect-handler.js +147 -3
- package/dist/tools/multi-items/inspect-handler.js.map +1 -1
- package/dist/tools/pages/builders.d.ts.map +1 -1
- package/dist/tools/pages/builders.js +153 -39
- package/dist/tools/pages/builders.js.map +1 -1
- package/dist/tools/pages/handlers-read.d.ts +40 -5
- package/dist/tools/pages/handlers-read.d.ts.map +1 -1
- package/dist/tools/pages/handlers-read.js +404 -76
- package/dist/tools/pages/handlers-read.js.map +1 -1
- package/dist/tools/pages/handlers-write.d.ts +95 -1
- package/dist/tools/pages/handlers-write.d.ts.map +1 -1
- package/dist/tools/pages/handlers-write.js +137 -8
- package/dist/tools/pages/handlers-write.js.map +1 -1
- package/dist/tools/pages/index.d.ts +1 -1
- package/dist/tools/pages/index.d.ts.map +1 -1
- package/dist/tools/pages/index.js +1 -1
- package/dist/tools/pages/index.js.map +1 -1
- package/dist/tools/pages/schemas.d.ts +74 -5
- package/dist/tools/pages/schemas.d.ts.map +1 -1
- package/dist/tools/pages/schemas.js +243 -8
- package/dist/tools/pages/schemas.js.map +1 -1
- package/dist/tools/shared-schemas.d.ts +3 -0
- package/dist/tools/shared-schemas.d.ts.map +1 -1
- package/dist/tools/shared-schemas.js +43 -13
- package/dist/tools/shared-schemas.js.map +1 -1
- package/dist/tools/sources/builders.d.ts.map +1 -1
- package/dist/tools/sources/builders.js +93 -10
- package/dist/tools/sources/builders.js.map +1 -1
- package/dist/tools/sources/handlers-bind.d.ts +13 -1
- package/dist/tools/sources/handlers-bind.d.ts.map +1 -1
- package/dist/tools/sources/handlers-bind.js +163 -1
- package/dist/tools/sources/handlers-bind.js.map +1 -1
- package/dist/tools/sources/handlers.d.ts +12 -1
- package/dist/tools/sources/handlers.d.ts.map +1 -1
- package/dist/tools/sources/handlers.js +103 -16
- package/dist/tools/sources/handlers.js.map +1 -1
- package/dist/tools/sparse-fields.d.ts +73 -0
- package/dist/tools/sparse-fields.d.ts.map +1 -1
- package/dist/tools/sparse-fields.js +136 -0
- package/dist/tools/sparse-fields.js.map +1 -1
- package/dist/tools/sublayout/builders.d.ts +23 -0
- package/dist/tools/sublayout/builders.d.ts.map +1 -0
- package/dist/tools/sublayout/builders.js +54 -0
- package/dist/tools/sublayout/builders.js.map +1 -0
- package/dist/tools/sublayout/index.d.ts +13 -0
- package/dist/tools/sublayout/index.d.ts.map +1 -0
- package/dist/tools/sublayout/index.js +13 -0
- package/dist/tools/sublayout/index.js.map +1 -0
- package/dist/tools/sublayout/inspect-sublayout-handler.d.ts +95 -0
- package/dist/tools/sublayout/inspect-sublayout-handler.d.ts.map +1 -0
- package/dist/tools/sublayout/inspect-sublayout-handler.js +181 -0
- package/dist/tools/sublayout/inspect-sublayout-handler.js.map +1 -0
- package/dist/tools/tool-builder/results.d.ts +31 -0
- package/dist/tools/tool-builder/results.d.ts.map +1 -1
- package/dist/tools/tool-builder/results.js +95 -4
- package/dist/tools/tool-builder/results.js.map +1 -1
- package/dist/update/update-check.d.ts +56 -0
- package/dist/update/update-check.d.ts.map +1 -0
- package/dist/update/update-check.js +155 -0
- package/dist/update/update-check.js.map +1 -0
- package/manifest.json +1 -1
- package/package.json +5 -2
- package/skills/yt-builder-mcp/SKILL.md +148 -82
package/dist/index.js
CHANGED
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
15
15
|
import { isTestMode, loadRegistry } from './auth.js';
|
|
16
16
|
import { ConfigError } from './errors.js';
|
|
17
|
+
import { installGlobalProxyDispatcher } from './net/proxy-dispatcher.js';
|
|
18
|
+
import { SERVER_VERSION } from './server.js';
|
|
19
|
+
import { checkForUpdate, formatUpdateNotice } from './update/update-check.js';
|
|
20
|
+
/** npm package name — used by the startup update-notice + the diagnose tool. */
|
|
21
|
+
const MCP_PACKAGE_NAME = '@wootsup/yt-builder-mcp';
|
|
17
22
|
import { bootstrapActiveSite } from './sites/bootstrap.js';
|
|
18
23
|
import { ClientPool } from './sites/client-pool.js';
|
|
19
24
|
import { SiteRegistry } from './sites/registry.js';
|
|
@@ -36,6 +41,46 @@ export { CapturingServer, } from './gateway/capturing-server.js';
|
|
|
36
41
|
export { registerAdvancedTool } from './gateway/advanced-tool.js';
|
|
37
42
|
export { collectAllRegisteredTools, findTool, } from './gateway/test-support.js';
|
|
38
43
|
async function main() {
|
|
44
|
+
// Honor an enterprise HTTP(S)_PROXY / NO_PROXY before any outbound call —
|
|
45
|
+
// BOTH proxy mode (below) and fat mode use Node's global fetch, which reads
|
|
46
|
+
// this global dispatcher. Unconditional + passthrough-safe (no proxy env =
|
|
47
|
+
// direct). Also auto-derives NO_PROXY for an internal site host (gap #9).
|
|
48
|
+
// See net/proxy-dispatcher.ts.
|
|
49
|
+
installGlobalProxyDispatcher();
|
|
50
|
+
// Gap #14 — surface any YTB_MCP_EXTRA_HEADERS parse warnings ONCE at startup
|
|
51
|
+
// (dropped lines / a forbidden Authorization key) so a misconfigured corp
|
|
52
|
+
// header isn't silently ignored. stderr only — stdout is the JSON-RPC channel.
|
|
53
|
+
if (!isTestMode()) {
|
|
54
|
+
const { parseExtraHeaders } = await import('./net/extra-headers.js');
|
|
55
|
+
for (const w of parseExtraHeaders().warnings) {
|
|
56
|
+
process.stderr.write(`[yt-builder-mcp] ${w}\n`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Update notice (non-blocking, cached 24h, fail-silent) — surfaces a newer
|
|
60
|
+
// npm version to stderr without ever delaying serving or breaking behind a
|
|
61
|
+
// corporate proxy. Skipped in test-mode (the smoke probe must not hit the
|
|
62
|
+
// network). stderr only — stdout is the JSON-RPC channel.
|
|
63
|
+
if (!isTestMode()) {
|
|
64
|
+
void checkForUpdate({ currentVersion: SERVER_VERSION, packageName: MCP_PACKAGE_NAME })
|
|
65
|
+
.then((status) => {
|
|
66
|
+
const notice = formatUpdateNotice(status);
|
|
67
|
+
if (notice !== null)
|
|
68
|
+
console.error(notice);
|
|
69
|
+
})
|
|
70
|
+
.catch(() => {
|
|
71
|
+
/* fail-silent — the notifier is a courtesy, never a dependency */
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
// Proxy mode: forward stdio 1:1 to the plugin-served PHP MCP endpoint and
|
|
75
|
+
// stop here — the fat catalog below is bypassed entirely. Default (fat) is
|
|
76
|
+
// unchanged. The dynamic import is gated on the env so the fat path never
|
|
77
|
+
// loads the proxy module (and the SDK client transport it pulls in) — keeps
|
|
78
|
+
// fat-mode startup lean and avoids loading proxy code when it can't apply.
|
|
79
|
+
if (process.env.YTB_MCP_MODE === 'proxy') {
|
|
80
|
+
const { maybeRunProxy } = await import('./proxy/bridge.js');
|
|
81
|
+
if (await maybeRunProxy(process.env))
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
39
84
|
const testMode = isTestMode();
|
|
40
85
|
// W10 — multi-site bootstrap is now centralised in
|
|
41
86
|
// `auth.loadRegistry()`. The canonical resolution order is:
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/install-skill.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Idempotent: re-running overwrites existing skill files and skips the
|
|
9
9
|
* AGENTS.md append when the marker is already present.
|
|
10
10
|
*
|
|
11
|
-
* Mirrors the pattern from `@wootsup/mcp install-skill` so users on both
|
|
11
|
+
* Mirrors the pattern from `@wootsup/apimapper-mcp install-skill` so users on both
|
|
12
12
|
* products get the same workflow.
|
|
13
13
|
*
|
|
14
14
|
* @license MIT
|
package/dist/install-skill.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Idempotent: re-running overwrites existing skill files and skips the
|
|
9
9
|
* AGENTS.md append when the marker is already present.
|
|
10
10
|
*
|
|
11
|
-
* Mirrors the pattern from `@wootsup/mcp install-skill` so users on both
|
|
11
|
+
* Mirrors the pattern from `@wootsup/apimapper-mcp install-skill` so users on both
|
|
12
12
|
* products get the same workflow.
|
|
13
13
|
*
|
|
14
14
|
* @license MIT
|
|
@@ -27,7 +27,7 @@ The YT Builder MCP skill is installed at \`~/.claude/skills/yt-builder-mcp/\`
|
|
|
27
27
|
— the universal-marker path recognised by Claude Desktop and other AI
|
|
28
28
|
clients that follow the \`~/AGENTS.md\` discovery protocol.
|
|
29
29
|
|
|
30
|
-
It documents the 30 MCP tools (29 domain + 1 gateway,
|
|
30
|
+
It documents the 30 MCP tools (29 domain + 1 gateway, 26 entries in
|
|
31
31
|
\`tools/list\`), the 5 canonical workflows (build hero, bind dynamic
|
|
32
32
|
source, clone section, diagnose 401, add custom element), and the
|
|
33
33
|
gateway routing model. Source: https://github.com/wootsup/yt-builder-mcp.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse `YTB_MCP_EXTRA_HEADERS` into a header map that is merged into EVERY
|
|
3
|
+
* outbound request on BOTH transports (fat-mode {@link RestClient} and
|
|
4
|
+
* proxy-mode `StreamableHTTPClientTransport`).
|
|
5
|
+
*
|
|
6
|
+
* Why this exists (Glöckle gap #14): a WordPress/Joomla site fronted by HTTP
|
|
7
|
+
* Basic-Auth, a WAF cookie, or a custom required header (e.g. a corporate
|
|
8
|
+
* gateway secret) cannot be reached — the only header we ever send is
|
|
9
|
+
* `Authorization: Bearer …`. This env var lets the operator attach the extra
|
|
10
|
+
* header(s) the gateway demands WITHOUT us inventing per-provider knobs.
|
|
11
|
+
*
|
|
12
|
+
* ── Accepted formats (auto-detected) ───────────────────────────────────────
|
|
13
|
+
* The value is read as ONE of two shapes, in this precedence:
|
|
14
|
+
*
|
|
15
|
+
* 1. **JSON object** — `{"X-WAF-Token":"abc","Cookie":"sid=42"}`.
|
|
16
|
+
* Used when the trimmed value starts with `{` AND parses as a non-array
|
|
17
|
+
* object. Non-string values are coerced via String(); null/array values
|
|
18
|
+
* are dropped (with a warning).
|
|
19
|
+
*
|
|
20
|
+
* 2. **`Name: value` lines** — one header per line, first `:` splits:
|
|
21
|
+
*
|
|
22
|
+
* X-WAF-Token: abc
|
|
23
|
+
* Cookie: sid=42
|
|
24
|
+
*
|
|
25
|
+
* Blank lines and lines starting with `#` (comments) are ignored. A line
|
|
26
|
+
* with no `:` is dropped with a warning.
|
|
27
|
+
*
|
|
28
|
+
* ── Hard invariants ─────────────────────────────────────────────────────────
|
|
29
|
+
* - **`Authorization` can NEVER be set here.** Any `Authorization` key (case-
|
|
30
|
+
* insensitive) in the parsed map is dropped with a warning — the Bearer is
|
|
31
|
+
* owned by the auth layer and an extra header must never overwrite it. This
|
|
32
|
+
* is the parse-side half of "Bearer always wins"; the merge-side half lives
|
|
33
|
+
* in {@link mergeExtraHeaders}.
|
|
34
|
+
* - Empty header NAMES are dropped. Empty VALUES are kept (a deliberately
|
|
35
|
+
* blank header is a legitimate, if rare, gateway requirement).
|
|
36
|
+
*
|
|
37
|
+
* Pure + deterministic. `warn` collects non-fatal issues; the caller decides
|
|
38
|
+
* whether to surface them (the server logs them once to stderr at startup).
|
|
39
|
+
*
|
|
40
|
+
* @license MIT
|
|
41
|
+
*/
|
|
42
|
+
/** Canonical env-var name for the extra-headers feature. */
|
|
43
|
+
export declare const ENV_EXTRA_HEADERS = "YTB_MCP_EXTRA_HEADERS";
|
|
44
|
+
export interface ParsedExtraHeaders {
|
|
45
|
+
/** The accepted header map (never contains `Authorization`). */
|
|
46
|
+
readonly headers: Record<string, string>;
|
|
47
|
+
/** Non-fatal issues (dropped lines, forbidden keys, bad JSON shape). */
|
|
48
|
+
readonly warnings: readonly string[];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Parse `YTB_MCP_EXTRA_HEADERS` from `env` (default `process.env`). Returns an
|
|
52
|
+
* empty map (no warnings) when the var is unset/blank.
|
|
53
|
+
*/
|
|
54
|
+
export declare function parseExtraHeaders(env?: NodeJS.ProcessEnv): ParsedExtraHeaders;
|
|
55
|
+
/**
|
|
56
|
+
* Merge extra headers UNDER an existing header set so the caller's headers
|
|
57
|
+
* (which always include `Authorization: Bearer …`) win on collision. Extra
|
|
58
|
+
* headers fill gaps; they never overwrite. Case-insensitive collision check so
|
|
59
|
+
* a `authorization` extra can never shadow a real `Authorization`.
|
|
60
|
+
*
|
|
61
|
+
* @param base Authoritative headers (Bearer, Accept, Content-Type, If-Match…).
|
|
62
|
+
* @param extra Parsed extra headers to merge underneath `base`.
|
|
63
|
+
* @returns A fresh record; inputs are not mutated.
|
|
64
|
+
*/
|
|
65
|
+
export declare function mergeExtraHeaders(base: Record<string, string>, extra: Record<string, string>): Record<string, string>;
|
|
66
|
+
//# sourceMappingURL=extra-headers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extra-headers.d.ts","sourceRoot":"","sources":["../../src/net/extra-headers.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,4DAA4D;AAC5D,eAAO,MAAM,iBAAiB,0BAA0B,CAAC;AAKzD,MAAM,WAAW,kBAAkB;IAC/B,gEAAgE;IAChE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAwED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,kBAAkB,CAiB1F;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAaxB"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// packages/mcp/src/net/extra-headers.ts
|
|
2
|
+
/**
|
|
3
|
+
* Parse `YTB_MCP_EXTRA_HEADERS` into a header map that is merged into EVERY
|
|
4
|
+
* outbound request on BOTH transports (fat-mode {@link RestClient} and
|
|
5
|
+
* proxy-mode `StreamableHTTPClientTransport`).
|
|
6
|
+
*
|
|
7
|
+
* Why this exists (Glöckle gap #14): a WordPress/Joomla site fronted by HTTP
|
|
8
|
+
* Basic-Auth, a WAF cookie, or a custom required header (e.g. a corporate
|
|
9
|
+
* gateway secret) cannot be reached — the only header we ever send is
|
|
10
|
+
* `Authorization: Bearer …`. This env var lets the operator attach the extra
|
|
11
|
+
* header(s) the gateway demands WITHOUT us inventing per-provider knobs.
|
|
12
|
+
*
|
|
13
|
+
* ── Accepted formats (auto-detected) ───────────────────────────────────────
|
|
14
|
+
* The value is read as ONE of two shapes, in this precedence:
|
|
15
|
+
*
|
|
16
|
+
* 1. **JSON object** — `{"X-WAF-Token":"abc","Cookie":"sid=42"}`.
|
|
17
|
+
* Used when the trimmed value starts with `{` AND parses as a non-array
|
|
18
|
+
* object. Non-string values are coerced via String(); null/array values
|
|
19
|
+
* are dropped (with a warning).
|
|
20
|
+
*
|
|
21
|
+
* 2. **`Name: value` lines** — one header per line, first `:` splits:
|
|
22
|
+
*
|
|
23
|
+
* X-WAF-Token: abc
|
|
24
|
+
* Cookie: sid=42
|
|
25
|
+
*
|
|
26
|
+
* Blank lines and lines starting with `#` (comments) are ignored. A line
|
|
27
|
+
* with no `:` is dropped with a warning.
|
|
28
|
+
*
|
|
29
|
+
* ── Hard invariants ─────────────────────────────────────────────────────────
|
|
30
|
+
* - **`Authorization` can NEVER be set here.** Any `Authorization` key (case-
|
|
31
|
+
* insensitive) in the parsed map is dropped with a warning — the Bearer is
|
|
32
|
+
* owned by the auth layer and an extra header must never overwrite it. This
|
|
33
|
+
* is the parse-side half of "Bearer always wins"; the merge-side half lives
|
|
34
|
+
* in {@link mergeExtraHeaders}.
|
|
35
|
+
* - Empty header NAMES are dropped. Empty VALUES are kept (a deliberately
|
|
36
|
+
* blank header is a legitimate, if rare, gateway requirement).
|
|
37
|
+
*
|
|
38
|
+
* Pure + deterministic. `warn` collects non-fatal issues; the caller decides
|
|
39
|
+
* whether to surface them (the server logs them once to stderr at startup).
|
|
40
|
+
*
|
|
41
|
+
* @license MIT
|
|
42
|
+
*/
|
|
43
|
+
/** Canonical env-var name for the extra-headers feature. */
|
|
44
|
+
export const ENV_EXTRA_HEADERS = 'YTB_MCP_EXTRA_HEADERS';
|
|
45
|
+
/** Header names that may never be supplied via the extra-headers env var. */
|
|
46
|
+
const FORBIDDEN_HEADER_NAMES = new Set(['authorization']);
|
|
47
|
+
/** Is `name` a header we refuse to let the env var set? Case-insensitive. */
|
|
48
|
+
function isForbidden(name) {
|
|
49
|
+
return FORBIDDEN_HEADER_NAMES.has(name.trim().toLowerCase());
|
|
50
|
+
}
|
|
51
|
+
/** Coerce a JSON value to a string, or null when it cannot sanely become one. */
|
|
52
|
+
function coerceValue(value) {
|
|
53
|
+
if (typeof value === 'string')
|
|
54
|
+
return value;
|
|
55
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
56
|
+
return String(value);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
/** Parse the JSON-object form. Returns null when `raw` is not a JSON object. */
|
|
60
|
+
function parseJsonObject(raw, warnings) {
|
|
61
|
+
let parsed;
|
|
62
|
+
try {
|
|
63
|
+
parsed = JSON.parse(raw);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const out = {};
|
|
72
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
73
|
+
const name = key.trim();
|
|
74
|
+
if (name === '') {
|
|
75
|
+
warnings.push(`${ENV_EXTRA_HEADERS}: dropped an entry with an empty header name.`);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (isForbidden(name)) {
|
|
79
|
+
warnings.push(`${ENV_EXTRA_HEADERS}: "${name}" is ignored — the Bearer token is set automatically and can never be overridden here.`);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const coerced = coerceValue(value);
|
|
83
|
+
if (coerced === null) {
|
|
84
|
+
warnings.push(`${ENV_EXTRA_HEADERS}: dropped header "${name}" — its value is not a string/number/boolean.`);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
out[name] = coerced;
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
/** Parse the `Name: value` line form. */
|
|
92
|
+
function parseLines(raw, warnings) {
|
|
93
|
+
const out = {};
|
|
94
|
+
for (const lineRaw of raw.split(/\r?\n/)) {
|
|
95
|
+
const line = lineRaw.trim();
|
|
96
|
+
if (line === '' || line.startsWith('#'))
|
|
97
|
+
continue;
|
|
98
|
+
const idx = line.indexOf(':');
|
|
99
|
+
if (idx < 0) {
|
|
100
|
+
warnings.push(`${ENV_EXTRA_HEADERS}: dropped line with no ":" separator — "${line}".`);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const name = line.slice(0, idx).trim();
|
|
104
|
+
const value = line.slice(idx + 1).trim();
|
|
105
|
+
if (name === '') {
|
|
106
|
+
warnings.push(`${ENV_EXTRA_HEADERS}: dropped a line with an empty header name.`);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (isForbidden(name)) {
|
|
110
|
+
warnings.push(`${ENV_EXTRA_HEADERS}: "${name}" is ignored — the Bearer token is set automatically and can never be overridden here.`);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
out[name] = value;
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Parse `YTB_MCP_EXTRA_HEADERS` from `env` (default `process.env`). Returns an
|
|
119
|
+
* empty map (no warnings) when the var is unset/blank.
|
|
120
|
+
*/
|
|
121
|
+
export function parseExtraHeaders(env = process.env) {
|
|
122
|
+
const raw = (env[ENV_EXTRA_HEADERS] ?? '').trim();
|
|
123
|
+
if (raw === '') {
|
|
124
|
+
return { headers: {}, warnings: [] };
|
|
125
|
+
}
|
|
126
|
+
const warnings = [];
|
|
127
|
+
// JSON-object form wins when the value looks like an object literal.
|
|
128
|
+
if (raw.startsWith('{')) {
|
|
129
|
+
const obj = parseJsonObject(raw, warnings);
|
|
130
|
+
if (obj !== null) {
|
|
131
|
+
return { headers: obj, warnings };
|
|
132
|
+
}
|
|
133
|
+
// Looked like JSON but didn't parse — fall back to line-parsing rather
|
|
134
|
+
// than silently dropping everything, and say so.
|
|
135
|
+
warnings.push(`${ENV_EXTRA_HEADERS}: value started with "{" but is not valid JSON — falling back to "Name: value" line parsing.`);
|
|
136
|
+
}
|
|
137
|
+
return { headers: parseLines(raw, warnings), warnings };
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Merge extra headers UNDER an existing header set so the caller's headers
|
|
141
|
+
* (which always include `Authorization: Bearer …`) win on collision. Extra
|
|
142
|
+
* headers fill gaps; they never overwrite. Case-insensitive collision check so
|
|
143
|
+
* a `authorization` extra can never shadow a real `Authorization`.
|
|
144
|
+
*
|
|
145
|
+
* @param base Authoritative headers (Bearer, Accept, Content-Type, If-Match…).
|
|
146
|
+
* @param extra Parsed extra headers to merge underneath `base`.
|
|
147
|
+
* @returns A fresh record; inputs are not mutated.
|
|
148
|
+
*/
|
|
149
|
+
export function mergeExtraHeaders(base, extra) {
|
|
150
|
+
const lowerBaseKeys = new Set(Object.keys(base).map((k) => k.toLowerCase()));
|
|
151
|
+
const merged = {};
|
|
152
|
+
// Extra first, so a same-name base key overwrites it below.
|
|
153
|
+
for (const [name, value] of Object.entries(extra)) {
|
|
154
|
+
// Never let an extra header shadow a base header (esp. Authorization).
|
|
155
|
+
if (lowerBaseKeys.has(name.toLowerCase()))
|
|
156
|
+
continue;
|
|
157
|
+
merged[name] = value;
|
|
158
|
+
}
|
|
159
|
+
for (const [name, value] of Object.entries(base)) {
|
|
160
|
+
merged[name] = value;
|
|
161
|
+
}
|
|
162
|
+
return merged;
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=extra-headers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extra-headers.js","sourceRoot":"","sources":["../../src/net/extra-headers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classify a host as internal. A host is internal when ANY of:
|
|
3
|
+
* - it is `localhost`,
|
|
4
|
+
* - it is a private/loopback/link-local IPv4 or IPv6 literal,
|
|
5
|
+
* - it ends in an internal DNS suffix (`.local`, `.internal`, …),
|
|
6
|
+
* - it is a bare single-label hostname (no dot — an intranet short name).
|
|
7
|
+
*
|
|
8
|
+
* A normal public FQDN (`example.com`, `acme.co.uk`) is NOT internal.
|
|
9
|
+
*/
|
|
10
|
+
export declare function isInternalHost(rawHost: string): boolean;
|
|
11
|
+
/** Extract a lowercased, bracket-stripped host from a URL string, or null. */
|
|
12
|
+
export declare function hostFromUrl(rawUrl: string): string | null;
|
|
13
|
+
export interface NoProxyDerivation {
|
|
14
|
+
/** True when this call would change NO_PROXY (host added). */
|
|
15
|
+
readonly changed: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* The effective NO_PROXY value to use. When `changed` is false this is the
|
|
18
|
+
* input value verbatim (or undefined when it was unset and stays unset).
|
|
19
|
+
*/
|
|
20
|
+
readonly noProxy: string | undefined;
|
|
21
|
+
/** The internal host that was added, or null when nothing was added. */
|
|
22
|
+
readonly addedHost: string | null;
|
|
23
|
+
/** Non-fatal reason when nothing was added (for an optional debug log). */
|
|
24
|
+
readonly reason?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface DeriveNoProxyInput {
|
|
27
|
+
/** Source of env vars. */
|
|
28
|
+
readonly env: NodeJS.ProcessEnv;
|
|
29
|
+
/** The configured site URL (typically `YTB_MCP_SITE_URL`). */
|
|
30
|
+
readonly siteUrl: string | undefined;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Compute the NO_PROXY value to apply so an internal site host bypasses a
|
|
34
|
+
* configured corporate proxy. Idempotent + non-clobbering:
|
|
35
|
+
*
|
|
36
|
+
* - No proxy configured → no change (the agent isn't installed anyway).
|
|
37
|
+
* - `siteUrl` missing / unparseable / clearly public → no change.
|
|
38
|
+
* - Host already present in NO_PROXY (case-insensitive) → no change.
|
|
39
|
+
* - Otherwise → append the host to the existing operator NO_PROXY (preserved
|
|
40
|
+
* verbatim, comma-joined), or seed NO_PROXY with just the host when unset.
|
|
41
|
+
*
|
|
42
|
+
* Pure: returns the value to write; the caller mutates `process.env`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function deriveNoProxyForInternalSite(input: DeriveNoProxyInput): NoProxyDerivation;
|
|
45
|
+
//# sourceMappingURL=internal-host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal-host.d.ts","sourceRoot":"","sources":["../../src/net/internal-host.ts"],"names":[],"mappings":"AAmEA;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAWvD;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASzD;AAeD,MAAM,WAAW,iBAAiB;IAC9B,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,wEAAwE;IACxE,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IAC/B,0BAA0B;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,kBAAkB,GAAG,iBAAiB,CAwBzF"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// packages/mcp/src/net/internal-host.ts
|
|
2
|
+
/**
|
|
3
|
+
* Classify a site host as internal vs public, and derive a NO_PROXY addition
|
|
4
|
+
* for an internal host when a forward proxy is configured (Glöckle gap #9).
|
|
5
|
+
*
|
|
6
|
+
* Why this exists: when a corporate `HTTP(S)_PROXY` is set, undici's
|
|
7
|
+
* `EnvHttpProxyAgent` routes EVERYTHING through it — including traffic to an
|
|
8
|
+
* intranet WordPress/Joomla that the proxy can't (or shouldn't) reach. The
|
|
9
|
+
* `NO_PROXY` exception list fixes that, but only if the operator pre-set it.
|
|
10
|
+
* A common Glöckle-class failure: the proxy reaches the public internet fine,
|
|
11
|
+
* but the internal CMS is direct-only (or vice-versa), and outbound MCP traffic
|
|
12
|
+
* silently dies.
|
|
13
|
+
*
|
|
14
|
+
* We therefore classify the configured `YTB_MCP_SITE_URL` host and, when a
|
|
15
|
+
* proxy is set AND the host is clearly internal, add it to the effective
|
|
16
|
+
* NO_PROXY the agent reads. **Conservative by construction:** only RFC1918 /
|
|
17
|
+
* loopback / link-local IPs, `.local` / `.internal` / `.intranet` suffixes, and
|
|
18
|
+
* bare single-label hostnames (no dot) are treated as internal. A normal
|
|
19
|
+
* public FQDN is NEVER routed to NO_PROXY.
|
|
20
|
+
*
|
|
21
|
+
* Pure + env-injectable so it is unit-testable offline. Never mutates input.
|
|
22
|
+
*
|
|
23
|
+
* @license MIT
|
|
24
|
+
*/
|
|
25
|
+
/** Internal-by-convention DNS suffixes (RFC 6762 + common intranet TLDs). */
|
|
26
|
+
const INTERNAL_SUFFIXES = ['.local', '.internal', '.intranet', '.lan', '.home.arpa'];
|
|
27
|
+
/** Strip an `[…]` IPv6 bracket wrapper and a trailing `.` if present. */
|
|
28
|
+
function normalizeHost(host) {
|
|
29
|
+
let h = host.trim().toLowerCase();
|
|
30
|
+
if (h.startsWith('[') && h.endsWith(']'))
|
|
31
|
+
h = h.slice(1, -1);
|
|
32
|
+
if (h.endsWith('.'))
|
|
33
|
+
h = h.slice(0, -1);
|
|
34
|
+
return h;
|
|
35
|
+
}
|
|
36
|
+
/** Is `host` an IPv4 literal in a private/loopback/link-local range? */
|
|
37
|
+
function isPrivateIpv4(host) {
|
|
38
|
+
const m = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
|
|
39
|
+
if (m === null)
|
|
40
|
+
return false;
|
|
41
|
+
const octets = [m[1], m[2], m[3], m[4]].map((o) => Number.parseInt(o, 10));
|
|
42
|
+
if (octets.some((o) => o > 255))
|
|
43
|
+
return false;
|
|
44
|
+
const [a, b] = octets;
|
|
45
|
+
if (a === 10)
|
|
46
|
+
return true; // 10.0.0.0/8
|
|
47
|
+
if (a === 127)
|
|
48
|
+
return true; // 127.0.0.0/8 loopback
|
|
49
|
+
if (a === 192 && b === 168)
|
|
50
|
+
return true; // 192.168.0.0/16
|
|
51
|
+
if (a === 172 && b >= 16 && b <= 31)
|
|
52
|
+
return true; // 172.16.0.0/12
|
|
53
|
+
if (a === 169 && b === 254)
|
|
54
|
+
return true; // 169.254.0.0/16 link-local
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
/** Is `host` an IPv6 loopback / unique-local (fc00::/7) / link-local (fe80::/10)? */
|
|
58
|
+
function isPrivateIpv6(host) {
|
|
59
|
+
if (!host.includes(':'))
|
|
60
|
+
return false;
|
|
61
|
+
if (host === '::1')
|
|
62
|
+
return true; // loopback
|
|
63
|
+
const first = host.split(':')[0] ?? '';
|
|
64
|
+
if (first === '')
|
|
65
|
+
return false; // e.g. "::" unspecified — not a real site host
|
|
66
|
+
const prefix = Number.parseInt(first, 16);
|
|
67
|
+
if (Number.isNaN(prefix))
|
|
68
|
+
return false;
|
|
69
|
+
// fc00::/7 (unique-local) → first hextet 0xfc00–0xfdff.
|
|
70
|
+
if (prefix >= 0xfc00 && prefix <= 0xfdff)
|
|
71
|
+
return true;
|
|
72
|
+
// fe80::/10 (link-local) → first hextet 0xfe80–0xfebf.
|
|
73
|
+
if (prefix >= 0xfe80 && prefix <= 0xfebf)
|
|
74
|
+
return true;
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Classify a host as internal. A host is internal when ANY of:
|
|
79
|
+
* - it is `localhost`,
|
|
80
|
+
* - it is a private/loopback/link-local IPv4 or IPv6 literal,
|
|
81
|
+
* - it ends in an internal DNS suffix (`.local`, `.internal`, …),
|
|
82
|
+
* - it is a bare single-label hostname (no dot — an intranet short name).
|
|
83
|
+
*
|
|
84
|
+
* A normal public FQDN (`example.com`, `acme.co.uk`) is NOT internal.
|
|
85
|
+
*/
|
|
86
|
+
export function isInternalHost(rawHost) {
|
|
87
|
+
const host = normalizeHost(rawHost);
|
|
88
|
+
if (host === '')
|
|
89
|
+
return false;
|
|
90
|
+
if (host === 'localhost')
|
|
91
|
+
return true;
|
|
92
|
+
if (isPrivateIpv4(host))
|
|
93
|
+
return true;
|
|
94
|
+
if (isPrivateIpv6(host))
|
|
95
|
+
return true;
|
|
96
|
+
if (INTERNAL_SUFFIXES.some((s) => host.endsWith(s)))
|
|
97
|
+
return true;
|
|
98
|
+
// A bare hostname with no dot is an intranet short name (e.g. "wpserver").
|
|
99
|
+
// IPv6 literals contain ":" and are handled above; guard against them here.
|
|
100
|
+
if (!host.includes('.') && !host.includes(':'))
|
|
101
|
+
return true;
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
/** Extract a lowercased, bracket-stripped host from a URL string, or null. */
|
|
105
|
+
export function hostFromUrl(rawUrl) {
|
|
106
|
+
const url = rawUrl.trim();
|
|
107
|
+
if (url === '')
|
|
108
|
+
return null;
|
|
109
|
+
try {
|
|
110
|
+
const parsed = new URL(url);
|
|
111
|
+
return normalizeHost(parsed.hostname);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** Is any forward-proxy configured in `env`? */
|
|
118
|
+
function hasProxyConfigured(env) {
|
|
119
|
+
return Boolean(env.HTTPS_PROXY ?? env.https_proxy ?? env.HTTP_PROXY ?? env.http_proxy);
|
|
120
|
+
}
|
|
121
|
+
/** Split a NO_PROXY string into trimmed, lowercased, non-empty entries. */
|
|
122
|
+
function splitNoProxy(value) {
|
|
123
|
+
return value
|
|
124
|
+
.split(',')
|
|
125
|
+
.map((e) => e.trim().toLowerCase())
|
|
126
|
+
.filter((e) => e !== '');
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Compute the NO_PROXY value to apply so an internal site host bypasses a
|
|
130
|
+
* configured corporate proxy. Idempotent + non-clobbering:
|
|
131
|
+
*
|
|
132
|
+
* - No proxy configured → no change (the agent isn't installed anyway).
|
|
133
|
+
* - `siteUrl` missing / unparseable / clearly public → no change.
|
|
134
|
+
* - Host already present in NO_PROXY (case-insensitive) → no change.
|
|
135
|
+
* - Otherwise → append the host to the existing operator NO_PROXY (preserved
|
|
136
|
+
* verbatim, comma-joined), or seed NO_PROXY with just the host when unset.
|
|
137
|
+
*
|
|
138
|
+
* Pure: returns the value to write; the caller mutates `process.env`.
|
|
139
|
+
*/
|
|
140
|
+
export function deriveNoProxyForInternalSite(input) {
|
|
141
|
+
const { env, siteUrl } = input;
|
|
142
|
+
const current = env.NO_PROXY ?? env.no_proxy;
|
|
143
|
+
if (!hasProxyConfigured(env)) {
|
|
144
|
+
return { changed: false, noProxy: current, addedHost: null, reason: 'no proxy configured' };
|
|
145
|
+
}
|
|
146
|
+
const host = siteUrl !== undefined ? hostFromUrl(siteUrl) : null;
|
|
147
|
+
if (host === null) {
|
|
148
|
+
return { changed: false, noProxy: current, addedHost: null, reason: 'no parseable site URL' };
|
|
149
|
+
}
|
|
150
|
+
if (!isInternalHost(host)) {
|
|
151
|
+
return { changed: false, noProxy: current, addedHost: null, reason: 'site host is public' };
|
|
152
|
+
}
|
|
153
|
+
const existing = current !== undefined ? splitNoProxy(current) : [];
|
|
154
|
+
if (existing.includes(host)) {
|
|
155
|
+
return { changed: false, noProxy: current, addedHost: null, reason: 'host already in NO_PROXY' };
|
|
156
|
+
}
|
|
157
|
+
// Append, preserving the operator's existing entries verbatim (original
|
|
158
|
+
// casing/order) — we only add the host at the end.
|
|
159
|
+
const next = current !== undefined && current.trim() !== ''
|
|
160
|
+
? `${current},${host}`
|
|
161
|
+
: host;
|
|
162
|
+
return { changed: true, noProxy: next, addedHost: host };
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=internal-host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal-host.js","sourceRoot":"","sources":["../../src/net/internal-host.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type Dispatcher } from 'undici';
|
|
2
|
+
/**
|
|
3
|
+
* Injection seam for tests — production uses the real undici functions + env.
|
|
4
|
+
*/
|
|
5
|
+
export interface ProxyDispatcherDeps {
|
|
6
|
+
/** Defaults to `process.env`. Read for HTTP(S)_PROXY presence. */
|
|
7
|
+
env?: NodeJS.ProcessEnv;
|
|
8
|
+
/** Defaults to undici's {@link undiciSetGlobalDispatcher}. */
|
|
9
|
+
setGlobalDispatcher?: (dispatcher: Dispatcher) => void;
|
|
10
|
+
/** Defaults to `() => new EnvHttpProxyAgent()`. */
|
|
11
|
+
makeEnvHttpProxyAgent?: () => Dispatcher;
|
|
12
|
+
/** Defaults to `console.error`. */
|
|
13
|
+
stderr?: (message: string) => void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Install a process-global undici dispatcher that honors the enterprise
|
|
17
|
+
* `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY` environment variables at request
|
|
18
|
+
* time (undici's {@link EnvHttpProxyAgent}).
|
|
19
|
+
*
|
|
20
|
+
* Why this exists: Node's global `fetch` only auto-honors proxy env vars from
|
|
21
|
+
* **Node 24** (`NODE_USE_ENV_PROXY`). On the Node 18–23 floor this package
|
|
22
|
+
* supports, an enterprise forward/auth proxy is otherwise ignored and outbound
|
|
23
|
+
* MCP traffic silently fails. Node's global `fetch` reads the global dispatcher,
|
|
24
|
+
* and so does the MCP SDK's `StreamableHTTPClientTransport` (proxy mode) and the
|
|
25
|
+
* fat-mode `RestClient` — so one global dispatcher covers every outbound call.
|
|
26
|
+
*
|
|
27
|
+
* Installed ONLY when a forward proxy is actually configured
|
|
28
|
+
* (`HTTP_PROXY`/`HTTPS_PROXY`). With no proxy env, the default dispatcher (a
|
|
29
|
+
* direct connection) is already correct, so we skip — this also avoids undici's
|
|
30
|
+
* `EnvHttpProxyAgent is experimental` startup warning for the common, direct
|
|
31
|
+
* case. Corporate TLS-inspection root CAs are handled separately by Node's
|
|
32
|
+
* native `NODE_EXTRA_CA_CERTS` (undici's default connector honors it).
|
|
33
|
+
*
|
|
34
|
+
* Never throws: a failure to install (exotic runtime, frozen global) degrades
|
|
35
|
+
* to a stderr warning and a direct connection, returning `false`.
|
|
36
|
+
*
|
|
37
|
+
* @returns `true` if the dispatcher was installed, `false` if skipped/degraded.
|
|
38
|
+
*/
|
|
39
|
+
export declare function installGlobalProxyDispatcher(deps?: ProxyDispatcherDeps): boolean;
|
|
40
|
+
//# sourceMappingURL=proxy-dispatcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-dispatcher.d.ts","sourceRoot":"","sources":["../../src/net/proxy-dispatcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAuE,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC;AAI9G;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,kEAAkE;IAClE,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,8DAA8D;IAC9D,mBAAmB,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IACvD,mDAAmD;IACnD,qBAAqB,CAAC,EAAE,MAAM,UAAU,CAAC;IACzC,mCAAmC;IACnC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAOD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAkCpF"}
|