@gaunt-sloth/agent 2.0.0-alpha.12 → 2.0.0-alpha.13
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/mcp/tlsTrust.d.ts +61 -0
- package/dist/mcp/tlsTrust.js +109 -0
- package/dist/mcp/tlsTrust.js.map +1 -0
- package/dist/resolvers.js +34 -2
- package/dist/resolvers.js.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* Process-global TLS trust for outbound HTTPS, driven by the `tls` config block.
|
|
4
|
+
*
|
|
5
|
+
* Motivation: an `http`-transport MCP server behind a private/corporate CA (e.g. a dev monolith
|
|
6
|
+
* with a self-signed cert) makes Node's global `fetch` reject the connection with
|
|
7
|
+
* `TypeError: fetch failed` (`SELF_SIGNED_CERT_IN_CHAIN`). The only pre-existing workaround was to
|
|
8
|
+
* prepend `NODE_EXTRA_CA_CERTS=<ca.crt>` on every invocation — that env var is read ONCE at Node
|
|
9
|
+
* startup, so it can't be set from config after launch.
|
|
10
|
+
*
|
|
11
|
+
* Node's `fetch` runs on undici and ignores `https.globalAgent`, so the CA must be installed via an
|
|
12
|
+
* undici dispatcher. `@langchain/mcp-adapters` forwards only `serverName`/`headers`/`authProvider`
|
|
13
|
+
* to the SDK transport (it drops the SDK's `requestInit`/`fetch` hooks), so a per-server CA is not
|
|
14
|
+
* reachable through the adapter. The trust is therefore installed **process-globally** via
|
|
15
|
+
* `setGlobalDispatcher`. That is acceptable for adding a CA (additive trust weakens nothing) — but
|
|
16
|
+
* note the insecure latch below is global too, and so affects LLM provider calls, not only MCP.
|
|
17
|
+
*/
|
|
18
|
+
import type { GthConfig } from '@gaunt-sloth/core/config.js';
|
|
19
|
+
type TlsConfig = GthConfig['tls'];
|
|
20
|
+
/** A cert path that could not be read, with the reason (surfaced by the installer). */
|
|
21
|
+
export interface TlsCertFailure {
|
|
22
|
+
path: string;
|
|
23
|
+
message: string;
|
|
24
|
+
}
|
|
25
|
+
/** The undici `connect` options computed from a `tls` config, plus what happened while reading. */
|
|
26
|
+
export interface BuiltDispatcher {
|
|
27
|
+
/** Node's default roots FIRST, then successfully-read user certs. `connect.ca` REPLACES the */
|
|
28
|
+
/** default store, so the roots must be included or every public-CA fetch breaks. */
|
|
29
|
+
ca: string[];
|
|
30
|
+
/** `false` disables verification entirely (process-global). Defaults to `true`. */
|
|
31
|
+
rejectUnauthorized: boolean;
|
|
32
|
+
/** How many configured certs were read successfully. */
|
|
33
|
+
loadedCount: number;
|
|
34
|
+
/** Configured certs that could not be read (fail-soft: skipped, not fatal). */
|
|
35
|
+
failures: TlsCertFailure[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Compute the undici dispatcher options from a `tls` config. Pure aside from the injected
|
|
39
|
+
* `readCert` (so unit tests stub file reads): resolves nothing about the environment itself.
|
|
40
|
+
*
|
|
41
|
+
* Returns `null` only when NOTHING is configured — no `tls` block, or an empty `tls` with no cert
|
|
42
|
+
* entries and verification left at its secure default. When certs ARE configured it always returns
|
|
43
|
+
* the object (even if every read failed), so the caller can surface those `failures` rather than
|
|
44
|
+
* silently swallowing a bad cert path. Whether to actually install is the caller's call:
|
|
45
|
+
* `loadedCount > 0 || !rejectUnauthorized`.
|
|
46
|
+
*
|
|
47
|
+
* `readCert` receives each configured path verbatim and must return the PEM contents or throw.
|
|
48
|
+
*/
|
|
49
|
+
export declare function buildDispatcherOptions(tls: TlsConfig, readCert: (path: string) => string, baseRoots?: readonly string[]): BuiltDispatcher | null;
|
|
50
|
+
/**
|
|
51
|
+
* Install the process-global undici dispatcher from `config.tls`, if any. Idempotent (installs at
|
|
52
|
+
* most once per process). Fail-soft: an unreadable cert is warned about and skipped, never fatal.
|
|
53
|
+
* Emits a loud security warning when verification is disabled.
|
|
54
|
+
*
|
|
55
|
+
* Wired into `getMcpClient` before the MCP client is created; because it is global it also covers
|
|
56
|
+
* LLM/tool fetches — which is desired for the CA case and is the danger to flag for the latch.
|
|
57
|
+
*/
|
|
58
|
+
export declare function installMcpTlsTrust(config: GthConfig): void;
|
|
59
|
+
/** Test-only: reset the install-once guard between cases. */
|
|
60
|
+
export declare function resetMcpTlsTrustForTests(): void;
|
|
61
|
+
export {};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* Process-global TLS trust for outbound HTTPS, driven by the `tls` config block.
|
|
4
|
+
*
|
|
5
|
+
* Motivation: an `http`-transport MCP server behind a private/corporate CA (e.g. a dev monolith
|
|
6
|
+
* with a self-signed cert) makes Node's global `fetch` reject the connection with
|
|
7
|
+
* `TypeError: fetch failed` (`SELF_SIGNED_CERT_IN_CHAIN`). The only pre-existing workaround was to
|
|
8
|
+
* prepend `NODE_EXTRA_CA_CERTS=<ca.crt>` on every invocation — that env var is read ONCE at Node
|
|
9
|
+
* startup, so it can't be set from config after launch.
|
|
10
|
+
*
|
|
11
|
+
* Node's `fetch` runs on undici and ignores `https.globalAgent`, so the CA must be installed via an
|
|
12
|
+
* undici dispatcher. `@langchain/mcp-adapters` forwards only `serverName`/`headers`/`authProvider`
|
|
13
|
+
* to the SDK transport (it drops the SDK's `requestInit`/`fetch` hooks), so a per-server CA is not
|
|
14
|
+
* reachable through the adapter. The trust is therefore installed **process-globally** via
|
|
15
|
+
* `setGlobalDispatcher`. That is acceptable for adding a CA (additive trust weakens nothing) — but
|
|
16
|
+
* note the insecure latch below is global too, and so affects LLM provider calls, not only MCP.
|
|
17
|
+
*/
|
|
18
|
+
import { rootCertificates } from 'node:tls';
|
|
19
|
+
import { readFileSync } from 'node:fs';
|
|
20
|
+
import { homedir } from 'node:os';
|
|
21
|
+
import { resolve } from 'node:path';
|
|
22
|
+
import { Agent, setGlobalDispatcher } from 'undici';
|
|
23
|
+
import { getProjectDir } from '@gaunt-sloth/core/utils/systemUtils.js';
|
|
24
|
+
import { displayWarning } from '@gaunt-sloth/core/utils/consoleUtils.js';
|
|
25
|
+
import { debugLog } from '@gaunt-sloth/core/utils/debugUtils.js';
|
|
26
|
+
/**
|
|
27
|
+
* Compute the undici dispatcher options from a `tls` config. Pure aside from the injected
|
|
28
|
+
* `readCert` (so unit tests stub file reads): resolves nothing about the environment itself.
|
|
29
|
+
*
|
|
30
|
+
* Returns `null` only when NOTHING is configured — no `tls` block, or an empty `tls` with no cert
|
|
31
|
+
* entries and verification left at its secure default. When certs ARE configured it always returns
|
|
32
|
+
* the object (even if every read failed), so the caller can surface those `failures` rather than
|
|
33
|
+
* silently swallowing a bad cert path. Whether to actually install is the caller's call:
|
|
34
|
+
* `loadedCount > 0 || !rejectUnauthorized`.
|
|
35
|
+
*
|
|
36
|
+
* `readCert` receives each configured path verbatim and must return the PEM contents or throw.
|
|
37
|
+
*/
|
|
38
|
+
export function buildDispatcherOptions(tls, readCert, baseRoots = rootCertificates) {
|
|
39
|
+
const rejectUnauthorized = tls?.rejectUnauthorized ?? true;
|
|
40
|
+
const certPaths = tls?.extraCaCerts ?? [];
|
|
41
|
+
// Nothing configured at all: no cert entries and verification not being disabled.
|
|
42
|
+
if (certPaths.length === 0 && rejectUnauthorized)
|
|
43
|
+
return null;
|
|
44
|
+
const userCerts = [];
|
|
45
|
+
const failures = [];
|
|
46
|
+
for (const path of certPaths) {
|
|
47
|
+
try {
|
|
48
|
+
userCerts.push(readCert(path));
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
failures.push({ path, message: error instanceof Error ? error.message : String(error) });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
// ca REPLACES the trust store — keep the default roots or public-CA fetches (incl. the LLM
|
|
56
|
+
// provider) silently break.
|
|
57
|
+
ca: [...baseRoots, ...userCerts],
|
|
58
|
+
rejectUnauthorized,
|
|
59
|
+
loadedCount: userCerts.length,
|
|
60
|
+
failures,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// Install-once guard: the trust is process-global and set-once, so re-running resolveTools (re-init)
|
|
64
|
+
// must not re-replace the dispatcher nor re-emit the security warning every time.
|
|
65
|
+
let installed = false;
|
|
66
|
+
/**
|
|
67
|
+
* Install the process-global undici dispatcher from `config.tls`, if any. Idempotent (installs at
|
|
68
|
+
* most once per process). Fail-soft: an unreadable cert is warned about and skipped, never fatal.
|
|
69
|
+
* Emits a loud security warning when verification is disabled.
|
|
70
|
+
*
|
|
71
|
+
* Wired into `getMcpClient` before the MCP client is created; because it is global it also covers
|
|
72
|
+
* LLM/tool fetches — which is desired for the CA case and is the danger to flag for the latch.
|
|
73
|
+
*/
|
|
74
|
+
export function installMcpTlsTrust(config) {
|
|
75
|
+
if (installed) {
|
|
76
|
+
debugLog('MCP TLS trust already installed this process; skipping.');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const built = buildDispatcherOptions(config.tls, resolveAndReadCert);
|
|
80
|
+
if (!built)
|
|
81
|
+
return;
|
|
82
|
+
// Surface unreadable certs even if we end up installing nothing — a bad cert path must never be
|
|
83
|
+
// swallowed silently. Warn BEFORE the "nothing actionable" bail below.
|
|
84
|
+
for (const failure of built.failures) {
|
|
85
|
+
displayWarning(`TLS: could not read CA certificate '${failure.path}' (${failure.message}); skipping it.`);
|
|
86
|
+
}
|
|
87
|
+
// Nothing left to install: every configured cert failed to read and verification is still on.
|
|
88
|
+
// (Failures were already surfaced above.) Leave Node's default dispatcher untouched.
|
|
89
|
+
if (built.loadedCount === 0 && built.rejectUnauthorized)
|
|
90
|
+
return;
|
|
91
|
+
installed = true;
|
|
92
|
+
if (!built.rejectUnauthorized) {
|
|
93
|
+
displayWarning('TLS: certificate verification is DISABLED (tls.rejectUnauthorized: false). This is INSECURE ' +
|
|
94
|
+
'and applies to ALL outbound HTTPS this process makes — MCP servers AND LLM provider calls. ' +
|
|
95
|
+
'Use only against trusted dev endpoints; prefer tls.extraCaCerts to trust a specific CA instead.');
|
|
96
|
+
}
|
|
97
|
+
setGlobalDispatcher(new Agent({ connect: { ca: built.ca, rejectUnauthorized: built.rejectUnauthorized } }));
|
|
98
|
+
debugLog(`MCP TLS trust installed: ${built.loadedCount} extra CA cert(s), rejectUnauthorized=${built.rejectUnauthorized}.`);
|
|
99
|
+
}
|
|
100
|
+
/** Resolve a configured cert path (`~` / relative-to-project / absolute) and read it as UTF-8. */
|
|
101
|
+
function resolveAndReadCert(path) {
|
|
102
|
+
const expanded = path === '~' || path.startsWith('~/') ? homedir() + path.slice(1) : path;
|
|
103
|
+
return readFileSync(resolve(getProjectDir(), expanded), 'utf8');
|
|
104
|
+
}
|
|
105
|
+
/** Test-only: reset the install-once guard between cases. */
|
|
106
|
+
export function resetMcpTlsTrustForTests() {
|
|
107
|
+
installed = false;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=tlsTrust.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tlsTrust.js","sourceRoot":"","sources":["../../src/mcp/tlsTrust.ts"],"names":[],"mappings":"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"}
|
package/dist/resolvers.js
CHANGED
|
@@ -14,6 +14,7 @@ import { createA2AAgentTool } from '#src/tools/A2AAgentTool.js';
|
|
|
14
14
|
import { prepareMcpTools } from '#src/utils/mcpUtils.js';
|
|
15
15
|
import { formatMcpConnectFailureMessage } from '#src/utils/mcpAuthError.js';
|
|
16
16
|
import { createAuthProviderAndAuthenticate } from '#src/mcp/OAuthClientProviderImpl.js';
|
|
17
|
+
import { installMcpTlsTrust } from '#src/mcp/tlsTrust.js';
|
|
17
18
|
import { MultiServerMCPClient } from '@langchain/mcp-adapters';
|
|
18
19
|
import { MCP_TOOL_NAME_PREFIX } from '@gaunt-sloth/core/constants.js';
|
|
19
20
|
/**
|
|
@@ -28,10 +29,24 @@ export function createResolvers() {
|
|
|
28
29
|
// Kept on the resolver so the composed system prompt (via getMcpServerInstructions) AND a future
|
|
29
30
|
// MCP debug tab ([[TUI-C20]]) can read the SAME captured text without re-querying the servers.
|
|
30
31
|
let mcpServerInstructions = [];
|
|
32
|
+
// Per-server MCP connection failures captured during the most recent resolveTools. Kept on the
|
|
33
|
+
// resolver (like mcpServerInstructions) so the TUI can re-surface a failure that would otherwise
|
|
34
|
+
// print once via displayWarning and scroll out of sight the moment Ink repaints — both as a
|
|
35
|
+
// persistent chrome notice and as a per-server line in the /debug MCP tab.
|
|
36
|
+
let mcpConnectionFailures = [];
|
|
37
|
+
// Record a failure by server name; dedup so a retry (or the getTools backstop firing after
|
|
38
|
+
// onConnectionError already recorded) can't list the same server twice.
|
|
39
|
+
const recordMcpFailure = (server, error) => {
|
|
40
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
41
|
+
if (!mcpConnectionFailures.some((f) => f.server === server)) {
|
|
42
|
+
mcpConnectionFailures.push({ server, reason });
|
|
43
|
+
}
|
|
44
|
+
};
|
|
31
45
|
const resolveTools = async (config, command) => {
|
|
32
46
|
const tools = [];
|
|
33
47
|
// Fresh capture per resolution; a stale value must never leak across re-inits.
|
|
34
48
|
mcpServerInstructions = [];
|
|
49
|
+
mcpConnectionFailures = [];
|
|
35
50
|
// 1. Get built-in tools (filesystem, devTools, customTools, etc.)
|
|
36
51
|
try {
|
|
37
52
|
const { getDefaultTools } = await import('#src/builtInToolsConfig.js');
|
|
@@ -44,7 +59,7 @@ export function createResolvers() {
|
|
|
44
59
|
}
|
|
45
60
|
// 2. Get MCP tools
|
|
46
61
|
try {
|
|
47
|
-
mcpClientInstance = await getMcpClient(config);
|
|
62
|
+
mcpClientInstance = await getMcpClient(config, recordMcpFailure);
|
|
48
63
|
if (mcpClientInstance) {
|
|
49
64
|
const rawMcpTools = await mcpClientInstance.getTools();
|
|
50
65
|
// Use a simple status callback for prepareMcpTools
|
|
@@ -66,6 +81,14 @@ export function createResolvers() {
|
|
|
66
81
|
// silently, while the session degrades gracefully (built-in and A2A tools below still load).
|
|
67
82
|
displayWarning(`MCP integration tools could not be fully loaded; continuing without them. ` +
|
|
68
83
|
`Underlying error: ${error}`);
|
|
84
|
+
// A wholesale throw here isn't per-server, so onConnectionError may not have recorded anything.
|
|
85
|
+
// If nothing was captured, attribute the failure to every configured server so the TUI notice
|
|
86
|
+
// and MCP tab still name what's unavailable rather than silently showing empty tool lists.
|
|
87
|
+
if (mcpConnectionFailures.length === 0) {
|
|
88
|
+
for (const serverName of Object.keys(config.mcpServers || {})) {
|
|
89
|
+
recordMcpFailure(serverName, error);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
69
92
|
}
|
|
70
93
|
// 2b. EXT-32: capture each connected MCP server's discovery `instructions` string (from its MCP
|
|
71
94
|
// `initialize` handshake, exposed by the SDK Client via getInstructions()). This is ISOLATED
|
|
@@ -128,17 +151,24 @@ export function createResolvers() {
|
|
|
128
151
|
// later, the [[TUI-C20]] MCP debug tab). Returns a defensive copy so callers can't mutate the
|
|
129
152
|
// captured state. Empty until resolveTools has run (or when no server supplied instructions).
|
|
130
153
|
const getMcpServerInstructions = () => [...mcpServerInstructions];
|
|
154
|
+
// Defensive copy so callers can't mutate the captured state. Empty until resolveTools has run
|
|
155
|
+
// (or when every configured server connected). Read by the TUI notice bar and the /debug MCP tab.
|
|
156
|
+
const getMcpConnectionFailures = () => [...mcpConnectionFailures];
|
|
131
157
|
return {
|
|
132
158
|
resolveTools,
|
|
133
159
|
cleanupTools,
|
|
134
160
|
resolveMiddleware,
|
|
135
161
|
cleanupMiddleware,
|
|
136
162
|
getMcpServerInstructions,
|
|
163
|
+
getMcpConnectionFailures,
|
|
137
164
|
};
|
|
138
165
|
}
|
|
139
166
|
// --- Private helpers ---
|
|
140
|
-
async function getMcpClient(config) {
|
|
167
|
+
async function getMcpClient(config, recordFailure) {
|
|
141
168
|
debugLog('Setting up MCP client...');
|
|
169
|
+
// Install any configured TLS trust (custom CA / insecure latch) BEFORE the client connects.
|
|
170
|
+
// Process-global (see tlsTrust.ts) and idempotent, so it also covers LLM/tool fetches — desired.
|
|
171
|
+
installMcpTlsTrust(config);
|
|
142
172
|
const rawMcpServers = { ...(config.mcpServers || {}) };
|
|
143
173
|
debugLog(`MCP servers count: ${Object.keys(rawMcpServers).length}`);
|
|
144
174
|
const mcpServers = {};
|
|
@@ -158,6 +188,7 @@ async function getMcpClient(config) {
|
|
|
158
188
|
// EXT-31: an OAuth handshake/refresh failure happens BEFORE the MCP client exists, so it
|
|
159
189
|
// can't reach the connect-time onConnectionError callback below. Surface it here (named +
|
|
160
190
|
// actionable) and skip only THIS server so the others still load — never a silent drop.
|
|
191
|
+
recordFailure(serverName, error);
|
|
161
192
|
displayWarning(formatMcpConnectFailureMessage(serverName, error, { oauth: true }));
|
|
162
193
|
}
|
|
163
194
|
}
|
|
@@ -181,6 +212,7 @@ async function getMcpClient(config) {
|
|
|
181
212
|
onConnectionError: ({ serverName, error }) => {
|
|
182
213
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
183
214
|
const oauth = rawMcpServers[serverName]?.authProvider === 'OAuth';
|
|
215
|
+
recordFailure(serverName, error);
|
|
184
216
|
displayWarning(formatMcpConnectFailureMessage(serverName, error, { oauth }));
|
|
185
217
|
},
|
|
186
218
|
});
|
package/dist/resolvers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolvers.js","sourceRoot":"","sources":["../src/resolvers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"resolvers.js","sourceRoot":"","sources":["../src/resolvers.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gaunt-sloth/agent",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.13",
|
|
4
4
|
"description": "Deep agent runtime for Gaunt Sloth: tools, middleware, MCP/A2A clients and the AG-UI server",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Andrew Kondratev",
|
|
@@ -50,9 +50,10 @@
|
|
|
50
50
|
"diff": "^9.0.0",
|
|
51
51
|
"express": "^5.2.1",
|
|
52
52
|
"langchain": "^1.5.2",
|
|
53
|
+
"undici": "^7.16.0",
|
|
53
54
|
"uuid": ">=11.1.1",
|
|
54
55
|
"zod": "^4.0.0",
|
|
55
|
-
"@gaunt-sloth/core": "2.0.0-alpha.
|
|
56
|
+
"@gaunt-sloth/core": "2.0.0-alpha.13"
|
|
56
57
|
},
|
|
57
58
|
"files": [
|
|
58
59
|
"./dist/*",
|