@h1deya/langchain-mcp-tools 0.2.4 → 0.2.6
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 +132 -67
- package/dist/langchain-mcp-tools.d.ts +9 -0
- package/dist/langchain-mcp-tools.js +390 -61
- package/package.json +28 -10
package/README.md
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
# MCP To LangChain Tools Conversion Utility / TypeScript [](https://github.com/hideya/langchain-mcp-tools-ts/blob/main/LICENSE) [](https://www.npmjs.com/package/@h1deya/langchain-mcp-tools)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This is a simple, lightweight library intended to simplify the use of
|
|
4
|
+
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/)
|
|
5
|
+
server tools with LangChain.
|
|
6
|
+
|
|
7
|
+
Its simplicity and extra features for stdio MCP servers can make it useful as a basis for your own customizations.
|
|
8
|
+
However, it only supports text results of tool calls and does not support MCP features other than tools.
|
|
4
9
|
|
|
5
|
-
LangChain's official
|
|
10
|
+
LangChain's **official LangChain.js MCP Adapters** library,
|
|
11
|
+
which supports comprehensive integration with LangChain, has been released at:
|
|
6
12
|
- npmjs: https://www.npmjs.com/package/@langchain/mcp-adapters
|
|
7
|
-
- github: https://github.com/langchain-ai/langchainjs-mcp-adapters
|
|
13
|
+
- github: https://github.com/langchain-ai/langchainjs/tree/main/libs/langchain-mcp-adapters`
|
|
8
14
|
|
|
9
|
-
You may want to consider using the above if you don't have specific needs for
|
|
15
|
+
You may want to consider using the above if you don't have specific needs for this library.
|
|
10
16
|
|
|
11
17
|
## Introduction
|
|
12
18
|
|
|
@@ -14,23 +20,17 @@ This package is intended to simplify the use of
|
|
|
14
20
|
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/)
|
|
15
21
|
server tools with LangChain / TypeScript.
|
|
16
22
|
|
|
17
|
-
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
dramatically expands LLM's scope
|
|
21
|
-
by enabling external tool and resource integration, including
|
|
22
|
-
GitHub, Google Drive, Slack, Notion, Spotify, Docker, PostgreSQL, and more…
|
|
23
|
+
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is the de facto industry standard
|
|
24
|
+
that dramatically expands the scope of LLMs by enabling the integration of external tools and resources,
|
|
25
|
+
including DBs, GitHub, Google Drive, Docker, Slack, Notion, Spotify, and more.
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
[OpenAI has announced its adoption](https://techcrunch.com/2025/03/26/openai-adopts-rival-anthropics-standard-for-connecting-ai-models-to-data).
|
|
26
|
-
|
|
27
|
-
Over 2000 functional components available as MCP servers:
|
|
27
|
+
There are quite a few useful MCP servers already available:
|
|
28
28
|
|
|
29
29
|
- [MCP Server Listing on the Official Site](https://github.com/modelcontextprotocol/servers?tab=readme-ov-file#model-context-protocol-servers)
|
|
30
30
|
- [MCP.so - Find Awesome MCP Servers and Clients](https://mcp.so/)
|
|
31
31
|
- [Smithery: MCP Server Registry](https://smithery.ai/)
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
This utility's goal is to make these massive numbers of MCP servers easily accessible from LangChain.
|
|
34
34
|
|
|
35
35
|
It contains a utility function `convertMcpToLangchainTools()`.
|
|
36
36
|
This async function handles parallel initialization of specified multiple MCP servers
|
|
@@ -94,7 +94,7 @@ The returned tools can be used with LangChain, e.g.:
|
|
|
94
94
|
|
|
95
95
|
```ts
|
|
96
96
|
// import { ChatAnthropic } from "@langchain/anthropic";
|
|
97
|
-
const llm = new ChatAnthropic({ model: "claude-
|
|
97
|
+
const llm = new ChatAnthropic({ model: "claude-sonnet-4-0" });
|
|
98
98
|
|
|
99
99
|
// import { createReactAgent } from "@langchain/langgraph/prebuilt";
|
|
100
100
|
const agent = createReactAgent({
|
|
@@ -109,33 +109,134 @@ try [this LangChain application built with the utility](https://github.com/hidey
|
|
|
109
109
|
For detailed information on how to use this library, please refer to the following document:
|
|
110
110
|
["Supercharging LangChain: Integrating 2000+ MCP with ReAct"](https://medium.com/@h1deya/supercharging-langchain-integrating-450-mcp-with-react-d4e467cbf41a)
|
|
111
111
|
|
|
112
|
-
##
|
|
112
|
+
## Features
|
|
113
|
+
|
|
114
|
+
### `stderr` Redirection for Local MCP Server
|
|
115
|
+
|
|
116
|
+
A new key `"stderr"` has been introduced to specify a file descriptor
|
|
117
|
+
to which local (stdio) MCP server's stderr is redirected.
|
|
118
|
+
The key name `stderr` is derived from
|
|
119
|
+
TypeScript SDK's [`StdioServerParameters`](https://github.com/modelcontextprotocol/typescript-sdk/blob/131776764536b5fdca642df51230a3746fb4ade0/src/client/stdio.ts#L32).
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
const logPath = `mcp-server-${serverName}.log`;
|
|
123
|
+
const logFd = fs.openSync(logPath, "w");
|
|
124
|
+
mcpServers[serverName].stderr = logFd;
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
A usage example can be found [here](
|
|
128
|
+
https://github.com/hideya/langchain-mcp-tools-ts-usage/blob/694b877ed5336bfcd5274d95d3f6d14bed0937a6/src/index.ts#L72-L83)
|
|
129
|
+
|
|
130
|
+
### Working Directory Configuration for Local MCP Servers
|
|
131
|
+
|
|
132
|
+
The working directory that is used when spawning a local (stdio) MCP server
|
|
133
|
+
can be specified with the `"cwd"` key as follows:
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
"local-server-name": {
|
|
137
|
+
command: "...",
|
|
138
|
+
args: [...],
|
|
139
|
+
cwd: "/working/directory" // the working dir to be use by the server
|
|
140
|
+
},
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The key name `cwd` is derived from
|
|
144
|
+
TypeScript SDK's [`StdioServerParameters`](https://github.com/modelcontextprotocol/typescript-sdk/blob/131776764536b5fdca642df51230a3746fb4ade0/src/client/stdio.ts#L39).
|
|
145
|
+
|
|
113
146
|
|
|
114
147
|
### Remote MCP Server Support
|
|
115
148
|
|
|
116
|
-
`mcp_servers` configuration for SSE and Websocket servers are as follows:
|
|
149
|
+
`mcp_servers` configuration for Streamable HTTP, SSE and Websocket servers are as follows:
|
|
117
150
|
|
|
118
151
|
```ts
|
|
152
|
+
// Auto-detection: tries Streamable HTTP first, falls back to SSE on 4xx errors
|
|
153
|
+
"auto-detect-server": {
|
|
154
|
+
url: `http://${server_host}:${server_port}/...`
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
// Explicit Streamable HTTP
|
|
158
|
+
"streamable-http-server": {
|
|
159
|
+
url: `http://${server_host}:${server_port}/...`,
|
|
160
|
+
transport: "streamable_http"
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
// Explicit SSE
|
|
119
164
|
"sse-server-name": {
|
|
120
|
-
url: `http://${sse_server_host}:${sse_server_port}
|
|
165
|
+
url: `http://${sse_server_host}:${sse_server_port}/...`,
|
|
166
|
+
transport: "sse"
|
|
121
167
|
},
|
|
122
168
|
|
|
169
|
+
// WebSocket
|
|
123
170
|
"ws-server-name": {
|
|
124
171
|
url: `ws://${ws_server_host}:${ws_server_port}/...`
|
|
125
172
|
},
|
|
126
173
|
```
|
|
127
174
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
175
|
+
**Auto-detection behavior (default):**
|
|
176
|
+
- 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
|
+
- First attempts Streamable HTTP transport
|
|
178
|
+
- If Streamable HTTP fails with a 4xx error, automatically falls back to SSE transport
|
|
179
|
+
- Non-4xx errors (network issues, etc.) are re-thrown without fallback
|
|
131
180
|
|
|
132
|
-
|
|
133
|
-
|
|
181
|
+
**Explicit transport selection:**
|
|
182
|
+
- Set `transport: "streamable_http"` to force Streamable HTTP (no fallback)
|
|
183
|
+
- Set `transport: "sse"` to force SSE transport
|
|
184
|
+
- WebSocket URLs (`ws://` or `wss://`) always use WebSocket transport
|
|
185
|
+
|
|
186
|
+
Streamable HTTP is the modern MCP transport that replaces the older HTTP+SSE transport. According to the [official MCP documentation](https://modelcontextprotocol.io/docs/concepts/transports):
|
|
187
|
+
|
|
188
|
+
> "SSE as a standalone transport is deprecated as of protocol version 2024-11-05. It has been replaced by Streamable HTTP, which incorporates SSE as an optional streaming mechanism."
|
|
189
|
+
|
|
190
|
+
Note that even when you specify the Streamable HTTP transport, you may see SSE activity in the logs, such as `Accept: text/event-stream`.
|
|
191
|
+
This occurs when the MCP SDK chooses to use SSE for streaming server responses within the Streamable HTTP transport.
|
|
134
192
|
|
|
135
|
-
### Authentication Support for
|
|
193
|
+
### Authentication Support for Streamable HTTP Connections
|
|
136
194
|
|
|
137
|
-
The library
|
|
138
|
-
|
|
195
|
+
The library supports OAuth 2.1 authentication for Streamable HTTP connections:
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js';
|
|
199
|
+
|
|
200
|
+
// Implement your own OAuth client provider
|
|
201
|
+
class MyOAuthProvider implements OAuthClientProvider {
|
|
202
|
+
// Implementation details...
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const mcpServers = {
|
|
206
|
+
"secure-streamable-server": {
|
|
207
|
+
url: "https://secure-mcp-server.example.com/mcp",
|
|
208
|
+
transport: "streamable_http", // Optional: explicit transport
|
|
209
|
+
streamableHTTPOptions: {
|
|
210
|
+
// Provide an OAuth client provider
|
|
211
|
+
authProvider: new MyOAuthProvider(),
|
|
212
|
+
|
|
213
|
+
// Optionally customize HTTP requests
|
|
214
|
+
requestInit: {
|
|
215
|
+
headers: {
|
|
216
|
+
'X-Custom-Header': 'custom-value'
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
// Optionally configure reconnection behavior
|
|
221
|
+
reconnectionOptions: {
|
|
222
|
+
maxReconnectAttempts: 5,
|
|
223
|
+
reconnectDelay: 1000
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Test implementations are provided:
|
|
231
|
+
|
|
232
|
+
- **Streamable HTTP Authentication Tests**:
|
|
233
|
+
- MCP client uses this library: [streamable-http-auth-test-client.ts](https://github.com/hideya/langchain-mcp-tools-ts/tree/main/testfiles/streamable-http-auth-test-client.ts)
|
|
234
|
+
- Test MCP Server: [streamable-http-auth-test-server.ts](https://github.com/hideya/langchain-mcp-tools-ts/tree/main/testfiles/streamable-http-auth-test-server.ts)
|
|
235
|
+
|
|
236
|
+
### Authentication Support for SSE Connections (Legacy)
|
|
237
|
+
|
|
238
|
+
The library also supports authentication for SSE connections to MCP servers.
|
|
239
|
+
Note that SSE transport is deprecated; Streamable HTTP is the recommended approach.
|
|
139
240
|
|
|
140
241
|
To enable authentication, provide SSE options in your server configuration:
|
|
141
242
|
|
|
@@ -170,47 +271,11 @@ const mcpServers = {
|
|
|
170
271
|
};
|
|
171
272
|
```
|
|
172
273
|
|
|
173
|
-
|
|
174
|
-
in [sse-auth-test-client.ts](https://github.com/hideya/langchain-mcp-tools-ts-usage/tree/main/src/sse-auth-test-client.ts)
|
|
175
|
-
of [this usage examples repo](https://github.com/hideya/langchain-mcp-tools-ts-usage).
|
|
176
|
-
|
|
177
|
-
For testing purposes, a sample MCP server with OAuth authentication support
|
|
178
|
-
that works with the above client is provided
|
|
179
|
-
in [sse-auth-test-server.ts](https://github.com/hideya/langchain-mcp-tools-ts-usage/tree/main/src/sse-auth-test-server.ts)
|
|
180
|
-
of [this usage examples repo](https://github.com/hideya/langchain-mcp-tools-ts-usage).
|
|
181
|
-
|
|
182
|
-
### Working Directory Configuration for Local MCP Servers
|
|
183
|
-
|
|
184
|
-
The working directory that is used when spawning a local (stdio) MCP server
|
|
185
|
-
can be specified with the `"cwd"` key as follows:
|
|
186
|
-
|
|
187
|
-
```ts
|
|
188
|
-
"local-server-name": {
|
|
189
|
-
command: "...",
|
|
190
|
-
args: [...],
|
|
191
|
-
cwd: "/working/directory" // the working dir to be use by the server
|
|
192
|
-
},
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
The key name `cwd` is derived from
|
|
196
|
-
TypeScript SDK's [`StdioServerParameters`](https://github.com/modelcontextprotocol/typescript-sdk/blob/131776764536b5fdca642df51230a3746fb4ade0/src/client/stdio.ts#L39).
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
### `stderr` Redirection for Local MCP Server
|
|
200
|
-
|
|
201
|
-
A new key `"stderr"` has been introduced to specify a file descriptor
|
|
202
|
-
to which local (stdio) MCP server's stderr is redirected.
|
|
203
|
-
The key name `stderr` is derived from
|
|
204
|
-
TypeScript SDK's [`StdioServerParameters`](https://github.com/modelcontextprotocol/typescript-sdk/blob/131776764536b5fdca642df51230a3746fb4ade0/src/client/stdio.ts#L32).
|
|
274
|
+
Test implementations are provided:
|
|
205
275
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
mcpServers[serverName].stderr = logFd;
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
A usage example can be found [here](
|
|
213
|
-
https://github.com/hideya/langchain-mcp-tools-ts-usage/blob/694b877ed5336bfcd5274d95d3f6d14bed0937a6/src/index.ts#L72-L83)
|
|
276
|
+
- **SSE Authentication Tests**:
|
|
277
|
+
- MCP client uses this library: [sse-auth-test-client.ts](https://github.com/hideya/langchain-mcp-tools-ts/tree/main/testfiles/sse-auth-test-client.ts)
|
|
278
|
+
- Test MCP Server: [sse-auth-test-server.ts](https://github.com/hideya/langchain-mcp-tools-ts/tree/main/testfiles/sse-auth-test-server.ts)
|
|
214
279
|
|
|
215
280
|
## Limitations
|
|
216
281
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { IOType } from "node:child_process";
|
|
2
2
|
import { Stream } from "node:stream";
|
|
3
3
|
import { StructuredTool } from "@langchain/core/tools";
|
|
4
|
+
import { StreamableHTTPReconnectionOptions } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
4
5
|
import { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
5
6
|
/**
|
|
6
7
|
* Configuration for a command-line based MCP server.
|
|
@@ -10,6 +11,7 @@ import { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
|
10
11
|
*/
|
|
11
12
|
export interface CommandBasedConfig {
|
|
12
13
|
url?: never;
|
|
14
|
+
transport?: never;
|
|
13
15
|
command: string;
|
|
14
16
|
args?: string[];
|
|
15
17
|
env?: Record<string, string>;
|
|
@@ -24,6 +26,7 @@ export interface CommandBasedConfig {
|
|
|
24
26
|
*/
|
|
25
27
|
export interface UrlBasedConfig {
|
|
26
28
|
url: string;
|
|
29
|
+
transport?: string;
|
|
27
30
|
command?: never;
|
|
28
31
|
args?: never;
|
|
29
32
|
env?: never;
|
|
@@ -34,6 +37,12 @@ export interface UrlBasedConfig {
|
|
|
34
37
|
eventSourceInit?: EventSourceInit;
|
|
35
38
|
requestInit?: RequestInit;
|
|
36
39
|
};
|
|
40
|
+
streamableHTTPOptions?: {
|
|
41
|
+
authProvider?: OAuthClientProvider;
|
|
42
|
+
requestInit?: RequestInit;
|
|
43
|
+
reconnectionOptions?: StreamableHTTPReconnectionOptions;
|
|
44
|
+
sessionId?: string;
|
|
45
|
+
};
|
|
37
46
|
}
|
|
38
47
|
/**
|
|
39
48
|
* Configuration for an MCP server.
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { DynamicStructuredTool } from "@langchain/core/tools";
|
|
2
2
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
3
3
|
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
4
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
4
5
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
5
6
|
import { WebSocketClientTransport } from "@modelcontextprotocol/sdk/client/websocket.js";
|
|
6
7
|
import { CallToolResultSchema, ListToolsResultSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
7
8
|
import { jsonSchemaToZod } from "@n8n/json-schema-to-zod";
|
|
9
|
+
import { z } from "zod";
|
|
8
10
|
import { Logger } from "./logger.js";
|
|
9
11
|
// Custom error type for MCP server initialization failures
|
|
10
12
|
/**
|
|
@@ -82,6 +84,278 @@ export async function convertMcpToLangchainTools(configs, options) {
|
|
|
82
84
|
allTools.forEach((tool) => logger.debug(`- ${tool.name}`));
|
|
83
85
|
return { tools: allTools, cleanup };
|
|
84
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Sanitizes a JSON Schema to make it compatible with Google Gemini API.
|
|
89
|
+
*
|
|
90
|
+
* ⚠️ IMPORTANT: This is a temporary workaround to keep applications running.
|
|
91
|
+
* The underlying schema compatibility issues should be fixed on the MCP server side
|
|
92
|
+
* for proper Google LLM compatibility. This function will log warnings when
|
|
93
|
+
* performing schema conversions to help track which servers need upstream fixes.
|
|
94
|
+
*
|
|
95
|
+
* Gemini supports only a limited subset of OpenAPI 3.0 Schema properties:
|
|
96
|
+
* - string: enum, format (only 'date-time' documented)
|
|
97
|
+
* - integer/number: format only
|
|
98
|
+
* - array: minItems, maxItems, items
|
|
99
|
+
* - object: properties, required, propertyOrdering, nullable
|
|
100
|
+
*
|
|
101
|
+
* This function removes known problematic properties while preserving
|
|
102
|
+
* as much validation as possible. When debug logging is enabled,
|
|
103
|
+
* it reports what schema changes were made for transparency.
|
|
104
|
+
*
|
|
105
|
+
* Reference: https://ai.google.dev/gemini-api/docs/structured-output#json-schemas
|
|
106
|
+
*
|
|
107
|
+
* @param schema - The JSON schema to sanitize
|
|
108
|
+
* @param logger - Optional logger for reporting sanitization actions
|
|
109
|
+
* @param toolName - Optional tool name for logging context
|
|
110
|
+
* @returns A sanitized schema compatible with all major LLM providers
|
|
111
|
+
*
|
|
112
|
+
* @internal This function is meant to be used internally by convertSingleMcpToLangchainTools
|
|
113
|
+
*/
|
|
114
|
+
function sanitizeSchemaForGemini(schema, logger, toolName) {
|
|
115
|
+
if (typeof schema !== 'object' || schema === null) {
|
|
116
|
+
return schema;
|
|
117
|
+
}
|
|
118
|
+
const sanitized = { ...schema };
|
|
119
|
+
const removedProperties = [];
|
|
120
|
+
const convertedProperties = [];
|
|
121
|
+
// Remove unsupported properties
|
|
122
|
+
if (sanitized.exclusiveMinimum !== undefined) {
|
|
123
|
+
removedProperties.push('exclusiveMinimum');
|
|
124
|
+
delete sanitized.exclusiveMinimum;
|
|
125
|
+
}
|
|
126
|
+
if (sanitized.exclusiveMaximum !== undefined) {
|
|
127
|
+
removedProperties.push('exclusiveMaximum');
|
|
128
|
+
delete sanitized.exclusiveMaximum;
|
|
129
|
+
}
|
|
130
|
+
// Convert exclusiveMinimum/Maximum to minimum/maximum if needed
|
|
131
|
+
if (schema.exclusiveMinimum !== undefined) {
|
|
132
|
+
sanitized.minimum = schema.exclusiveMinimum;
|
|
133
|
+
convertedProperties.push('exclusiveMinimum → minimum');
|
|
134
|
+
}
|
|
135
|
+
if (schema.exclusiveMaximum !== undefined) {
|
|
136
|
+
sanitized.maximum = schema.exclusiveMaximum;
|
|
137
|
+
convertedProperties.push('exclusiveMaximum → maximum');
|
|
138
|
+
}
|
|
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
|
+
if (!supportedFormats.includes(sanitized.format)) {
|
|
143
|
+
removedProperties.push(`format: ${sanitized.format}`);
|
|
144
|
+
delete sanitized.format;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Log sanitization actions for this level
|
|
148
|
+
if (logger && toolName && (removedProperties.length > 0 || convertedProperties.length > 0)) {
|
|
149
|
+
const changes = [];
|
|
150
|
+
if (removedProperties.length > 0) {
|
|
151
|
+
changes.push(`removed: ${removedProperties.join(', ')}`);
|
|
152
|
+
}
|
|
153
|
+
if (convertedProperties.length > 0) {
|
|
154
|
+
changes.push(`converted: ${convertedProperties.join(', ')}`);
|
|
155
|
+
}
|
|
156
|
+
logger.warn(`MCP tool "${toolName}": schema sanitized for Gemini compatibility (${changes.join('; ')})`);
|
|
157
|
+
}
|
|
158
|
+
// Recursively process nested objects and arrays
|
|
159
|
+
if (sanitized.properties) {
|
|
160
|
+
sanitized.properties = Object.fromEntries(Object.entries(sanitized.properties).map(([key, value]) => [
|
|
161
|
+
key,
|
|
162
|
+
sanitizeSchemaForGemini(value, logger, toolName)
|
|
163
|
+
]));
|
|
164
|
+
}
|
|
165
|
+
if (sanitized.anyOf) {
|
|
166
|
+
sanitized.anyOf = sanitized.anyOf.map((subSchema) => sanitizeSchemaForGemini(subSchema, logger, toolName));
|
|
167
|
+
}
|
|
168
|
+
if (sanitized.oneOf) {
|
|
169
|
+
sanitized.oneOf = sanitized.oneOf.map((subSchema) => sanitizeSchemaForGemini(subSchema, logger, toolName));
|
|
170
|
+
}
|
|
171
|
+
if (sanitized.allOf) {
|
|
172
|
+
sanitized.allOf = sanitized.allOf.map((subSchema) => sanitizeSchemaForGemini(subSchema, logger, toolName));
|
|
173
|
+
}
|
|
174
|
+
if (sanitized.items) {
|
|
175
|
+
sanitized.items = sanitizeSchemaForGemini(sanitized.items, logger, toolName);
|
|
176
|
+
}
|
|
177
|
+
if (sanitized.additionalProperties && typeof sanitized.additionalProperties === 'object') {
|
|
178
|
+
sanitized.additionalProperties = sanitizeSchemaForGemini(sanitized.additionalProperties, logger, toolName);
|
|
179
|
+
}
|
|
180
|
+
return sanitized;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Creates Streamable HTTP transport options from configuration.
|
|
184
|
+
* Consolidates repeated option configuration logic into a single reusable function.
|
|
185
|
+
*
|
|
186
|
+
* @param config - URL-based server configuration
|
|
187
|
+
* @param logger - Logger instance for recording authentication setup
|
|
188
|
+
* @param serverName - Server name for logging context
|
|
189
|
+
* @returns Configured StreamableHTTPClientTransportOptions or undefined if no options needed
|
|
190
|
+
*
|
|
191
|
+
* @internal This function is meant to be used internally by transport creation functions
|
|
192
|
+
*/
|
|
193
|
+
function createStreamableHttpOptions(config, logger, serverName) {
|
|
194
|
+
const options = {};
|
|
195
|
+
if (config.streamableHTTPOptions) {
|
|
196
|
+
if (config.streamableHTTPOptions.authProvider) {
|
|
197
|
+
options.authProvider = config.streamableHTTPOptions.authProvider;
|
|
198
|
+
logger.info(`MCP server "${serverName}": configuring Streamable HTTP with authentication provider`);
|
|
199
|
+
}
|
|
200
|
+
if (config.streamableHTTPOptions.requestInit) {
|
|
201
|
+
options.requestInit = config.streamableHTTPOptions.requestInit;
|
|
202
|
+
}
|
|
203
|
+
if (config.streamableHTTPOptions.reconnectionOptions) {
|
|
204
|
+
options.reconnectionOptions = config.streamableHTTPOptions.reconnectionOptions;
|
|
205
|
+
}
|
|
206
|
+
if (config.streamableHTTPOptions.sessionId) {
|
|
207
|
+
options.sessionId = config.streamableHTTPOptions.sessionId;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return Object.keys(options).length > 0 ? options : undefined;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Creates SSE transport options from configuration.
|
|
214
|
+
* Consolidates repeated option configuration logic into a single reusable function.
|
|
215
|
+
*
|
|
216
|
+
* @param config - URL-based server configuration
|
|
217
|
+
* @param logger - Logger instance for recording authentication setup
|
|
218
|
+
* @param serverName - Server name for logging context
|
|
219
|
+
* @returns Configured SSEClientTransportOptions or undefined if no options needed
|
|
220
|
+
*
|
|
221
|
+
* @internal This function is meant to be used internally by transport creation functions
|
|
222
|
+
*/
|
|
223
|
+
function createSseOptions(config, logger, serverName) {
|
|
224
|
+
const options = {};
|
|
225
|
+
if (config.sseOptions) {
|
|
226
|
+
if (config.sseOptions.authProvider) {
|
|
227
|
+
options.authProvider = config.sseOptions.authProvider;
|
|
228
|
+
logger.info(`MCP server "${serverName}": configuring SSE with authentication provider`);
|
|
229
|
+
}
|
|
230
|
+
if (config.sseOptions.eventSourceInit) {
|
|
231
|
+
options.eventSourceInit = config.sseOptions.eventSourceInit;
|
|
232
|
+
}
|
|
233
|
+
if (config.sseOptions.requestInit) {
|
|
234
|
+
options.requestInit = config.sseOptions.requestInit;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return Object.keys(options).length > 0 ? options : undefined;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Determines if an error represents a 4xx HTTP status code.
|
|
241
|
+
* Used to decide whether to fall back from Streamable HTTP to SSE transport.
|
|
242
|
+
*
|
|
243
|
+
* @param error - The error to check
|
|
244
|
+
* @returns true if the error represents a 4xx HTTP status
|
|
245
|
+
*
|
|
246
|
+
* @internal This function is meant to be used internally by createHttpTransportWithFallback
|
|
247
|
+
*/
|
|
248
|
+
function is4xxError(error) {
|
|
249
|
+
if (!error || typeof error !== 'object') {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
// Check for common error patterns that indicate 4xx responses
|
|
253
|
+
const errorObj = error;
|
|
254
|
+
// Check if it's a fetch Response error with status
|
|
255
|
+
if (errorObj.status && typeof errorObj.status === 'number') {
|
|
256
|
+
return errorObj.status >= 400 && errorObj.status < 500;
|
|
257
|
+
}
|
|
258
|
+
// Check if it's wrapped in a Response object
|
|
259
|
+
if (errorObj.response && errorObj.response.status && typeof errorObj.response.status === 'number') {
|
|
260
|
+
return errorObj.response.status >= 400 && errorObj.response.status < 500;
|
|
261
|
+
}
|
|
262
|
+
// Check for error messages that typically indicate 4xx errors
|
|
263
|
+
const message = errorObj.message || errorObj.toString();
|
|
264
|
+
if (typeof message === 'string') {
|
|
265
|
+
return /4[0-9]{2}/.test(message) ||
|
|
266
|
+
message.includes('Bad Request') ||
|
|
267
|
+
message.includes('Unauthorized') ||
|
|
268
|
+
message.includes('Forbidden') ||
|
|
269
|
+
message.includes('Not Found') ||
|
|
270
|
+
message.includes('Method Not Allowed');
|
|
271
|
+
}
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Creates an HTTP transport with automatic fallback from Streamable HTTP to SSE.
|
|
276
|
+
* Follows the MCP specification recommendation to try Streamable HTTP first,
|
|
277
|
+
* then fall back to SSE if a 4xx error is encountered.
|
|
278
|
+
*
|
|
279
|
+
* See: https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#backwards-compatibility
|
|
280
|
+
*
|
|
281
|
+
* @param url - The URL to connect to
|
|
282
|
+
* @param config - URL-based server configuration
|
|
283
|
+
* @param logger - Logger instance for recording connection attempts
|
|
284
|
+
* @param serverName - Server name for logging context
|
|
285
|
+
* @returns A promise that resolves to a configured Transport
|
|
286
|
+
*
|
|
287
|
+
* @internal This function is meant to be used internally by convertSingleMcpToLangchainTools
|
|
288
|
+
*/
|
|
289
|
+
async function createHttpTransportWithFallback(url, config, logger, serverName) {
|
|
290
|
+
// If transport is explicitly specified, respect user's choice
|
|
291
|
+
if (config.transport === "streamable_http") {
|
|
292
|
+
logger.debug(`MCP server "${serverName}": using explicitly configured Streamable HTTP transport`);
|
|
293
|
+
const options = createStreamableHttpOptions(config, logger, serverName);
|
|
294
|
+
return new StreamableHTTPClientTransport(url, options);
|
|
295
|
+
}
|
|
296
|
+
if (config.transport === "sse") {
|
|
297
|
+
logger.debug(`MCP server "${serverName}": using explicitly configured SSE transport`);
|
|
298
|
+
const options = createSseOptions(config, logger, serverName);
|
|
299
|
+
return new SSEClientTransport(url, options);
|
|
300
|
+
}
|
|
301
|
+
// Auto-detection: try Streamable HTTP first, fall back to SSE on 4xx errors
|
|
302
|
+
logger.debug(`MCP server "${serverName}": attempting Streamable HTTP transport with SSE fallback`);
|
|
303
|
+
try {
|
|
304
|
+
const options = createStreamableHttpOptions(config, logger, serverName);
|
|
305
|
+
const transport = new StreamableHTTPClientTransport(url, options);
|
|
306
|
+
logger.info(`MCP server "${serverName}": successfully created Streamable HTTP transport`);
|
|
307
|
+
return transport;
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
if (is4xxError(error)) {
|
|
311
|
+
logger.info(`MCP server "${serverName}": Streamable HTTP failed with 4xx error, falling back to SSE transport`);
|
|
312
|
+
const options = createSseOptions(config, logger, serverName);
|
|
313
|
+
return new SSEClientTransport(url, options);
|
|
314
|
+
}
|
|
315
|
+
// Re-throw non-4xx errors (network issues, etc.)
|
|
316
|
+
logger.error(`MCP server "${serverName}": Streamable HTTP transport creation failed with non-4xx error:`, error);
|
|
317
|
+
throw error;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Transforms a Zod schema to be compatible with OpenAI's Structured Outputs requirements.
|
|
322
|
+
*
|
|
323
|
+
* OpenAI's Structured Outputs feature requires that all optional fields must also be nullable.
|
|
324
|
+
* This function converts Zod schemas that use `.optional()` or `.default()` to also include
|
|
325
|
+
* `.nullable()`, ensuring compatibility with OpenAI models while maintaining compatibility
|
|
326
|
+
* with other LLM providers like Anthropic.
|
|
327
|
+
* See: https://platform.openai.com/docs/guides/structured-outputs?api-mode=responses#all-fields-must-be-required
|
|
328
|
+
*
|
|
329
|
+
* @param schema - The Zod object schema to transform
|
|
330
|
+
* @returns A new Zod schema with optional/default fields made nullable
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* // Input schema: z.object({ name: z.string(), age: z.number().optional() })
|
|
334
|
+
* // Output schema: z.object({ name: z.string(), age: z.number().optional().nullable() })
|
|
335
|
+
*
|
|
336
|
+
* @see {@link https://platform.openai.com/docs/guides/structured-outputs | OpenAI Structured Outputs Documentation}
|
|
337
|
+
*
|
|
338
|
+
* @internal This function is meant to be used internally by convertSingleMcpToLangchainTools
|
|
339
|
+
*/
|
|
340
|
+
function makeZodSchemaOpenAICompatible(schema) {
|
|
341
|
+
const shape = schema.shape;
|
|
342
|
+
const newShape = {};
|
|
343
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
344
|
+
if (value instanceof z.ZodOptional && !(value instanceof z.ZodNullable)) {
|
|
345
|
+
// Convert .optional() to .optional().nullable() for OpenAI compatibility
|
|
346
|
+
newShape[key] = value.nullable();
|
|
347
|
+
}
|
|
348
|
+
else if (value instanceof z.ZodDefault && !(value instanceof z.ZodNullable)) {
|
|
349
|
+
// Convert .default() to .default().nullable() for OpenAI compatibility
|
|
350
|
+
newShape[key] = value.nullable();
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
// Keep existing fields unchanged (including already nullable fields)
|
|
354
|
+
newShape[key] = value;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return z.object(newShape);
|
|
358
|
+
}
|
|
85
359
|
/**
|
|
86
360
|
* Initializes a single MCP server and converts its capabilities into LangChain tools.
|
|
87
361
|
* Sets up a connection to the server, retrieves available tools, and creates corresponding
|
|
@@ -113,22 +387,67 @@ async function convertSingleMcpToLangchainTools(serverName, config, logger) {
|
|
|
113
387
|
// Ignore
|
|
114
388
|
}
|
|
115
389
|
if (url?.protocol === "http:" || url?.protocol === "https:") {
|
|
116
|
-
//
|
|
390
|
+
// Use the new auto-detection logic with fallback
|
|
117
391
|
const urlConfig = config;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
392
|
+
// Try to connect with Streamable HTTP first, fallback to SSE on 4xx errors
|
|
393
|
+
let connectionSucceeded = false;
|
|
394
|
+
// If transport is explicitly specified, respect user's choice (no fallback)
|
|
395
|
+
if (urlConfig.transport === "streamable_http" || urlConfig.transport === "sse") {
|
|
396
|
+
transport = await createHttpTransportWithFallback(url, urlConfig, logger, serverName);
|
|
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`);
|
|
126
416
|
}
|
|
127
|
-
|
|
128
|
-
|
|
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
|
+
}
|
|
129
449
|
}
|
|
130
450
|
}
|
|
131
|
-
transport = new SSEClientTransport(url, Object.keys(sseOptions).length > 0 ? sseOptions : undefined);
|
|
132
451
|
}
|
|
133
452
|
else if (url?.protocol === "ws:" || url?.protocol === "wss:") {
|
|
134
453
|
transport = new WebSocketClientTransport(url);
|
|
@@ -150,57 +469,67 @@ async function convertSingleMcpToLangchainTools(serverName, config, logger) {
|
|
|
150
469
|
cwd: stdioServerConfig.cwd
|
|
151
470
|
});
|
|
152
471
|
}
|
|
153
|
-
client
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
472
|
+
// Only create client if not already created during auto-detection fallback
|
|
473
|
+
if (!client) {
|
|
474
|
+
client = new Client({
|
|
475
|
+
name: "mcp-client",
|
|
476
|
+
version: "0.0.1",
|
|
477
|
+
}, {
|
|
478
|
+
capabilities: {},
|
|
479
|
+
});
|
|
480
|
+
await client.connect(transport);
|
|
481
|
+
logger.info(`MCP server "${serverName}": connected`);
|
|
482
|
+
}
|
|
161
483
|
const toolsResponse = await client.request({ method: "tools/list" }, ListToolsResultSchema);
|
|
162
|
-
const tools = toolsResponse.tools.map((tool) =>
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
//
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
484
|
+
const tools = toolsResponse.tools.map((tool) => {
|
|
485
|
+
// Apply sanitization for all LLMs (harmless for non-Gemini providers)
|
|
486
|
+
const sanitizedSchema = sanitizeSchemaForGemini(tool.inputSchema, logger, `${serverName}/${tool.name}`);
|
|
487
|
+
const baseSchema = jsonSchemaToZod(sanitizedSchema);
|
|
488
|
+
// Transforms a Zod schema to be compatible with OpenAI's Structured Outputs requirements.
|
|
489
|
+
const compatibleSchema = makeZodSchemaOpenAICompatible(baseSchema);
|
|
490
|
+
return new DynamicStructuredTool({
|
|
491
|
+
name: tool.name,
|
|
492
|
+
description: tool.description || "",
|
|
493
|
+
// FIXME
|
|
494
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
495
|
+
schema: compatibleSchema,
|
|
496
|
+
func: async function (input) {
|
|
497
|
+
logger.info(`MCP tool "${serverName}"/"${tool.name}" received input:`, input);
|
|
498
|
+
try {
|
|
499
|
+
// Execute tool call
|
|
500
|
+
const result = await client?.request({
|
|
501
|
+
method: "tools/call",
|
|
502
|
+
params: {
|
|
503
|
+
name: tool.name,
|
|
504
|
+
arguments: input,
|
|
505
|
+
},
|
|
506
|
+
}, CallToolResultSchema);
|
|
507
|
+
// Handles null/undefined cases gracefully
|
|
508
|
+
if (!result?.content) {
|
|
509
|
+
logger.info(`MCP tool "${serverName}"/"${tool.name}" received null/undefined result`);
|
|
510
|
+
return "";
|
|
511
|
+
}
|
|
512
|
+
const textContent = result.content
|
|
513
|
+
.filter(content => content.type === "text")
|
|
514
|
+
.map(content => content.text)
|
|
515
|
+
.join("\n\n");
|
|
516
|
+
// const textItems = result.content
|
|
517
|
+
// .filter(content => content.type === "text")
|
|
518
|
+
// .map(content => content.text)
|
|
519
|
+
// const textContent = JSON.stringify(textItems);
|
|
520
|
+
// Log rough result size for monitoring
|
|
521
|
+
const size = new TextEncoder().encode(textContent).length;
|
|
522
|
+
logger.info(`MCP tool "${serverName}"/"${tool.name}" received result (size: ${size})`);
|
|
523
|
+
// If no text content, return a clear message describing the situation
|
|
524
|
+
return textContent || "No text content available in response";
|
|
183
525
|
}
|
|
184
|
-
|
|
185
|
-
.
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
// const textContent = JSON.stringify(textItems);
|
|
192
|
-
// Log rough result size for monitoring
|
|
193
|
-
const size = new TextEncoder().encode(textContent).length;
|
|
194
|
-
logger.info(`MCP tool "${serverName}"/"${tool.name}" received result (size: ${size})`);
|
|
195
|
-
// If no text content, return a clear message describing the situation
|
|
196
|
-
return textContent || "No text content available in response";
|
|
197
|
-
}
|
|
198
|
-
catch (error) {
|
|
199
|
-
logger.warn(`MCP tool "${serverName}"/"${tool.name}" caused error: ${error}`);
|
|
200
|
-
return `Error executing MCP tool: ${error}`;
|
|
201
|
-
}
|
|
202
|
-
},
|
|
203
|
-
})));
|
|
526
|
+
catch (error) {
|
|
527
|
+
logger.warn(`MCP tool "${serverName}"/"${tool.name}" caused error: ${error}`);
|
|
528
|
+
return `Error executing MCP tool: ${error}`;
|
|
529
|
+
}
|
|
530
|
+
},
|
|
531
|
+
});
|
|
532
|
+
});
|
|
204
533
|
logger.info(`MCP server "${serverName}": ${tools.length} tool(s) available:`);
|
|
205
534
|
tools.forEach((tool) => logger.info(`- ${tool.name}`));
|
|
206
535
|
async function cleanup() {
|
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.6",
|
|
4
4
|
"description": "MCP To LangChain Tools Conversion Utility",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -30,22 +30,39 @@
|
|
|
30
30
|
"url": "git+https://github.com/hideya/langchain-mcp-tools-ts.git"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
|
+
"_comment_build": "# Build and development scripts",
|
|
33
34
|
"build": "tsc",
|
|
34
35
|
"prepare": "npm run build",
|
|
35
36
|
"watch": "tsc --watch",
|
|
36
|
-
"simple-usage": "tsx testfiles/simple-usage.ts",
|
|
37
|
-
"direct-test": "tsx -- testfiles/direct-test.ts",
|
|
38
|
-
"sse-auth-test-client": "tsx testfiles/sse-auth-test-client.ts",
|
|
39
|
-
"sse-auth-test-server": "tsx testfiles/sse-auth-test-server.ts",
|
|
40
37
|
"lint": "eslint src",
|
|
38
|
+
"clean": "git clean -fdxn -e .env && read -p 'OK?' && git clean -fdx -e .env",
|
|
39
|
+
|
|
40
|
+
"_comment_test": "# Testing scripts",
|
|
41
41
|
"test": "vitest run",
|
|
42
42
|
"test:watch": "vitest",
|
|
43
43
|
"test:coverage": "vitest run --coverage",
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
"
|
|
44
|
+
|
|
45
|
+
"_comment_examples": "# Basic usage examples",
|
|
46
|
+
"example:simple": "tsx testfiles/simple-usage.ts",
|
|
47
|
+
|
|
48
|
+
"_comment_streamable": "# Streamable HTTP transport tests",
|
|
49
|
+
"test:streamable:auth:server": "tsx testfiles/streamable-http-auth-test-server.ts",
|
|
50
|
+
"test:streamable:auth:client": "tsx testfiles/streamable-http-auth-test-client.ts",
|
|
51
|
+
"test:streamable:stateless:server": "tsx testfiles/streamable-http-stateless-test-server.ts",
|
|
52
|
+
"test:streamable:stateless:client": "tsx testfiles/streamable-http-stateless-test-client.ts",
|
|
53
|
+
"test:streamable:auto-detection": "tsx testfiles/streamable-http-auto-detection-test.ts",
|
|
54
|
+
|
|
55
|
+
"_comment_sse": "# SSE transport tests (with authentication)",
|
|
56
|
+
"test:sse:server": "tsx testfiles/sse-auth-test-server.ts",
|
|
57
|
+
"test:sse:client": "tsx testfiles/sse-auth-test-client.ts",
|
|
58
|
+
|
|
59
|
+
"_comment_docs": "# Documentation scripts",
|
|
60
|
+
"docs:build": "npx typedoc --options typedoc.json",
|
|
61
|
+
"docs:deploy": "npm run docs:build && ghp-import -n -p -f docs",
|
|
62
|
+
|
|
63
|
+
"_comment_publish": "# Publishing scripts",
|
|
64
|
+
"publish:test": "npm run clean && npm install && npm publish --access=public --dry-run",
|
|
65
|
+
"publish:do": "npm run clean && npm install && npm publish --access=public"
|
|
49
66
|
},
|
|
50
67
|
"dependencies": {
|
|
51
68
|
"@langchain/core": "^0.3.27",
|
|
@@ -56,6 +73,7 @@
|
|
|
56
73
|
"devDependencies": {
|
|
57
74
|
"@eslint/js": "^9.17.0",
|
|
58
75
|
"@langchain/anthropic": "^0.3.11",
|
|
76
|
+
"@langchain/google-genai": "^0.2.12",
|
|
59
77
|
"@langchain/langgraph": "^0.2.36",
|
|
60
78
|
"@langchain/openai": "^0.3.16",
|
|
61
79
|
"@types/node": "^22.10.5",
|