@mcp-z/client 1.0.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/AGENTS.md +159 -0
- package/LICENSE +21 -0
- package/README.md +90 -0
- package/dist/cjs/auth/capability-discovery.d.cts +25 -0
- package/dist/cjs/auth/capability-discovery.d.ts +25 -0
- package/dist/cjs/auth/capability-discovery.js +280 -0
- package/dist/cjs/auth/capability-discovery.js.map +1 -0
- package/dist/cjs/auth/index.d.cts +9 -0
- package/dist/cjs/auth/index.d.ts +9 -0
- package/dist/cjs/auth/index.js +28 -0
- package/dist/cjs/auth/index.js.map +1 -0
- package/dist/cjs/auth/interactive-oauth-flow.d.cts +58 -0
- package/dist/cjs/auth/interactive-oauth-flow.d.ts +58 -0
- package/dist/cjs/auth/interactive-oauth-flow.js +537 -0
- package/dist/cjs/auth/interactive-oauth-flow.js.map +1 -0
- package/dist/cjs/auth/oauth-callback-listener.d.cts +56 -0
- package/dist/cjs/auth/oauth-callback-listener.d.ts +56 -0
- package/dist/cjs/auth/oauth-callback-listener.js +333 -0
- package/dist/cjs/auth/oauth-callback-listener.js.map +1 -0
- package/dist/cjs/auth/pkce.d.cts +17 -0
- package/dist/cjs/auth/pkce.d.ts +17 -0
- package/dist/cjs/auth/pkce.js +192 -0
- package/dist/cjs/auth/pkce.js.map +1 -0
- package/dist/cjs/auth/rfc9728-discovery.d.cts +34 -0
- package/dist/cjs/auth/rfc9728-discovery.d.ts +34 -0
- package/dist/cjs/auth/rfc9728-discovery.js +436 -0
- package/dist/cjs/auth/rfc9728-discovery.js.map +1 -0
- package/dist/cjs/auth/types.d.cts +137 -0
- package/dist/cjs/auth/types.d.ts +137 -0
- package/dist/cjs/auth/types.js +9 -0
- package/dist/cjs/auth/types.js.map +1 -0
- package/dist/cjs/client-helpers.d.cts +55 -0
- package/dist/cjs/client-helpers.d.ts +55 -0
- package/dist/cjs/client-helpers.js +128 -0
- package/dist/cjs/client-helpers.js.map +1 -0
- package/dist/cjs/config/server-loader.d.cts +27 -0
- package/dist/cjs/config/server-loader.d.ts +27 -0
- package/dist/cjs/config/server-loader.js +111 -0
- package/dist/cjs/config/server-loader.js.map +1 -0
- package/dist/cjs/config/validate-config.d.cts +15 -0
- package/dist/cjs/config/validate-config.d.ts +15 -0
- package/dist/cjs/config/validate-config.js +128 -0
- package/dist/cjs/config/validate-config.js.map +1 -0
- package/dist/cjs/connection/connect-client.d.cts +59 -0
- package/dist/cjs/connection/connect-client.d.ts +59 -0
- package/dist/cjs/connection/connect-client.js +536 -0
- package/dist/cjs/connection/connect-client.js.map +1 -0
- package/dist/cjs/connection/existing-process-transport.d.cts +40 -0
- package/dist/cjs/connection/existing-process-transport.d.ts +40 -0
- package/dist/cjs/connection/existing-process-transport.js +274 -0
- package/dist/cjs/connection/existing-process-transport.js.map +1 -0
- package/dist/cjs/connection/types.d.cts +61 -0
- package/dist/cjs/connection/types.d.ts +61 -0
- package/dist/cjs/connection/types.js +53 -0
- package/dist/cjs/connection/types.js.map +1 -0
- package/dist/cjs/connection/wait-for-http-ready.d.cts +15 -0
- package/dist/cjs/connection/wait-for-http-ready.d.ts +15 -0
- package/dist/cjs/connection/wait-for-http-ready.js +232 -0
- package/dist/cjs/connection/wait-for-http-ready.js.map +1 -0
- package/dist/cjs/dcr/dcr-authenticator.d.cts +73 -0
- package/dist/cjs/dcr/dcr-authenticator.d.ts +73 -0
- package/dist/cjs/dcr/dcr-authenticator.js +655 -0
- package/dist/cjs/dcr/dcr-authenticator.js.map +1 -0
- package/dist/cjs/dcr/dynamic-client-registrar.d.cts +28 -0
- package/dist/cjs/dcr/dynamic-client-registrar.d.ts +28 -0
- package/dist/cjs/dcr/dynamic-client-registrar.js +245 -0
- package/dist/cjs/dcr/dynamic-client-registrar.js.map +1 -0
- package/dist/cjs/dcr/index.d.cts +8 -0
- package/dist/cjs/dcr/index.d.ts +8 -0
- package/dist/cjs/dcr/index.js +24 -0
- package/dist/cjs/dcr/index.js.map +1 -0
- package/dist/cjs/index.d.cts +21 -0
- package/dist/cjs/index.d.ts +21 -0
- package/dist/cjs/index.js +94 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/monkey-patches.d.cts +6 -0
- package/dist/cjs/monkey-patches.d.ts +6 -0
- package/dist/cjs/monkey-patches.js +236 -0
- package/dist/cjs/monkey-patches.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/response-wrappers.d.cts +41 -0
- package/dist/cjs/response-wrappers.d.ts +41 -0
- package/dist/cjs/response-wrappers.js +443 -0
- package/dist/cjs/response-wrappers.js.map +1 -0
- package/dist/cjs/search/index.d.cts +6 -0
- package/dist/cjs/search/index.d.ts +6 -0
- package/dist/cjs/search/index.js +25 -0
- package/dist/cjs/search/index.js.map +1 -0
- package/dist/cjs/search/search.d.cts +22 -0
- package/dist/cjs/search/search.d.ts +22 -0
- package/dist/cjs/search/search.js +630 -0
- package/dist/cjs/search/search.js.map +1 -0
- package/dist/cjs/search/types.d.cts +122 -0
- package/dist/cjs/search/types.d.ts +122 -0
- package/dist/cjs/search/types.js +10 -0
- package/dist/cjs/search/types.js.map +1 -0
- package/dist/cjs/spawn/spawn-server.d.cts +83 -0
- package/dist/cjs/spawn/spawn-server.d.ts +83 -0
- package/dist/cjs/spawn/spawn-server.js +410 -0
- package/dist/cjs/spawn/spawn-server.js.map +1 -0
- package/dist/cjs/spawn/spawn-servers.d.cts +151 -0
- package/dist/cjs/spawn/spawn-servers.d.ts +151 -0
- package/dist/cjs/spawn/spawn-servers.js +911 -0
- package/dist/cjs/spawn/spawn-servers.js.map +1 -0
- package/dist/cjs/types.d.cts +11 -0
- package/dist/cjs/types.d.ts +11 -0
- package/dist/cjs/types.js +10 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cjs/utils/logger.d.cts +24 -0
- package/dist/cjs/utils/logger.d.ts +24 -0
- package/dist/cjs/utils/logger.js +80 -0
- package/dist/cjs/utils/logger.js.map +1 -0
- package/dist/cjs/utils/path-utils.d.cts +45 -0
- package/dist/cjs/utils/path-utils.d.ts +45 -0
- package/dist/cjs/utils/path-utils.js +158 -0
- package/dist/cjs/utils/path-utils.js.map +1 -0
- package/dist/cjs/utils/sanitizer.d.cts +30 -0
- package/dist/cjs/utils/sanitizer.d.ts +30 -0
- package/dist/cjs/utils/sanitizer.js +124 -0
- package/dist/cjs/utils/sanitizer.js.map +1 -0
- package/dist/esm/auth/capability-discovery.d.ts +25 -0
- package/dist/esm/auth/capability-discovery.js +110 -0
- package/dist/esm/auth/capability-discovery.js.map +1 -0
- package/dist/esm/auth/index.d.ts +9 -0
- package/dist/esm/auth/index.js +6 -0
- package/dist/esm/auth/index.js.map +1 -0
- package/dist/esm/auth/interactive-oauth-flow.d.ts +58 -0
- package/dist/esm/auth/interactive-oauth-flow.js +217 -0
- package/dist/esm/auth/interactive-oauth-flow.js.map +1 -0
- package/dist/esm/auth/oauth-callback-listener.d.ts +56 -0
- package/dist/esm/auth/oauth-callback-listener.js +166 -0
- package/dist/esm/auth/oauth-callback-listener.js.map +1 -0
- package/dist/esm/auth/pkce.d.ts +17 -0
- package/dist/esm/auth/pkce.js +41 -0
- package/dist/esm/auth/pkce.js.map +1 -0
- package/dist/esm/auth/rfc9728-discovery.d.ts +34 -0
- package/dist/esm/auth/rfc9728-discovery.js +157 -0
- package/dist/esm/auth/rfc9728-discovery.js.map +1 -0
- package/dist/esm/auth/types.d.ts +137 -0
- package/dist/esm/auth/types.js +7 -0
- package/dist/esm/auth/types.js.map +1 -0
- package/dist/esm/client-helpers.d.ts +55 -0
- package/dist/esm/client-helpers.js +81 -0
- package/dist/esm/client-helpers.js.map +1 -0
- package/dist/esm/config/server-loader.d.ts +27 -0
- package/dist/esm/config/server-loader.js +49 -0
- package/dist/esm/config/server-loader.js.map +1 -0
- package/dist/esm/config/validate-config.d.ts +15 -0
- package/dist/esm/config/validate-config.js +76 -0
- package/dist/esm/config/validate-config.js.map +1 -0
- package/dist/esm/connection/connect-client.d.ts +59 -0
- package/dist/esm/connection/connect-client.js +272 -0
- package/dist/esm/connection/connect-client.js.map +1 -0
- package/dist/esm/connection/existing-process-transport.d.ts +40 -0
- package/dist/esm/connection/existing-process-transport.js +103 -0
- package/dist/esm/connection/existing-process-transport.js.map +1 -0
- package/dist/esm/connection/types.d.ts +61 -0
- package/dist/esm/connection/types.js +34 -0
- package/dist/esm/connection/types.js.map +1 -0
- package/dist/esm/connection/wait-for-http-ready.d.ts +15 -0
- package/dist/esm/connection/wait-for-http-ready.js +43 -0
- package/dist/esm/connection/wait-for-http-ready.js.map +1 -0
- package/dist/esm/dcr/dcr-authenticator.d.ts +73 -0
- package/dist/esm/dcr/dcr-authenticator.js +235 -0
- package/dist/esm/dcr/dcr-authenticator.js.map +1 -0
- package/dist/esm/dcr/dynamic-client-registrar.d.ts +28 -0
- package/dist/esm/dcr/dynamic-client-registrar.js +66 -0
- package/dist/esm/dcr/dynamic-client-registrar.js.map +1 -0
- package/dist/esm/dcr/index.d.ts +8 -0
- package/dist/esm/dcr/index.js +5 -0
- package/dist/esm/dcr/index.js.map +1 -0
- package/dist/esm/index.d.ts +21 -0
- package/dist/esm/index.js +22 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/monkey-patches.d.ts +6 -0
- package/dist/esm/monkey-patches.js +32 -0
- package/dist/esm/monkey-patches.js.map +1 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/response-wrappers.d.ts +41 -0
- package/dist/esm/response-wrappers.js +201 -0
- package/dist/esm/response-wrappers.js.map +1 -0
- package/dist/esm/search/index.d.ts +6 -0
- package/dist/esm/search/index.js +3 -0
- package/dist/esm/search/index.js.map +1 -0
- package/dist/esm/search/search.d.ts +22 -0
- package/dist/esm/search/search.js +236 -0
- package/dist/esm/search/search.js.map +1 -0
- package/dist/esm/search/types.d.ts +122 -0
- package/dist/esm/search/types.js +8 -0
- package/dist/esm/search/types.js.map +1 -0
- package/dist/esm/spawn/spawn-server.d.ts +83 -0
- package/dist/esm/spawn/spawn-server.js +145 -0
- package/dist/esm/spawn/spawn-server.js.map +1 -0
- package/dist/esm/spawn/spawn-servers.d.ts +151 -0
- package/dist/esm/spawn/spawn-servers.js +406 -0
- package/dist/esm/spawn/spawn-servers.js.map +1 -0
- package/dist/esm/types.d.ts +11 -0
- package/dist/esm/types.js +9 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/utils/logger.d.ts +24 -0
- package/dist/esm/utils/logger.js +59 -0
- package/dist/esm/utils/logger.js.map +1 -0
- package/dist/esm/utils/path-utils.d.ts +45 -0
- package/dist/esm/utils/path-utils.js +89 -0
- package/dist/esm/utils/path-utils.js.map +1 -0
- package/dist/esm/utils/sanitizer.d.ts +30 -0
- package/dist/esm/utils/sanitizer.js +43 -0
- package/dist/esm/utils/sanitizer.js.map +1 -0
- package/package.json +92 -0
- package/schemas/servers.d.ts +90 -0
- package/schemas/servers.schema.json +104 -0
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* connect-mcp-client.ts
|
|
3
|
+
*
|
|
4
|
+
* Helper to connect MCP SDK clients to servers with intelligent transport inference.
|
|
5
|
+
* Automatically detects transport type from URL protocol or type field.
|
|
6
|
+
*/ "use strict";
|
|
7
|
+
Object.defineProperty(exports, "__esModule", {
|
|
8
|
+
value: true
|
|
9
|
+
});
|
|
10
|
+
Object.defineProperty(exports, "connectMcpClient", {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: function() {
|
|
13
|
+
return connectMcpClient;
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
require("../monkey-patches.js");
|
|
17
|
+
var _index = require("@modelcontextprotocol/sdk/client/index.js");
|
|
18
|
+
var _sse = require("@modelcontextprotocol/sdk/client/sse.js");
|
|
19
|
+
var _stdio = require("@modelcontextprotocol/sdk/client/stdio.js");
|
|
20
|
+
var _streamableHttp = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
21
|
+
var _getport = /*#__PURE__*/ _interop_require_default(require("get-port"));
|
|
22
|
+
var _indexts = require("../auth/index.js");
|
|
23
|
+
var _indexts1 = require("../dcr/index.js");
|
|
24
|
+
var _loggerts = require("../utils/logger.js");
|
|
25
|
+
var _existingprocesstransportts = require("./existing-process-transport.js");
|
|
26
|
+
var _waitforhttpreadyts = require("./wait-for-http-ready.js");
|
|
27
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
28
|
+
try {
|
|
29
|
+
var info = gen[key](arg);
|
|
30
|
+
var value = info.value;
|
|
31
|
+
} catch (error) {
|
|
32
|
+
reject(error);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (info.done) {
|
|
36
|
+
resolve(value);
|
|
37
|
+
} else {
|
|
38
|
+
Promise.resolve(value).then(_next, _throw);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function _async_to_generator(fn) {
|
|
42
|
+
return function() {
|
|
43
|
+
var self = this, args = arguments;
|
|
44
|
+
return new Promise(function(resolve, reject) {
|
|
45
|
+
var gen = fn.apply(self, args);
|
|
46
|
+
function _next(value) {
|
|
47
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
|
|
48
|
+
}
|
|
49
|
+
function _throw(err) {
|
|
50
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
|
|
51
|
+
}
|
|
52
|
+
_next(undefined);
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function _define_property(obj, key, value) {
|
|
57
|
+
if (key in obj) {
|
|
58
|
+
Object.defineProperty(obj, key, {
|
|
59
|
+
value: value,
|
|
60
|
+
enumerable: true,
|
|
61
|
+
configurable: true,
|
|
62
|
+
writable: true
|
|
63
|
+
});
|
|
64
|
+
} else {
|
|
65
|
+
obj[key] = value;
|
|
66
|
+
}
|
|
67
|
+
return obj;
|
|
68
|
+
}
|
|
69
|
+
function _instanceof(left, right) {
|
|
70
|
+
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
|
|
71
|
+
return !!right[Symbol.hasInstance](left);
|
|
72
|
+
} else {
|
|
73
|
+
return left instanceof right;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function _interop_require_default(obj) {
|
|
77
|
+
return obj && obj.__esModule ? obj : {
|
|
78
|
+
default: obj
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function _object_spread(target) {
|
|
82
|
+
for(var i = 1; i < arguments.length; i++){
|
|
83
|
+
var source = arguments[i] != null ? arguments[i] : {};
|
|
84
|
+
var ownKeys = Object.keys(source);
|
|
85
|
+
if (typeof Object.getOwnPropertySymbols === "function") {
|
|
86
|
+
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
|
|
87
|
+
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
ownKeys.forEach(function(key) {
|
|
91
|
+
_define_property(target, key, source[key]);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return target;
|
|
95
|
+
}
|
|
96
|
+
function _ts_generator(thisArg, body) {
|
|
97
|
+
var f, y, t, _ = {
|
|
98
|
+
label: 0,
|
|
99
|
+
sent: function() {
|
|
100
|
+
if (t[0] & 1) throw t[1];
|
|
101
|
+
return t[1];
|
|
102
|
+
},
|
|
103
|
+
trys: [],
|
|
104
|
+
ops: []
|
|
105
|
+
}, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
|
|
106
|
+
return d(g, "next", {
|
|
107
|
+
value: verb(0)
|
|
108
|
+
}), d(g, "throw", {
|
|
109
|
+
value: verb(1)
|
|
110
|
+
}), d(g, "return", {
|
|
111
|
+
value: verb(2)
|
|
112
|
+
}), typeof Symbol === "function" && d(g, Symbol.iterator, {
|
|
113
|
+
value: function() {
|
|
114
|
+
return this;
|
|
115
|
+
}
|
|
116
|
+
}), g;
|
|
117
|
+
function verb(n) {
|
|
118
|
+
return function(v) {
|
|
119
|
+
return step([
|
|
120
|
+
n,
|
|
121
|
+
v
|
|
122
|
+
]);
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function step(op) {
|
|
126
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
127
|
+
while(g && (g = 0, op[0] && (_ = 0)), _)try {
|
|
128
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
129
|
+
if (y = 0, t) op = [
|
|
130
|
+
op[0] & 2,
|
|
131
|
+
t.value
|
|
132
|
+
];
|
|
133
|
+
switch(op[0]){
|
|
134
|
+
case 0:
|
|
135
|
+
case 1:
|
|
136
|
+
t = op;
|
|
137
|
+
break;
|
|
138
|
+
case 4:
|
|
139
|
+
_.label++;
|
|
140
|
+
return {
|
|
141
|
+
value: op[1],
|
|
142
|
+
done: false
|
|
143
|
+
};
|
|
144
|
+
case 5:
|
|
145
|
+
_.label++;
|
|
146
|
+
y = op[1];
|
|
147
|
+
op = [
|
|
148
|
+
0
|
|
149
|
+
];
|
|
150
|
+
continue;
|
|
151
|
+
case 7:
|
|
152
|
+
op = _.ops.pop();
|
|
153
|
+
_.trys.pop();
|
|
154
|
+
continue;
|
|
155
|
+
default:
|
|
156
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
|
|
157
|
+
_ = 0;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
|
|
161
|
+
_.label = op[1];
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
if (op[0] === 6 && _.label < t[1]) {
|
|
165
|
+
_.label = t[1];
|
|
166
|
+
t = op;
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
if (t && _.label < t[2]) {
|
|
170
|
+
_.label = t[2];
|
|
171
|
+
_.ops.push(op);
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
if (t[2]) _.ops.pop();
|
|
175
|
+
_.trys.pop();
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
op = body.call(thisArg, _);
|
|
179
|
+
} catch (e) {
|
|
180
|
+
op = [
|
|
181
|
+
6,
|
|
182
|
+
e
|
|
183
|
+
];
|
|
184
|
+
y = 0;
|
|
185
|
+
} finally{
|
|
186
|
+
f = t = 0;
|
|
187
|
+
}
|
|
188
|
+
if (op[0] & 5) throw op[1];
|
|
189
|
+
return {
|
|
190
|
+
value: op[0] ? op[1] : void 0,
|
|
191
|
+
done: true
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Wrap promise with timeout - throws if promise takes too long
|
|
197
|
+
* Clears timeout when promise completes to prevent hanging event loop
|
|
198
|
+
* @param promise - Promise to wrap
|
|
199
|
+
* @param ms - Timeout in milliseconds
|
|
200
|
+
* @param operation - Description of operation for error message
|
|
201
|
+
* @returns Promise result or timeout error
|
|
202
|
+
*/ function withTimeout(promise, ms, operation) {
|
|
203
|
+
return _async_to_generator(function() {
|
|
204
|
+
var timeoutId;
|
|
205
|
+
return _ts_generator(this, function(_state) {
|
|
206
|
+
return [
|
|
207
|
+
2,
|
|
208
|
+
Promise.race([
|
|
209
|
+
promise.finally(function() {
|
|
210
|
+
return clearTimeout(timeoutId);
|
|
211
|
+
}),
|
|
212
|
+
new Promise(function(_, reject) {
|
|
213
|
+
timeoutId = setTimeout(function() {
|
|
214
|
+
return reject(new Error("Timeout after ".concat(ms, "ms: ").concat(operation)));
|
|
215
|
+
}, ms);
|
|
216
|
+
})
|
|
217
|
+
])
|
|
218
|
+
];
|
|
219
|
+
});
|
|
220
|
+
})();
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Extract base URL from MCP server URL
|
|
224
|
+
* @param mcpUrl - Full MCP endpoint URL (e.g., https://example.com/mcp)
|
|
225
|
+
* @returns Base URL (e.g., https://example.com)
|
|
226
|
+
*/ function extractBaseUrl(mcpUrl) {
|
|
227
|
+
var url = new URL(mcpUrl);
|
|
228
|
+
return "".concat(url.protocol, "//").concat(url.host);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Infer transport type from server configuration with validation.
|
|
232
|
+
*
|
|
233
|
+
* Priority:
|
|
234
|
+
* 1. Explicit type field (if present)
|
|
235
|
+
* 2. URL protocol (if URL present): http://, https://
|
|
236
|
+
* 3. Default to 'stdio' (if neither present)
|
|
237
|
+
*
|
|
238
|
+
* @param config - Server configuration
|
|
239
|
+
* @returns Transport type
|
|
240
|
+
* @throws Error if configuration is invalid or has conflicts
|
|
241
|
+
*/ function inferTransportType(config) {
|
|
242
|
+
// Priority 1: Explicit type field
|
|
243
|
+
if (config.type) {
|
|
244
|
+
// Validate consistency with URL if both present
|
|
245
|
+
if (config.url) {
|
|
246
|
+
var url = new URL(config.url);
|
|
247
|
+
var protocol = url.protocol;
|
|
248
|
+
if ((protocol === 'http:' || protocol === 'https:') && config.type !== 'http' && config.type !== 'sse-ide') {
|
|
249
|
+
throw new Error("Conflicting transport: URL protocol '".concat(protocol, "' requires type 'http', but got '").concat(config.type, "'"));
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// Return normalized type
|
|
253
|
+
if (config.type === 'http' || config.type === 'sse-ide') return 'http';
|
|
254
|
+
if (config.type === 'stdio') return 'stdio';
|
|
255
|
+
throw new Error("Unsupported transport type: ".concat(config.type));
|
|
256
|
+
}
|
|
257
|
+
// Priority 2: Infer from URL protocol
|
|
258
|
+
if (config.url) {
|
|
259
|
+
var url1 = new URL(config.url);
|
|
260
|
+
var protocol1 = url1.protocol;
|
|
261
|
+
if (protocol1 === 'http:' || protocol1 === 'https:') {
|
|
262
|
+
return 'http';
|
|
263
|
+
}
|
|
264
|
+
throw new Error("Unsupported URL protocol: ".concat(protocol1));
|
|
265
|
+
}
|
|
266
|
+
// Priority 3: Default to stdio
|
|
267
|
+
return 'stdio';
|
|
268
|
+
}
|
|
269
|
+
function connectMcpClient(registryOrConfig, serverName, options) {
|
|
270
|
+
return _async_to_generator(function() {
|
|
271
|
+
var _ref, isRegistry, serversConfig, registry, logger, serverConfig, available, transportType, client, serverHandle, transport, transport1, isSpawnedHttp, url, baseUrl, capabilities, authToken, port, redirectUri, authenticator, tokens, staticHeaders, dcrHeaders, mergedHeaders, transportOptions, transport2, error, errorMessage, cause, isConnectionRefused, shouldFallback, sseClient, staticHeaders1, dcrHeaders1, mergedHeaders1, sseTransportOptions, sseTransport, sseError;
|
|
272
|
+
return _ts_generator(this, function(_state) {
|
|
273
|
+
switch(_state.label){
|
|
274
|
+
case 0:
|
|
275
|
+
// Detect whether we have a RegistryLike instance or just config
|
|
276
|
+
isRegistry = 'servers' in registryOrConfig && _instanceof(registryOrConfig.servers, Map);
|
|
277
|
+
serversConfig = isRegistry ? registryOrConfig.config : registryOrConfig;
|
|
278
|
+
registry = isRegistry ? registryOrConfig : undefined;
|
|
279
|
+
logger = (_ref = options === null || options === void 0 ? void 0 : options.logger) !== null && _ref !== void 0 ? _ref : _loggerts.logger;
|
|
280
|
+
serverConfig = serversConfig[serverName];
|
|
281
|
+
if (!serverConfig) {
|
|
282
|
+
available = Object.keys(serversConfig).join(', ');
|
|
283
|
+
throw new Error("Server '".concat(serverName, "' not found in config. Available servers: ").concat(available || 'none'));
|
|
284
|
+
}
|
|
285
|
+
// Infer transport type with validation
|
|
286
|
+
transportType = inferTransportType(serverConfig);
|
|
287
|
+
// Create MCP client
|
|
288
|
+
client = new _index.Client({
|
|
289
|
+
name: 'mcp-cli-client',
|
|
290
|
+
version: '1.0.0'
|
|
291
|
+
}, {
|
|
292
|
+
capabilities: {}
|
|
293
|
+
});
|
|
294
|
+
if (!(transportType === 'stdio')) return [
|
|
295
|
+
3,
|
|
296
|
+
5
|
|
297
|
+
];
|
|
298
|
+
// Check if we have a spawned process in the registry
|
|
299
|
+
serverHandle = registry === null || registry === void 0 ? void 0 : registry.servers.get(serverName);
|
|
300
|
+
if (!serverHandle) return [
|
|
301
|
+
3,
|
|
302
|
+
2
|
|
303
|
+
];
|
|
304
|
+
// Reuse the already-spawned process
|
|
305
|
+
transport = new _existingprocesstransportts.ExistingProcessTransport(serverHandle.process);
|
|
306
|
+
return [
|
|
307
|
+
4,
|
|
308
|
+
client.connect(transport)
|
|
309
|
+
];
|
|
310
|
+
case 1:
|
|
311
|
+
_state.sent();
|
|
312
|
+
return [
|
|
313
|
+
3,
|
|
314
|
+
4
|
|
315
|
+
];
|
|
316
|
+
case 2:
|
|
317
|
+
// No registry or server not in registry - spawn new process directly
|
|
318
|
+
// This is the standard fallback when process management is not used
|
|
319
|
+
if (!serverConfig.command) {
|
|
320
|
+
throw new Error("Server '".concat(serverName, "' has stdio transport but missing 'command' field"));
|
|
321
|
+
}
|
|
322
|
+
transport1 = new _stdio.StdioClientTransport({
|
|
323
|
+
command: serverConfig.command,
|
|
324
|
+
args: serverConfig.args || [],
|
|
325
|
+
env: serverConfig.env || {}
|
|
326
|
+
});
|
|
327
|
+
// client.connect() performs initialize handshake - when it resolves, server is ready
|
|
328
|
+
return [
|
|
329
|
+
4,
|
|
330
|
+
client.connect(transport1)
|
|
331
|
+
];
|
|
332
|
+
case 3:
|
|
333
|
+
_state.sent();
|
|
334
|
+
_state.label = 4;
|
|
335
|
+
case 4:
|
|
336
|
+
return [
|
|
337
|
+
3,
|
|
338
|
+
22
|
|
339
|
+
];
|
|
340
|
+
case 5:
|
|
341
|
+
if (!(transportType === 'http')) return [
|
|
342
|
+
3,
|
|
343
|
+
22
|
|
344
|
+
];
|
|
345
|
+
if (!('url' in serverConfig) || !serverConfig.url) {
|
|
346
|
+
throw new Error("Server '".concat(serverName, "' has http transport but missing 'url' field"));
|
|
347
|
+
}
|
|
348
|
+
// Check if this is a freshly spawned HTTP server (from registry)
|
|
349
|
+
// that might not be ready yet - transport readiness check needed
|
|
350
|
+
isSpawnedHttp = registry === null || registry === void 0 ? void 0 : registry.servers.has(serverName);
|
|
351
|
+
if (!isSpawnedHttp) return [
|
|
352
|
+
3,
|
|
353
|
+
7
|
|
354
|
+
];
|
|
355
|
+
logger.debug("[connectMcpClient] waiting for HTTP server '".concat(serverName, "' at ").concat(serverConfig.url));
|
|
356
|
+
return [
|
|
357
|
+
4,
|
|
358
|
+
(0, _waitforhttpreadyts.waitForHttpReady)(serverConfig.url)
|
|
359
|
+
];
|
|
360
|
+
case 6:
|
|
361
|
+
_state.sent();
|
|
362
|
+
logger.debug("[connectMcpClient] HTTP server '".concat(serverName, "' ready"));
|
|
363
|
+
_state.label = 7;
|
|
364
|
+
case 7:
|
|
365
|
+
url = new URL(serverConfig.url);
|
|
366
|
+
// Check for DCR support and handle authentication automatically
|
|
367
|
+
baseUrl = extractBaseUrl(serverConfig.url);
|
|
368
|
+
return [
|
|
369
|
+
4,
|
|
370
|
+
withTimeout((0, _indexts.probeAuthCapabilities)(baseUrl), 5000, 'DCR capability discovery')
|
|
371
|
+
];
|
|
372
|
+
case 8:
|
|
373
|
+
capabilities = _state.sent();
|
|
374
|
+
if (!capabilities.supportsDcr) return [
|
|
375
|
+
3,
|
|
376
|
+
11
|
|
377
|
+
];
|
|
378
|
+
logger.debug("\uD83D\uDD10 Server '".concat(serverName, "' supports DCR authentication"));
|
|
379
|
+
return [
|
|
380
|
+
4,
|
|
381
|
+
(0, _getport.default)()
|
|
382
|
+
];
|
|
383
|
+
case 9:
|
|
384
|
+
port = _state.sent();
|
|
385
|
+
redirectUri = "http://localhost:".concat(port, "/callback");
|
|
386
|
+
// Handle authentication using DcrAuthenticator with fully resolved redirectUri
|
|
387
|
+
authenticator = new _indexts1.DcrAuthenticator(_object_spread({
|
|
388
|
+
headless: false,
|
|
389
|
+
redirectUri: redirectUri,
|
|
390
|
+
logger: logger
|
|
391
|
+
}, options === null || options === void 0 ? void 0 : options.dcrAuthenticator));
|
|
392
|
+
return [
|
|
393
|
+
4,
|
|
394
|
+
authenticator.ensureAuthenticated(baseUrl, capabilities)
|
|
395
|
+
];
|
|
396
|
+
case 10:
|
|
397
|
+
tokens = _state.sent();
|
|
398
|
+
authToken = tokens.accessToken;
|
|
399
|
+
logger.debug("✅ Authentication complete for '".concat(serverName, "'"));
|
|
400
|
+
return [
|
|
401
|
+
3,
|
|
402
|
+
12
|
|
403
|
+
];
|
|
404
|
+
case 11:
|
|
405
|
+
logger.debug("ℹ️ Server '".concat(serverName, "' does not support DCR - connecting without authentication"));
|
|
406
|
+
_state.label = 12;
|
|
407
|
+
case 12:
|
|
408
|
+
_state.trys.push([
|
|
409
|
+
12,
|
|
410
|
+
14,
|
|
411
|
+
,
|
|
412
|
+
22
|
|
413
|
+
]);
|
|
414
|
+
// Try modern Streamable HTTP first (protocol version 2025-03-26)
|
|
415
|
+
// Merge static headers from config with DCR auth headers (DCR Authorization takes precedence)
|
|
416
|
+
staticHeaders = serverConfig.headers || {};
|
|
417
|
+
dcrHeaders = authToken ? {
|
|
418
|
+
Authorization: "Bearer ".concat(authToken)
|
|
419
|
+
} : {};
|
|
420
|
+
mergedHeaders = _object_spread({}, staticHeaders, dcrHeaders);
|
|
421
|
+
transportOptions = Object.keys(mergedHeaders).length > 0 ? {
|
|
422
|
+
requestInit: {
|
|
423
|
+
headers: mergedHeaders
|
|
424
|
+
}
|
|
425
|
+
} : undefined;
|
|
426
|
+
transport2 = new _streamableHttp.StreamableHTTPClientTransport(url, transportOptions);
|
|
427
|
+
// Type assertion: SDK transport has sessionId: string | undefined but Transport expects string
|
|
428
|
+
// This is safe at runtime - the undefined is valid per MCP spec
|
|
429
|
+
return [
|
|
430
|
+
4,
|
|
431
|
+
withTimeout(client.connect(transport2), 30000, 'StreamableHTTP connection')
|
|
432
|
+
];
|
|
433
|
+
case 13:
|
|
434
|
+
_state.sent();
|
|
435
|
+
return [
|
|
436
|
+
3,
|
|
437
|
+
22
|
|
438
|
+
];
|
|
439
|
+
case 14:
|
|
440
|
+
error = _state.sent();
|
|
441
|
+
// Fall back to SSE transport (MCP protocol version 2024-11-05)
|
|
442
|
+
// SSE is a standard MCP transport used by many servers (e.g., FastMCP ecosystem)
|
|
443
|
+
errorMessage = _instanceof(error, Error) ? error.message : String(error);
|
|
444
|
+
// Fast-fail: Don't try SSE if connection was refused (server not running)
|
|
445
|
+
// Check error.cause.code for ECONNREFUSED (fetch errors wrap the actual error in cause)
|
|
446
|
+
cause = _instanceof(error, Error) ? error.cause : undefined;
|
|
447
|
+
isConnectionRefused = (cause === null || cause === void 0 ? void 0 : cause.code) === 'ECONNREFUSED' || errorMessage.includes('Connection refused');
|
|
448
|
+
if (!isConnectionRefused) return [
|
|
449
|
+
3,
|
|
450
|
+
16
|
|
451
|
+
];
|
|
452
|
+
// Clean up client resources before throwing
|
|
453
|
+
return [
|
|
454
|
+
4,
|
|
455
|
+
client.close().catch(function() {})
|
|
456
|
+
];
|
|
457
|
+
case 15:
|
|
458
|
+
_state.sent();
|
|
459
|
+
throw new Error("Server not running at ".concat(url));
|
|
460
|
+
case 16:
|
|
461
|
+
// Check for known errors that indicate SSE fallback is needed
|
|
462
|
+
shouldFallback = errorMessage.includes('Missing session ID') || // FastMCP specific
|
|
463
|
+
errorMessage.includes('404') || // Server doesn't have streamable HTTP endpoint
|
|
464
|
+
errorMessage.includes('405'); // Method not allowed
|
|
465
|
+
if (shouldFallback) {
|
|
466
|
+
logger.warn("Streamable HTTP failed (".concat(errorMessage, "), falling back to SSE transport"));
|
|
467
|
+
} else {
|
|
468
|
+
logger.warn('Streamable HTTP connection failed, trying SSE transport as fallback');
|
|
469
|
+
}
|
|
470
|
+
// Create new client for SSE transport (required per SDK pattern)
|
|
471
|
+
sseClient = new _index.Client({
|
|
472
|
+
name: 'mcp-cli-client',
|
|
473
|
+
version: '1.0.0'
|
|
474
|
+
}, {
|
|
475
|
+
capabilities: {}
|
|
476
|
+
});
|
|
477
|
+
// SSE transport with merged headers (static + DCR auth)
|
|
478
|
+
// Reuse the same header merging logic as Streamable HTTP
|
|
479
|
+
staticHeaders1 = serverConfig.headers || {};
|
|
480
|
+
dcrHeaders1 = authToken ? {
|
|
481
|
+
Authorization: "Bearer ".concat(authToken)
|
|
482
|
+
} : {};
|
|
483
|
+
mergedHeaders1 = _object_spread({}, staticHeaders1, dcrHeaders1);
|
|
484
|
+
sseTransportOptions = Object.keys(mergedHeaders1).length > 0 ? {
|
|
485
|
+
requestInit: {
|
|
486
|
+
headers: mergedHeaders1
|
|
487
|
+
}
|
|
488
|
+
} : undefined;
|
|
489
|
+
sseTransport = new _sse.SSEClientTransport(url, sseTransportOptions);
|
|
490
|
+
_state.label = 17;
|
|
491
|
+
case 17:
|
|
492
|
+
_state.trys.push([
|
|
493
|
+
17,
|
|
494
|
+
19,
|
|
495
|
+
,
|
|
496
|
+
21
|
|
497
|
+
]);
|
|
498
|
+
return [
|
|
499
|
+
4,
|
|
500
|
+
withTimeout(sseClient.connect(sseTransport), 30000, 'SSE connection')
|
|
501
|
+
];
|
|
502
|
+
case 18:
|
|
503
|
+
_state.sent();
|
|
504
|
+
// Return SSE client instead of original
|
|
505
|
+
return [
|
|
506
|
+
2,
|
|
507
|
+
sseClient
|
|
508
|
+
];
|
|
509
|
+
case 19:
|
|
510
|
+
sseError = _state.sent();
|
|
511
|
+
// SSE connection failed - clean up both clients before throwing
|
|
512
|
+
return [
|
|
513
|
+
4,
|
|
514
|
+
Promise.all([
|
|
515
|
+
client.close().catch(function() {}),
|
|
516
|
+
sseClient.close().catch(function() {})
|
|
517
|
+
])
|
|
518
|
+
];
|
|
519
|
+
case 20:
|
|
520
|
+
_state.sent();
|
|
521
|
+
throw sseError;
|
|
522
|
+
case 21:
|
|
523
|
+
return [
|
|
524
|
+
3,
|
|
525
|
+
22
|
|
526
|
+
];
|
|
527
|
+
case 22:
|
|
528
|
+
return [
|
|
529
|
+
2,
|
|
530
|
+
client
|
|
531
|
+
]; // Guaranteed ready when returned
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
})();
|
|
535
|
+
}
|
|
536
|
+
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/connection/connect-client.ts"],"sourcesContent":["/**\n * connect-mcp-client.ts\n *\n * Helper to connect MCP SDK clients to servers with intelligent transport inference.\n * Automatically detects transport type from URL protocol or type field.\n */\n\nimport '../monkey-patches.ts';\n\nimport { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';\nimport { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport getPort from 'get-port';\nimport { probeAuthCapabilities } from '../auth/index.ts';\nimport { DcrAuthenticator, type DcrAuthenticatorOptions } from '../dcr/index.ts';\nimport type { ServerProcess } from '../spawn/spawn-server.ts';\nimport type { ServersConfig } from '../spawn/spawn-servers.ts';\n\n/**\n * Minimal interface for connecting to servers.\n * Only needs config and servers map for connection logic.\n */\ninterface RegistryLike {\n config: ServersConfig;\n servers: Map<string, ServerProcess>;\n}\n\nimport type { McpServerEntry, TransportType } from '../types.ts';\nimport { logger as defaultLogger, type Logger } from '../utils/logger.ts';\nimport { ExistingProcessTransport } from './existing-process-transport.ts';\nimport { waitForHttpReady } from './wait-for-http-ready.ts';\n\n/**\n * Wrap promise with timeout - throws if promise takes too long\n * Clears timeout when promise completes to prevent hanging event loop\n * @param promise - Promise to wrap\n * @param ms - Timeout in milliseconds\n * @param operation - Description of operation for error message\n * @returns Promise result or timeout error\n */\nasync function withTimeout<T>(promise: Promise<T>, ms: number, operation: string): Promise<T> {\n let timeoutId: NodeJS.Timeout;\n\n return Promise.race([\n promise.finally(() => clearTimeout(timeoutId)),\n new Promise<T>((_, reject) => {\n timeoutId = setTimeout(() => reject(new Error(`Timeout after ${ms}ms: ${operation}`)), ms);\n }),\n ]);\n}\n\n/**\n * Extract base URL from MCP server URL\n * @param mcpUrl - Full MCP endpoint URL (e.g., https://example.com/mcp)\n * @returns Base URL (e.g., https://example.com)\n */\nfunction extractBaseUrl(mcpUrl: string): string {\n const url = new URL(mcpUrl);\n return `${url.protocol}//${url.host}`;\n}\n\n/**\n * Infer transport type from server configuration with validation.\n *\n * Priority:\n * 1. Explicit type field (if present)\n * 2. URL protocol (if URL present): http://, https://\n * 3. Default to 'stdio' (if neither present)\n *\n * @param config - Server configuration\n * @returns Transport type\n * @throws Error if configuration is invalid or has conflicts\n */\nfunction inferTransportType(config: McpServerEntry): TransportType {\n // Priority 1: Explicit type field\n if (config.type) {\n // Validate consistency with URL if both present\n if (config.url) {\n const url = new URL(config.url);\n const protocol = url.protocol;\n\n if ((protocol === 'http:' || protocol === 'https:') && config.type !== 'http' && config.type !== 'sse-ide') {\n throw new Error(`Conflicting transport: URL protocol '${protocol}' requires type 'http', but got '${config.type}'`);\n }\n }\n\n // Return normalized type\n if (config.type === 'http' || config.type === 'sse-ide') return 'http';\n if (config.type === 'stdio') return 'stdio';\n\n throw new Error(`Unsupported transport type: ${config.type}`);\n }\n\n // Priority 2: Infer from URL protocol\n if (config.url) {\n const url = new URL(config.url);\n const protocol = url.protocol;\n\n if (protocol === 'http:' || protocol === 'https:') {\n return 'http';\n }\n throw new Error(`Unsupported URL protocol: ${protocol}`);\n }\n\n // Priority 3: Default to stdio\n return 'stdio';\n}\n\n/**\n * Connect MCP SDK client to server with full readiness handling.\n * @internal - Use registry.connect() instead\n *\n * **Completely handles readiness**: transport availability + MCP protocol handshake.\n *\n * Transport is intelligently inferred and handled:\n * - **Stdio servers**: Direct MCP connect (fast for spawned processes)\n * - **HTTP servers**: Transport polling (/mcp endpoint) + MCP connect\n * - **Registry result**: Handles both spawned and external servers\n *\n * Returns only when server is fully MCP-ready (initialize handshake complete).\n *\n * @param registryOrConfig - Result from createServerRegistry() or servers config object\n * @param serverName - Server name from servers config\n * @returns Connected MCP SDK Client (guaranteed ready)\n *\n * @example\n * // Using registry (recommended)\n * const registry = createServerRegistry({ echo: { command: 'node', args: ['server.ts'] } });\n * const client = await registry.connect('echo');\n * // Server is fully ready - transport available + MCP handshake complete\n *\n * @example\n * // HTTP server readiness (waits for /mcp polling + MCP handshake)\n * const registry = createServerRegistry(\n * { http: { type: 'http', url: 'http://localhost:3000/mcp', start: {...} } },\n * { dialects: ['start'] }\n * );\n * const client = await registry.connect('http');\n * // 1. Waits for HTTP server to respond on /mcp\n * // 2. Performs MCP initialize handshake\n * // 3. Returns ready client\n */\nexport async function connectMcpClient(\n registryOrConfig: RegistryLike | ServersConfig,\n serverName: string,\n options?: {\n dcrAuthenticator?: Partial<DcrAuthenticatorOptions>;\n logger?: Logger;\n }\n): Promise<Client> {\n // Detect whether we have a RegistryLike instance or just config\n const isRegistry = 'servers' in registryOrConfig && registryOrConfig.servers instanceof Map;\n const serversConfig = isRegistry ? (registryOrConfig as RegistryLike).config : registryOrConfig;\n const registry = isRegistry ? (registryOrConfig as RegistryLike) : undefined;\n const logger = options?.logger ?? defaultLogger;\n\n const serverConfig = serversConfig[serverName];\n\n if (!serverConfig) {\n const available = Object.keys(serversConfig).join(', ');\n throw new Error(`Server '${serverName}' not found in config. Available servers: ${available || 'none'}`);\n }\n\n // Infer transport type with validation\n const transportType = inferTransportType(serverConfig);\n\n // Create MCP client\n const client = new Client({ name: 'mcp-cli-client', version: '1.0.0' }, { capabilities: {} });\n\n // Connect based on inferred transport\n if (transportType === 'stdio') {\n // Check if we have a spawned process in the registry\n const serverHandle = registry?.servers.get(serverName);\n\n if (serverHandle) {\n // Reuse the already-spawned process\n const transport = new ExistingProcessTransport(serverHandle.process);\n await client.connect(transport);\n } else {\n // No registry or server not in registry - spawn new process directly\n // This is the standard fallback when process management is not used\n if (!serverConfig.command) {\n throw new Error(`Server '${serverName}' has stdio transport but missing 'command' field`);\n }\n\n const transport = new StdioClientTransport({\n command: serverConfig.command,\n args: serverConfig.args || [],\n env: serverConfig.env || {},\n });\n\n // client.connect() performs initialize handshake - when it resolves, server is ready\n await client.connect(transport);\n }\n } else if (transportType === 'http') {\n if (!('url' in serverConfig) || !serverConfig.url) {\n throw new Error(`Server '${serverName}' has http transport but missing 'url' field`);\n }\n\n // Check if this is a freshly spawned HTTP server (from registry)\n // that might not be ready yet - transport readiness check needed\n const isSpawnedHttp = registry?.servers.has(serverName);\n\n if (isSpawnedHttp) {\n logger.debug(`[connectMcpClient] waiting for HTTP server '${serverName}' at ${serverConfig.url}`);\n await waitForHttpReady(serverConfig.url);\n logger.debug(`[connectMcpClient] HTTP server '${serverName}' ready`);\n }\n\n const url = new URL(serverConfig.url);\n\n // Check for DCR support and handle authentication automatically\n const baseUrl = extractBaseUrl(serverConfig.url);\n const capabilities = await withTimeout(probeAuthCapabilities(baseUrl), 5000, 'DCR capability discovery');\n\n let authToken: string | undefined;\n\n if (capabilities.supportsDcr) {\n logger.debug(`🔐 Server '${serverName}' supports DCR authentication`);\n\n // Get available port and create the exact redirect URI to use\n const port = await getPort();\n const redirectUri = `http://localhost:${port}/callback`;\n\n // Handle authentication using DcrAuthenticator with fully resolved redirectUri\n const authenticator = new DcrAuthenticator({\n headless: false,\n redirectUri,\n logger,\n ...options?.dcrAuthenticator,\n });\n\n // Ensure we have valid tokens (performs DCR + OAuth if needed)\n const tokens = await authenticator.ensureAuthenticated(baseUrl, capabilities);\n authToken = tokens.accessToken;\n\n logger.debug(`✅ Authentication complete for '${serverName}'`);\n } else {\n logger.debug(`ℹ️ Server '${serverName}' does not support DCR - connecting without authentication`);\n }\n\n try {\n // Try modern Streamable HTTP first (protocol version 2025-03-26)\n // Merge static headers from config with DCR auth headers (DCR Authorization takes precedence)\n const staticHeaders = serverConfig.headers || {};\n const dcrHeaders = authToken ? { Authorization: `Bearer ${authToken}` } : {};\n const mergedHeaders = { ...staticHeaders, ...dcrHeaders };\n\n const transportOptions =\n Object.keys(mergedHeaders).length > 0\n ? {\n requestInit: {\n headers: mergedHeaders,\n },\n }\n : undefined;\n\n const transport = new StreamableHTTPClientTransport(url, transportOptions);\n // Type assertion: SDK transport has sessionId: string | undefined but Transport expects string\n // This is safe at runtime - the undefined is valid per MCP spec\n await withTimeout(client.connect(transport as unknown as Transport), 30000, 'StreamableHTTP connection');\n } catch (error) {\n // Fall back to SSE transport (MCP protocol version 2024-11-05)\n // SSE is a standard MCP transport used by many servers (e.g., FastMCP ecosystem)\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n // Fast-fail: Don't try SSE if connection was refused (server not running)\n // Check error.cause.code for ECONNREFUSED (fetch errors wrap the actual error in cause)\n const cause = error instanceof Error ? (error as Error & { cause?: { code?: string } }).cause : undefined;\n const isConnectionRefused = cause?.code === 'ECONNREFUSED' || errorMessage.includes('Connection refused');\n\n if (isConnectionRefused) {\n // Clean up client resources before throwing\n await client.close().catch(() => {});\n throw new Error(`Server not running at ${url}`);\n }\n\n // Check for known errors that indicate SSE fallback is needed\n const shouldFallback =\n errorMessage.includes('Missing session ID') || // FastMCP specific\n errorMessage.includes('404') || // Server doesn't have streamable HTTP endpoint\n errorMessage.includes('405'); // Method not allowed\n\n if (shouldFallback) {\n logger.warn(`Streamable HTTP failed (${errorMessage}), falling back to SSE transport`);\n } else {\n logger.warn('Streamable HTTP connection failed, trying SSE transport as fallback');\n }\n\n // Create new client for SSE transport (required per SDK pattern)\n const sseClient = new Client({ name: 'mcp-cli-client', version: '1.0.0' }, { capabilities: {} });\n\n // SSE transport with merged headers (static + DCR auth)\n // Reuse the same header merging logic as Streamable HTTP\n const staticHeaders = serverConfig.headers || {};\n const dcrHeaders = authToken ? { Authorization: `Bearer ${authToken}` } : {};\n const mergedHeaders = { ...staticHeaders, ...dcrHeaders };\n\n const sseTransportOptions =\n Object.keys(mergedHeaders).length > 0\n ? {\n requestInit: {\n headers: mergedHeaders,\n },\n }\n : undefined;\n\n const sseTransport = new SSEClientTransport(url, sseTransportOptions);\n\n try {\n await withTimeout(sseClient.connect(sseTransport), 30000, 'SSE connection');\n // Return SSE client instead of original\n return sseClient;\n } catch (sseError) {\n // SSE connection failed - clean up both clients before throwing\n await Promise.all([client.close().catch(() => {}), sseClient.close().catch(() => {})]);\n throw sseError;\n }\n }\n }\n\n return client; // Guaranteed ready when returned\n}\n"],"names":["connectMcpClient","withTimeout","promise","ms","operation","timeoutId","Promise","race","finally","clearTimeout","_","reject","setTimeout","Error","extractBaseUrl","mcpUrl","url","URL","protocol","host","inferTransportType","config","type","registryOrConfig","serverName","options","isRegistry","serversConfig","registry","logger","serverConfig","available","transportType","client","serverHandle","transport","isSpawnedHttp","baseUrl","capabilities","authToken","port","redirectUri","authenticator","tokens","staticHeaders","dcrHeaders","mergedHeaders","transportOptions","error","errorMessage","cause","isConnectionRefused","shouldFallback","sseClient","sseTransportOptions","sseTransport","sseError","servers","Map","undefined","defaultLogger","Object","keys","join","Client","name","version","get","ExistingProcessTransport","process","connect","command","StdioClientTransport","args","env","has","debug","waitForHttpReady","probeAuthCapabilities","supportsDcr","getPort","DcrAuthenticator","headless","dcrAuthenticator","ensureAuthenticated","accessToken","headers","Authorization","length","requestInit","StreamableHTTPClientTransport","message","String","code","includes","close","catch","warn","SSEClientTransport","all"],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* existing-process-transport.ts
|
|
3
|
+
*
|
|
4
|
+
* MCP transport that wraps an existing child process for stdio communication.
|
|
5
|
+
* Used when connecting to servers already spawned by initServers().
|
|
6
|
+
*/
|
|
7
|
+
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
8
|
+
import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
|
|
9
|
+
import type { ChildProcess } from 'child_process';
|
|
10
|
+
/**
|
|
11
|
+
* Transport that communicates with an existing child process via stdio.
|
|
12
|
+
* Does NOT spawn a new process - uses the one provided.
|
|
13
|
+
*/
|
|
14
|
+
export declare class ExistingProcessTransport implements Transport {
|
|
15
|
+
private _process;
|
|
16
|
+
private _readBuffer;
|
|
17
|
+
private _dataHandler;
|
|
18
|
+
private _errorHandler;
|
|
19
|
+
onclose?: () => void;
|
|
20
|
+
onerror?: (error: Error) => void;
|
|
21
|
+
onmessage?: (message: JSONRPCMessage) => void;
|
|
22
|
+
constructor(process: ChildProcess);
|
|
23
|
+
/**
|
|
24
|
+
* Start the transport - sets up stdio listeners on existing process.
|
|
25
|
+
*/
|
|
26
|
+
start(): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Process buffered messages from stdout.
|
|
29
|
+
*/
|
|
30
|
+
private processReadBuffer;
|
|
31
|
+
/**
|
|
32
|
+
* Close the transport - close without killing the shared process.
|
|
33
|
+
* The process is managed by the cluster and may have other active connections.
|
|
34
|
+
*/
|
|
35
|
+
close(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Send a message to the server via stdin.
|
|
38
|
+
*/
|
|
39
|
+
send(message: JSONRPCMessage): Promise<void>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* existing-process-transport.ts
|
|
3
|
+
*
|
|
4
|
+
* MCP transport that wraps an existing child process for stdio communication.
|
|
5
|
+
* Used when connecting to servers already spawned by initServers().
|
|
6
|
+
*/
|
|
7
|
+
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
8
|
+
import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
|
|
9
|
+
import type { ChildProcess } from 'child_process';
|
|
10
|
+
/**
|
|
11
|
+
* Transport that communicates with an existing child process via stdio.
|
|
12
|
+
* Does NOT spawn a new process - uses the one provided.
|
|
13
|
+
*/
|
|
14
|
+
export declare class ExistingProcessTransport implements Transport {
|
|
15
|
+
private _process;
|
|
16
|
+
private _readBuffer;
|
|
17
|
+
private _dataHandler;
|
|
18
|
+
private _errorHandler;
|
|
19
|
+
onclose?: () => void;
|
|
20
|
+
onerror?: (error: Error) => void;
|
|
21
|
+
onmessage?: (message: JSONRPCMessage) => void;
|
|
22
|
+
constructor(process: ChildProcess);
|
|
23
|
+
/**
|
|
24
|
+
* Start the transport - sets up stdio listeners on existing process.
|
|
25
|
+
*/
|
|
26
|
+
start(): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Process buffered messages from stdout.
|
|
29
|
+
*/
|
|
30
|
+
private processReadBuffer;
|
|
31
|
+
/**
|
|
32
|
+
* Close the transport - close without killing the shared process.
|
|
33
|
+
* The process is managed by the cluster and may have other active connections.
|
|
34
|
+
*/
|
|
35
|
+
close(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Send a message to the server via stdin.
|
|
38
|
+
*/
|
|
39
|
+
send(message: JSONRPCMessage): Promise<void>;
|
|
40
|
+
}
|