@h1deya/langchain-mcp-tools 0.2.6 → 0.2.7
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 +17 -0
- package/dist/langchain-mcp-tools.d.ts +4 -1
- package/dist/langchain-mcp-tools.js +144 -96
- package/dist/logger.d.ts +1 -1
- package/dist/logger.js +11 -11
- package/package.json +2 -7
package/README.md
CHANGED
|
@@ -158,6 +158,7 @@ TypeScript SDK's [`StdioServerParameters`](https://github.com/modelcontextprotoc
|
|
|
158
158
|
"streamable-http-server": {
|
|
159
159
|
url: `http://${server_host}:${server_port}/...`,
|
|
160
160
|
transport: "streamable_http"
|
|
161
|
+
// type: "http" // VSCode-style config works too
|
|
161
162
|
},
|
|
162
163
|
|
|
163
164
|
// Explicit SSE
|
|
@@ -172,6 +173,22 @@ TypeScript SDK's [`StdioServerParameters`](https://github.com/modelcontextprotoc
|
|
|
172
173
|
},
|
|
173
174
|
```
|
|
174
175
|
|
|
176
|
+
For the convenience of adding authorization headers, the following shorthand expression is supported.
|
|
177
|
+
This header configuration will be overridden if either `streamableHTTPOptions` or `sseOptions` is specified.
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
github: {
|
|
181
|
+
type: "http", // recommended to specify the protocol explicitly when authentication is used
|
|
182
|
+
url: "https://api.githubcopilot.com/mcp/",
|
|
183
|
+
headers: {
|
|
184
|
+
"Authorization": `Bearer ${process.env.GITHUB_PERSONAL_ACCESS_TOKEN}`
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
NOTE: When accessing the GitHub MCP server, [GitHub PAT (Personal Access Token)](https://github.com/settings/personal-access-tokens)
|
|
190
|
+
alone is not enough; your GitHub account must have an active Copilot subscription or be assigned a Copilot license through your organization.
|
|
191
|
+
|
|
175
192
|
**Auto-detection behavior (default):**
|
|
176
193
|
- For HTTP/HTTPS URLs without explicit `transport`, the library follows [MCP specification recommendations](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#backwards-compatibility)
|
|
177
194
|
- First attempts Streamable HTTP transport
|
|
@@ -11,7 +11,8 @@ import { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
|
11
11
|
*/
|
|
12
12
|
export interface CommandBasedConfig {
|
|
13
13
|
url?: never;
|
|
14
|
-
transport?:
|
|
14
|
+
transport?: string;
|
|
15
|
+
type?: string;
|
|
15
16
|
command: string;
|
|
16
17
|
args?: string[];
|
|
17
18
|
env?: Record<string, string>;
|
|
@@ -27,6 +28,8 @@ export interface CommandBasedConfig {
|
|
|
27
28
|
export interface UrlBasedConfig {
|
|
28
29
|
url: string;
|
|
29
30
|
transport?: string;
|
|
31
|
+
type?: string;
|
|
32
|
+
headers?: Record<string, string>;
|
|
30
33
|
command?: never;
|
|
31
34
|
args?: never;
|
|
32
35
|
env?: never;
|
|
@@ -93,7 +93,7 @@ export async function convertMcpToLangchainTools(configs, options) {
|
|
|
93
93
|
* performing schema conversions to help track which servers need upstream fixes.
|
|
94
94
|
*
|
|
95
95
|
* Gemini supports only a limited subset of OpenAPI 3.0 Schema properties:
|
|
96
|
-
* - string: enum, format (only
|
|
96
|
+
* - string: enum, format (only "date-time" documented)
|
|
97
97
|
* - integer/number: format only
|
|
98
98
|
* - array: minItems, maxItems, items
|
|
99
99
|
* - object: properties, required, propertyOrdering, nullable
|
|
@@ -112,7 +112,7 @@ export async function convertMcpToLangchainTools(configs, options) {
|
|
|
112
112
|
* @internal This function is meant to be used internally by convertSingleMcpToLangchainTools
|
|
113
113
|
*/
|
|
114
114
|
function sanitizeSchemaForGemini(schema, logger, toolName) {
|
|
115
|
-
if (typeof schema !==
|
|
115
|
+
if (typeof schema !== "object" || schema === null) {
|
|
116
116
|
return schema;
|
|
117
117
|
}
|
|
118
118
|
const sanitized = { ...schema };
|
|
@@ -120,25 +120,25 @@ function sanitizeSchemaForGemini(schema, logger, toolName) {
|
|
|
120
120
|
const convertedProperties = [];
|
|
121
121
|
// Remove unsupported properties
|
|
122
122
|
if (sanitized.exclusiveMinimum !== undefined) {
|
|
123
|
-
removedProperties.push(
|
|
123
|
+
removedProperties.push("exclusiveMinimum");
|
|
124
124
|
delete sanitized.exclusiveMinimum;
|
|
125
125
|
}
|
|
126
126
|
if (sanitized.exclusiveMaximum !== undefined) {
|
|
127
|
-
removedProperties.push(
|
|
127
|
+
removedProperties.push("exclusiveMaximum");
|
|
128
128
|
delete sanitized.exclusiveMaximum;
|
|
129
129
|
}
|
|
130
130
|
// Convert exclusiveMinimum/Maximum to minimum/maximum if needed
|
|
131
131
|
if (schema.exclusiveMinimum !== undefined) {
|
|
132
132
|
sanitized.minimum = schema.exclusiveMinimum;
|
|
133
|
-
convertedProperties.push(
|
|
133
|
+
convertedProperties.push("exclusiveMinimum → minimum");
|
|
134
134
|
}
|
|
135
135
|
if (schema.exclusiveMaximum !== undefined) {
|
|
136
136
|
sanitized.maximum = schema.exclusiveMaximum;
|
|
137
|
-
convertedProperties.push(
|
|
137
|
+
convertedProperties.push("exclusiveMaximum → maximum");
|
|
138
138
|
}
|
|
139
|
-
// Remove unsupported string formats (Gemini only supports
|
|
140
|
-
if (sanitized.type ===
|
|
141
|
-
const supportedFormats = [
|
|
139
|
+
// Remove unsupported string formats (Gemini only supports "enum" and "date-time")
|
|
140
|
+
if (sanitized.type === "string" && sanitized.format) {
|
|
141
|
+
const supportedFormats = ["enum", "date-time"];
|
|
142
142
|
if (!supportedFormats.includes(sanitized.format)) {
|
|
143
143
|
removedProperties.push(`format: ${sanitized.format}`);
|
|
144
144
|
delete sanitized.format;
|
|
@@ -148,12 +148,12 @@ function sanitizeSchemaForGemini(schema, logger, toolName) {
|
|
|
148
148
|
if (logger && toolName && (removedProperties.length > 0 || convertedProperties.length > 0)) {
|
|
149
149
|
const changes = [];
|
|
150
150
|
if (removedProperties.length > 0) {
|
|
151
|
-
changes.push(`removed: ${removedProperties.join(
|
|
151
|
+
changes.push(`removed: ${removedProperties.join(", ")}`);
|
|
152
152
|
}
|
|
153
153
|
if (convertedProperties.length > 0) {
|
|
154
|
-
changes.push(`converted: ${convertedProperties.join(
|
|
154
|
+
changes.push(`converted: ${convertedProperties.join(", ")}`);
|
|
155
155
|
}
|
|
156
|
-
logger.warn(`MCP tool "${toolName}": schema sanitized for Gemini compatibility (${changes.join(
|
|
156
|
+
logger.warn(`MCP tool "${toolName}": schema sanitized for Gemini compatibility (${changes.join("; ")})`);
|
|
157
157
|
}
|
|
158
158
|
// Recursively process nested objects and arrays
|
|
159
159
|
if (sanitized.properties) {
|
|
@@ -174,7 +174,7 @@ function sanitizeSchemaForGemini(schema, logger, toolName) {
|
|
|
174
174
|
if (sanitized.items) {
|
|
175
175
|
sanitized.items = sanitizeSchemaForGemini(sanitized.items, logger, toolName);
|
|
176
176
|
}
|
|
177
|
-
if (sanitized.additionalProperties && typeof sanitized.additionalProperties ===
|
|
177
|
+
if (sanitized.additionalProperties && typeof sanitized.additionalProperties === "object") {
|
|
178
178
|
sanitized.additionalProperties = sanitizeSchemaForGemini(sanitized.additionalProperties, logger, toolName);
|
|
179
179
|
}
|
|
180
180
|
return sanitized;
|
|
@@ -207,6 +207,9 @@ function createStreamableHttpOptions(config, logger, serverName) {
|
|
|
207
207
|
options.sessionId = config.streamableHTTPOptions.sessionId;
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
|
+
else if (config.headers) {
|
|
211
|
+
options.requestInit = { headers: config.headers };
|
|
212
|
+
}
|
|
210
213
|
return Object.keys(options).length > 0 ? options : undefined;
|
|
211
214
|
}
|
|
212
215
|
/**
|
|
@@ -233,6 +236,9 @@ function createSseOptions(config, logger, serverName) {
|
|
|
233
236
|
if (config.sseOptions.requestInit) {
|
|
234
237
|
options.requestInit = config.sseOptions.requestInit;
|
|
235
238
|
}
|
|
239
|
+
else if (config.headers) {
|
|
240
|
+
options.requestInit = { headers: config.headers };
|
|
241
|
+
}
|
|
236
242
|
}
|
|
237
243
|
return Object.keys(options).length > 0 ? options : undefined;
|
|
238
244
|
}
|
|
@@ -246,34 +252,126 @@ function createSseOptions(config, logger, serverName) {
|
|
|
246
252
|
* @internal This function is meant to be used internally by createHttpTransportWithFallback
|
|
247
253
|
*/
|
|
248
254
|
function is4xxError(error) {
|
|
249
|
-
if (!error || typeof error !==
|
|
255
|
+
if (!error || typeof error !== "object") {
|
|
250
256
|
return false;
|
|
251
257
|
}
|
|
252
258
|
// Check for common error patterns that indicate 4xx responses
|
|
253
259
|
const errorObj = error;
|
|
254
|
-
// Check if it
|
|
255
|
-
if (errorObj.status && typeof errorObj.status ===
|
|
260
|
+
// Check if it"s a fetch Response error with status
|
|
261
|
+
if (errorObj.status && typeof errorObj.status === "number") {
|
|
256
262
|
return errorObj.status >= 400 && errorObj.status < 500;
|
|
257
263
|
}
|
|
258
264
|
// Check if it's wrapped in a Response object
|
|
259
|
-
if (errorObj.response && errorObj.response.status && typeof errorObj.response.status ===
|
|
265
|
+
if (errorObj.response && errorObj.response.status && typeof errorObj.response.status === "number") {
|
|
260
266
|
return errorObj.response.status >= 400 && errorObj.response.status < 500;
|
|
261
267
|
}
|
|
262
268
|
// Check for error messages that typically indicate 4xx errors
|
|
263
269
|
const message = errorObj.message || errorObj.toString();
|
|
264
|
-
if (typeof message ===
|
|
270
|
+
if (typeof message === "string") {
|
|
265
271
|
return /4[0-9]{2}/.test(message) ||
|
|
266
|
-
message.includes(
|
|
267
|
-
message.includes(
|
|
268
|
-
message.includes(
|
|
269
|
-
message.includes(
|
|
270
|
-
message.includes(
|
|
272
|
+
message.includes("Bad Request") ||
|
|
273
|
+
message.includes("Unauthorized") ||
|
|
274
|
+
message.includes("Forbidden") ||
|
|
275
|
+
message.includes("Not Found") ||
|
|
276
|
+
message.includes("Method Not Allowed");
|
|
271
277
|
}
|
|
272
278
|
return false;
|
|
273
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* Tests MCP server transport support using direct POST InitializeRequest.
|
|
282
|
+
* Follows the official MCP specification's recommended approach for backwards compatibility.
|
|
283
|
+
*
|
|
284
|
+
* See: https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#backwards-compatibility
|
|
285
|
+
*
|
|
286
|
+
* @param url - The URL to test
|
|
287
|
+
* @param config - URL-based server configuration
|
|
288
|
+
* @param logger - Logger instance for recording test attempts
|
|
289
|
+
* @param serverName - Server name for logging context
|
|
290
|
+
* @returns A promise that resolves to the detected transport type
|
|
291
|
+
*
|
|
292
|
+
* @internal This function is meant to be used internally by createHttpTransportWithFallback
|
|
293
|
+
*/
|
|
294
|
+
async function testTransportSupport(url, config, logger, serverName) {
|
|
295
|
+
logger.debug(`MCP server "${serverName}": testing transport support using InitializeRequest`);
|
|
296
|
+
// Create InitializeRequest as per MCP specification
|
|
297
|
+
const initRequest = {
|
|
298
|
+
jsonrpc: "2.0",
|
|
299
|
+
id: `transport-test-${Date.now()}`,
|
|
300
|
+
method: "initialize",
|
|
301
|
+
params: {
|
|
302
|
+
protocolVersion: "2024-11-05", // Latest supported version
|
|
303
|
+
capabilities: {},
|
|
304
|
+
clientInfo: {
|
|
305
|
+
name: "mcp-transport-test",
|
|
306
|
+
version: "1.0.0"
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
// Prepare headers as required by MCP spec
|
|
311
|
+
const headers = {
|
|
312
|
+
"Content-Type": "application/json",
|
|
313
|
+
"Accept": "application/json, text/event-stream" // Required by spec
|
|
314
|
+
};
|
|
315
|
+
// Add authentication headers if available
|
|
316
|
+
if (config.streamableHTTPOptions?.authProvider) {
|
|
317
|
+
try {
|
|
318
|
+
const tokens = await config.streamableHTTPOptions.authProvider.tokens();
|
|
319
|
+
if (tokens?.access_token) {
|
|
320
|
+
headers["Authorization"] = `${tokens.token_type || "Bearer"} ${tokens.access_token}`;
|
|
321
|
+
logger.debug(`MCP server "${serverName}": added authentication to transport test`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
catch (authError) {
|
|
325
|
+
logger.debug(`MCP server "${serverName}": authentication setup failed for transport test:`, authError);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
// Merge custom headers from config
|
|
329
|
+
if (config.streamableHTTPOptions?.requestInit?.headers) {
|
|
330
|
+
Object.assign(headers, config.streamableHTTPOptions.requestInit.headers);
|
|
331
|
+
}
|
|
332
|
+
if (config.sseOptions?.requestInit?.headers) {
|
|
333
|
+
Object.assign(headers, config.sseOptions.requestInit.headers);
|
|
334
|
+
}
|
|
335
|
+
try {
|
|
336
|
+
logger.debug(`MCP server "${serverName}": POST InitializeRequest to test Streamable HTTP support`);
|
|
337
|
+
// POST InitializeRequest directly to test Streamable HTTP support
|
|
338
|
+
const response = await fetch(url.toString(), {
|
|
339
|
+
method: "POST",
|
|
340
|
+
headers,
|
|
341
|
+
body: JSON.stringify(initRequest),
|
|
342
|
+
...config.streamableHTTPOptions?.requestInit
|
|
343
|
+
});
|
|
344
|
+
logger.debug(`MCP server "${serverName}": transport test response: ${response.status} ${response.statusText}`);
|
|
345
|
+
if (response.ok) {
|
|
346
|
+
// Success indicates Streamable HTTP support
|
|
347
|
+
logger.info(`MCP server "${serverName}": detected Streamable HTTP transport support`);
|
|
348
|
+
return "streamable_http";
|
|
349
|
+
}
|
|
350
|
+
else if (response.status >= 400 && response.status < 500) {
|
|
351
|
+
// 4xx error indicates fallback to SSE per MCP spec
|
|
352
|
+
logger.info(`MCP server "${serverName}": received ${response.status}, falling back to SSE transport`);
|
|
353
|
+
return "sse";
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
// Other errors should be re-thrown
|
|
357
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
catch (error) {
|
|
361
|
+
// Network errors or other issues
|
|
362
|
+
logger.debug(`MCP server "${serverName}": transport test failed:`, error);
|
|
363
|
+
// Check if it's a 4xx-like error
|
|
364
|
+
if (is4xxError(error)) {
|
|
365
|
+
logger.info(`MCP server "${serverName}": transport test failed with 4xx-like error, falling back to SSE`);
|
|
366
|
+
return "sse";
|
|
367
|
+
}
|
|
368
|
+
// Re-throw other errors (network issues, etc.)
|
|
369
|
+
throw error;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
274
372
|
/**
|
|
275
373
|
* Creates an HTTP transport with automatic fallback from Streamable HTTP to SSE.
|
|
276
|
-
* Follows the MCP specification recommendation to
|
|
374
|
+
* Follows the MCP specification recommendation to test with direct POST InitializeRequest first,
|
|
277
375
|
* then fall back to SSE if a 4xx error is encountered.
|
|
278
376
|
*
|
|
279
377
|
* See: https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#backwards-compatibility
|
|
@@ -288,32 +386,33 @@ function is4xxError(error) {
|
|
|
288
386
|
*/
|
|
289
387
|
async function createHttpTransportWithFallback(url, config, logger, serverName) {
|
|
290
388
|
// If transport is explicitly specified, respect user's choice
|
|
291
|
-
if (config.transport === "streamable_http"
|
|
389
|
+
if (config.transport === "streamable_http" || config.transport === "http" ||
|
|
390
|
+
config.type === "streamable_http" || config.type === "http") {
|
|
292
391
|
logger.debug(`MCP server "${serverName}": using explicitly configured Streamable HTTP transport`);
|
|
293
392
|
const options = createStreamableHttpOptions(config, logger, serverName);
|
|
294
393
|
return new StreamableHTTPClientTransport(url, options);
|
|
295
394
|
}
|
|
296
|
-
if (config.transport === "sse") {
|
|
395
|
+
if (config.transport === "sse" || config.type === "sse") {
|
|
297
396
|
logger.debug(`MCP server "${serverName}": using explicitly configured SSE transport`);
|
|
298
397
|
const options = createSseOptions(config, logger, serverName);
|
|
299
398
|
return new SSEClientTransport(url, options);
|
|
300
399
|
}
|
|
301
|
-
// Auto-detection:
|
|
302
|
-
logger.debug(`MCP server "${serverName}":
|
|
400
|
+
// Auto-detection: test with POST InitializeRequest per MCP specification
|
|
401
|
+
logger.debug(`MCP server "${serverName}": auto-detecting transport using MCP specification method`);
|
|
303
402
|
try {
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
if (is4xxError(error)) {
|
|
311
|
-
logger.info(`MCP server "${serverName}": Streamable HTTP failed with 4xx error, falling back to SSE transport`);
|
|
403
|
+
const detectedTransport = await testTransportSupport(url, config, logger, serverName);
|
|
404
|
+
if (detectedTransport === "streamable_http") {
|
|
405
|
+
const options = createStreamableHttpOptions(config, logger, serverName);
|
|
406
|
+
return new StreamableHTTPClientTransport(url, options);
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
312
409
|
const options = createSseOptions(config, logger, serverName);
|
|
313
410
|
return new SSEClientTransport(url, options);
|
|
314
411
|
}
|
|
315
|
-
|
|
316
|
-
|
|
412
|
+
}
|
|
413
|
+
catch (error) {
|
|
414
|
+
// If transport detection fails completely, log error and re-throw
|
|
415
|
+
logger.error(`MCP server "${serverName}": transport detection failed:`, error);
|
|
317
416
|
throw error;
|
|
318
417
|
}
|
|
319
418
|
}
|
|
@@ -391,68 +490,14 @@ async function convertSingleMcpToLangchainTools(serverName, config, logger) {
|
|
|
391
490
|
const urlConfig = config;
|
|
392
491
|
// Try to connect with Streamable HTTP first, fallback to SSE on 4xx errors
|
|
393
492
|
let connectionSucceeded = false;
|
|
394
|
-
//
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
}
|
|
398
|
-
else {
|
|
399
|
-
// Auto-detection with connection-level fallback
|
|
400
|
-
logger.debug(`MCP server "${serverName}": attempting Streamable HTTP transport with SSE fallback`);
|
|
401
|
-
try {
|
|
402
|
-
// First attempt: Streamable HTTP
|
|
403
|
-
const options = createStreamableHttpOptions(urlConfig, logger, serverName);
|
|
404
|
-
transport = new StreamableHTTPClientTransport(url, options);
|
|
405
|
-
logger.info(`MCP server "${serverName}": created Streamable HTTP transport, attempting connection`);
|
|
406
|
-
// Try to connect with Streamable HTTP
|
|
407
|
-
client = new Client({
|
|
408
|
-
name: "mcp-client",
|
|
409
|
-
version: "0.0.1",
|
|
410
|
-
}, {
|
|
411
|
-
capabilities: {},
|
|
412
|
-
});
|
|
413
|
-
await client.connect(transport);
|
|
414
|
-
connectionSucceeded = true;
|
|
415
|
-
logger.info(`MCP server "${serverName}": successfully connected using Streamable HTTP`);
|
|
416
|
-
}
|
|
417
|
-
catch (error) {
|
|
418
|
-
if (is4xxError(error)) {
|
|
419
|
-
logger.info(`MCP server "${serverName}": Streamable HTTP failed with 4xx error, falling back to SSE transport`);
|
|
420
|
-
// Cleanup failed transport and client
|
|
421
|
-
if (transport) {
|
|
422
|
-
try {
|
|
423
|
-
await transport.close();
|
|
424
|
-
}
|
|
425
|
-
catch (cleanupError) {
|
|
426
|
-
logger.debug(`MCP server "${serverName}": cleanup error during fallback:`, cleanupError);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
// Fallback to SSE
|
|
430
|
-
const options = createSseOptions(urlConfig, logger, serverName);
|
|
431
|
-
transport = new SSEClientTransport(url, options);
|
|
432
|
-
logger.info(`MCP server "${serverName}": created SSE transport, attempting fallback connection`);
|
|
433
|
-
// Create new client for SSE connection
|
|
434
|
-
client = new Client({
|
|
435
|
-
name: "mcp-client",
|
|
436
|
-
version: "0.0.1",
|
|
437
|
-
}, {
|
|
438
|
-
capabilities: {},
|
|
439
|
-
});
|
|
440
|
-
await client.connect(transport);
|
|
441
|
-
connectionSucceeded = true;
|
|
442
|
-
logger.info(`MCP server "${serverName}": successfully connected using SSE fallback`);
|
|
443
|
-
}
|
|
444
|
-
else {
|
|
445
|
-
// Re-throw non-4xx errors (network issues, etc.)
|
|
446
|
-
logger.error(`MCP server "${serverName}": Streamable HTTP transport failed with non-4xx error:`, error);
|
|
447
|
-
throw error;
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
}
|
|
493
|
+
// Use the updated transport detection with MCP spec compliance
|
|
494
|
+
transport = await createHttpTransportWithFallback(url, urlConfig, logger, serverName);
|
|
495
|
+
logger.info(`MCP server "${serverName}": created transport, attempting connection`);
|
|
451
496
|
}
|
|
452
497
|
else if (url?.protocol === "ws:" || url?.protocol === "wss:") {
|
|
453
498
|
transport = new WebSocketClientTransport(url);
|
|
454
499
|
}
|
|
455
|
-
else {
|
|
500
|
+
else if (!(config?.transport) || config?.transport === "stdio" || config?.type === "stdio") {
|
|
456
501
|
// NOTE: Some servers (e.g. Brave) seem to require PATH to be set.
|
|
457
502
|
// To avoid confusion, it was decided to automatically append it to the env
|
|
458
503
|
// if not explicitly set by the config.
|
|
@@ -469,6 +514,9 @@ async function convertSingleMcpToLangchainTools(serverName, config, logger) {
|
|
|
469
514
|
cwd: stdioServerConfig.cwd
|
|
470
515
|
});
|
|
471
516
|
}
|
|
517
|
+
else {
|
|
518
|
+
throw new McpInitializationError(serverName, `Failed to initialize MCP server: ${serverName}: unknown transport type: ${config?.transport}`);
|
|
519
|
+
}
|
|
472
520
|
// Only create client if not already created during auto-detection fallback
|
|
473
521
|
if (!client) {
|
|
474
522
|
client = new Client({
|
package/dist/logger.d.ts
CHANGED
package/dist/logger.js
CHANGED
|
@@ -9,12 +9,12 @@ var LogLevel;
|
|
|
9
9
|
LogLevel[LogLevel["FATAL"] = 5] = "FATAL";
|
|
10
10
|
})(LogLevel || (LogLevel = {}));
|
|
11
11
|
const LOG_COLORS = {
|
|
12
|
-
[LogLevel.TRACE]:
|
|
13
|
-
[LogLevel.DEBUG]:
|
|
14
|
-
[LogLevel.INFO]:
|
|
15
|
-
[LogLevel.WARN]:
|
|
16
|
-
[LogLevel.ERROR]:
|
|
17
|
-
[LogLevel.FATAL]:
|
|
12
|
+
[LogLevel.TRACE]: "\x1b[90m", // Gray
|
|
13
|
+
[LogLevel.DEBUG]: "\x1b[90m", // Gray
|
|
14
|
+
[LogLevel.INFO]: "\x1b[90m", // Gray
|
|
15
|
+
[LogLevel.WARN]: "\x1b[1;93m", // Bold bright yellow
|
|
16
|
+
[LogLevel.ERROR]: "\x1b[1;91m", // Bold bright red
|
|
17
|
+
[LogLevel.FATAL]: "\x1b[1;101m", // Red background, Bold text
|
|
18
18
|
};
|
|
19
19
|
const LOG_LEVEL_MAP = {
|
|
20
20
|
trace: LogLevel.TRACE,
|
|
@@ -26,12 +26,12 @@ const LOG_LEVEL_MAP = {
|
|
|
26
26
|
};
|
|
27
27
|
class Logger {
|
|
28
28
|
level;
|
|
29
|
-
static RESET =
|
|
29
|
+
static RESET = "\x1b[0m";
|
|
30
30
|
constructor({ level = LogLevel.INFO } = {}) {
|
|
31
31
|
this.level = this.parseLogLevel(level);
|
|
32
32
|
}
|
|
33
33
|
parseLogLevel(level) {
|
|
34
|
-
if (typeof level ===
|
|
34
|
+
if (typeof level === "number")
|
|
35
35
|
return level;
|
|
36
36
|
return LOG_LEVEL_MAP[level.toLowerCase()];
|
|
37
37
|
}
|
|
@@ -44,10 +44,10 @@ class Logger {
|
|
|
44
44
|
}
|
|
45
45
|
formatValue(value) {
|
|
46
46
|
if (value === null)
|
|
47
|
-
return
|
|
47
|
+
return "null";
|
|
48
48
|
if (value === undefined)
|
|
49
|
-
return
|
|
50
|
-
return typeof value ===
|
|
49
|
+
return "undefined";
|
|
50
|
+
return typeof value === "object" ? JSON.stringify(value, null, 2) : String(value);
|
|
51
51
|
}
|
|
52
52
|
trace(...args) { this.log(LogLevel.TRACE, ...args); }
|
|
53
53
|
debug(...args) { this.log(LogLevel.DEBUG, ...args); }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h1deya/langchain-mcp-tools",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "MCP To LangChain Tools Conversion Utility",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -36,30 +36,24 @@
|
|
|
36
36
|
"watch": "tsc --watch",
|
|
37
37
|
"lint": "eslint src",
|
|
38
38
|
"clean": "git clean -fdxn -e .env && read -p 'OK?' && git clean -fdx -e .env",
|
|
39
|
-
|
|
40
39
|
"_comment_test": "# Testing scripts",
|
|
41
40
|
"test": "vitest run",
|
|
42
41
|
"test:watch": "vitest",
|
|
43
42
|
"test:coverage": "vitest run --coverage",
|
|
44
|
-
|
|
45
43
|
"_comment_examples": "# Basic usage examples",
|
|
46
44
|
"example:simple": "tsx testfiles/simple-usage.ts",
|
|
47
|
-
|
|
48
45
|
"_comment_streamable": "# Streamable HTTP transport tests",
|
|
49
46
|
"test:streamable:auth:server": "tsx testfiles/streamable-http-auth-test-server.ts",
|
|
50
47
|
"test:streamable:auth:client": "tsx testfiles/streamable-http-auth-test-client.ts",
|
|
51
48
|
"test:streamable:stateless:server": "tsx testfiles/streamable-http-stateless-test-server.ts",
|
|
52
49
|
"test:streamable:stateless:client": "tsx testfiles/streamable-http-stateless-test-client.ts",
|
|
53
50
|
"test:streamable:auto-detection": "tsx testfiles/streamable-http-auto-detection-test.ts",
|
|
54
|
-
|
|
55
51
|
"_comment_sse": "# SSE transport tests (with authentication)",
|
|
56
52
|
"test:sse:server": "tsx testfiles/sse-auth-test-server.ts",
|
|
57
53
|
"test:sse:client": "tsx testfiles/sse-auth-test-client.ts",
|
|
58
|
-
|
|
59
54
|
"_comment_docs": "# Documentation scripts",
|
|
60
55
|
"docs:build": "npx typedoc --options typedoc.json",
|
|
61
56
|
"docs:deploy": "npm run docs:build && ghp-import -n -p -f docs",
|
|
62
|
-
|
|
63
57
|
"_comment_publish": "# Publishing scripts",
|
|
64
58
|
"publish:test": "npm run clean && npm install && npm publish --access=public --dry-run",
|
|
65
59
|
"publish:do": "npm run clean && npm install && npm publish --access=public"
|
|
@@ -77,6 +71,7 @@
|
|
|
77
71
|
"@langchain/langgraph": "^0.2.36",
|
|
78
72
|
"@langchain/openai": "^0.3.16",
|
|
79
73
|
"@types/node": "^22.10.5",
|
|
74
|
+
"@types/ws": "^8.18.1",
|
|
80
75
|
"@typescript-eslint/eslint-plugin": "^8.19.0",
|
|
81
76
|
"@typescript-eslint/parser": "^8.19.0",
|
|
82
77
|
"@vitest/coverage-v8": "^3.0.9",
|