@h1deya/mcp-client-cli 0.3.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/LICENSE +21 -0
- package/README.md +258 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +233 -0
- package/dist/index.js.map +1 -0
- package/dist/init-chat-model.d.ts +11 -0
- package/dist/init-chat-model.d.ts.map +1 -0
- package/dist/init-chat-model.js +37 -0
- package/dist/init-chat-model.js.map +1 -0
- package/dist/load-config.d.ts +37 -0
- package/dist/load-config.d.ts.map +1 -0
- package/dist/load-config.js +123 -0
- package/dist/load-config.js.map +1 -0
- package/dist/remote-server-utils.d.ts +18 -0
- package/dist/remote-server-utils.d.ts.map +1 -0
- package/dist/remote-server-utils.js +91 -0
- package/dist/remote-server-utils.js.map +1 -0
- package/package.json +79 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 hideya
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# Simple MCP Client to Explore MCP Servers / TypeScript [](https://github.com/hideya/mcp-langchain-client-ts/blob/main/LICENSE) [](https://www.npmjs.com/package/@h1deya/mcp-client-cli)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
**Quickly test and explore MCP servers from the command line!**
|
|
5
|
+
|
|
6
|
+
A simple, text-based CLI client for [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers built with LangChain and TypeScript.
|
|
7
|
+
This tool automatically adjusts the schema for LLM compatibility, which can help some failing MCP servers run successfully.
|
|
8
|
+
Suitable for testing MCP servers, exploring their capabilities, and prototyping integrations.
|
|
9
|
+
|
|
10
|
+
Internally it uses [LangChain ReAct Agent](https://github.com/langchain-ai/react-agent-js) and
|
|
11
|
+
a utility function `convertMcpToLangchainTools()` from
|
|
12
|
+
[`@h1deya/langchain-mcp-tools`](https://www.npmjs.com/package/@h1deya/langchain-mcp-tools).
|
|
13
|
+
This function performs the aforementioned MCP tools schema transformations for LLM compatibility.
|
|
14
|
+
See [this page](https://github.com/hideya/langchain-mcp-tools-ts/blob/main/README.md#llm-provider-schema-compatibility)
|
|
15
|
+
for details.
|
|
16
|
+
|
|
17
|
+
A Python equivalent of this utility is available [here](https://pypi.org/project/mcp-chat/)
|
|
18
|
+
|
|
19
|
+
## Prerequisites
|
|
20
|
+
|
|
21
|
+
- Node.js 18+
|
|
22
|
+
- [optional] [`uv` (`uvx`)](https://docs.astral.sh/uv/getting-started/installation/)
|
|
23
|
+
installed to run Python-based local (stdio) MCP servers
|
|
24
|
+
- LLM API keys from
|
|
25
|
+
[OpenAI](https://platform.openai.com/api-keys),
|
|
26
|
+
[Anthropic](https://console.anthropic.com/settings/keys),
|
|
27
|
+
and/or
|
|
28
|
+
[Google AI Studio (for GenAI/Gemini)](https://aistudio.google.com/apikey)
|
|
29
|
+
as needed
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
- Install `mcp-client-cli` tool.
|
|
34
|
+
This can take up to a few minutes to complete:
|
|
35
|
+
```bash
|
|
36
|
+
npm install -g @h1deya/mcp-client-cli
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- Configure LLM and MCP Servers settings via the configuration file, `llm_mcp_config.json5`
|
|
40
|
+
```bash
|
|
41
|
+
code llm_mcp_config.json5
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The following is a simple configuration for quick testing:
|
|
45
|
+
```json5
|
|
46
|
+
{
|
|
47
|
+
"llm": {
|
|
48
|
+
"model_provider": "openai",
|
|
49
|
+
"model": "gpt-4o-mini",
|
|
50
|
+
// "model_provider": "anthropic",
|
|
51
|
+
// "model": "claude-3-5-haiku-latest",
|
|
52
|
+
// "model_provider": "google_genai",
|
|
53
|
+
// "model": "gemini-2.5-flash",
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
"mcp_servers": {
|
|
57
|
+
"us-weather": { // US weather only
|
|
58
|
+
"command": "npx",
|
|
59
|
+
"args": ["-y", "@h1deya/mcp-server-weather"]
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
"example_queries": [
|
|
64
|
+
"Tell me how LLMs work in a few sentences",
|
|
65
|
+
"Are there any weather alerts in California?",
|
|
66
|
+
],
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
- Set up API keys
|
|
71
|
+
```bash
|
|
72
|
+
echo "ANTHROPIC_API_KEY=sk-ant-...
|
|
73
|
+
OPENAI_API_KEY=sk-proj-...
|
|
74
|
+
GOOGLE_API_KEY=AI..." > .env
|
|
75
|
+
|
|
76
|
+
code .env
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- Run the tool
|
|
80
|
+
```bash
|
|
81
|
+
mcp-client-cli
|
|
82
|
+
```
|
|
83
|
+
By default, it reads the configuration file, `llm_mcp_config.json5`, from the current directory.
|
|
84
|
+
Then, it applies the environment variables specified in the `.env` file,
|
|
85
|
+
as well as the ones that are already defined.
|
|
86
|
+
|
|
87
|
+
## Building from Source
|
|
88
|
+
|
|
89
|
+
See [README_DEV.md](https://github.com/hideya/mcp-client-langchain-ts/blob/main/README_DEV.md) for details.
|
|
90
|
+
|
|
91
|
+
## Features
|
|
92
|
+
|
|
93
|
+
- **Easy setup**: Works out of the box with popular MCP servers
|
|
94
|
+
- **Flexible configuration**: JSON5 config with environment variable support
|
|
95
|
+
- **Multiple LLM providers**: OpenAI, Anthropic, Google (GenAI)
|
|
96
|
+
- **Schema Compatibility Support**: Automatically adjusts tools schema for LLM compatibility, which can help some failing MCP servers run successfully.
|
|
97
|
+
See [this page](https://github.com/hideya/langchain-mcp-tools-ts/blob/main/README.md#llm-provider-schema-compatibility)
|
|
98
|
+
for details.
|
|
99
|
+
- **Command & URL servers**: Support for both local and remote MCP servers
|
|
100
|
+
- **Real-time logging**: Live stdio MCP server logs with customizable log directory
|
|
101
|
+
- **Interactive testing**: Example queries for the convenience of repeated testing
|
|
102
|
+
|
|
103
|
+
## Limitations
|
|
104
|
+
|
|
105
|
+
- **Tool Return Types**: Currently, only text results of tool calls are supported.
|
|
106
|
+
It uses LangChain's `response_format: 'content'` (the default) internally, which only supports text strings.
|
|
107
|
+
While MCP tools can return multiple content types (text, images, etc.), this library currently filters and uses only text content.
|
|
108
|
+
- **MCP Features**: Only MCP [Tools](https://modelcontextprotocol.io/docs/concepts/tools) are supported. Other MCP features like Resources, Prompts, and Sampling are not implemented.
|
|
109
|
+
|
|
110
|
+
## Usage
|
|
111
|
+
|
|
112
|
+
### Basic Usage
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
mcp-client-cli
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
By default, it reads the configuration file, `llm_mcp_config.json5`, from the current directory.
|
|
119
|
+
Then, it applies the environment variables specified in the `.env` file,
|
|
120
|
+
as well as the ones that are already defined.
|
|
121
|
+
It outputs local MCP server logs to the current directory.
|
|
122
|
+
|
|
123
|
+
### With Options
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Specify the config file to use
|
|
127
|
+
mcp-client-cli --config my-config.json5
|
|
128
|
+
|
|
129
|
+
# Store local (stdio) MCP server logs in specific directory
|
|
130
|
+
mcp-client-cli --log-dir ./logs
|
|
131
|
+
|
|
132
|
+
# Enable verbose logging
|
|
133
|
+
mcp-client-cli --verbose
|
|
134
|
+
|
|
135
|
+
# Show help
|
|
136
|
+
mcp-client-cli --help
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Supported LLM Providers
|
|
140
|
+
|
|
141
|
+
- **OpenAI**: `o4-mini`, `gpt-4o-mini`, etc.
|
|
142
|
+
- **Anthropic**: `claude-sonnet-4-0`, `claude-3-5-haiku-latest`, etc.
|
|
143
|
+
- **Google (GenAI)**: `gemini-2.5-pro`, `gemini-2.5-flash`, etc.
|
|
144
|
+
|
|
145
|
+
## Configuration
|
|
146
|
+
|
|
147
|
+
Create a `llm_mcp_config.json5` file:
|
|
148
|
+
|
|
149
|
+
- [The configuration file format](https://github.com/hideya/mcp-client-langchain-ts/blob/main/llm_mcp_config.json5)
|
|
150
|
+
for MCP servers follows the same structure as
|
|
151
|
+
[Claude for Desktop](https://modelcontextprotocol.io/quickstart/user),
|
|
152
|
+
with one difference: the key name `mcpServers` has been changed
|
|
153
|
+
to `mcp_servers` to follow the snake_case convention
|
|
154
|
+
commonly used in JSON configuration files.
|
|
155
|
+
- The file format is [JSON5](https://json5.org/),
|
|
156
|
+
where comments and trailing commas are allowed.
|
|
157
|
+
- The format is further extended to replace `${...}` notations
|
|
158
|
+
with the values of corresponding environment variables.
|
|
159
|
+
- Keep all the credentials and private info in the `.env` file
|
|
160
|
+
and refer to them with `${...}` notation as needed
|
|
161
|
+
|
|
162
|
+
```json5
|
|
163
|
+
{
|
|
164
|
+
"llm": {
|
|
165
|
+
"model_provider": "openai",
|
|
166
|
+
"model": "gpt-4.1-nano",
|
|
167
|
+
// model: "o4-mini",
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
// "llm": {
|
|
171
|
+
// "model_provider": "anthropic",
|
|
172
|
+
// "model": "claude-3-5-haiku-latest",
|
|
173
|
+
// // "model": "claude-sonnet-4-0",
|
|
174
|
+
// },
|
|
175
|
+
|
|
176
|
+
// "llm": {
|
|
177
|
+
// "model_provider": "google_genai",
|
|
178
|
+
// "model": "gemini-2.5-flash",
|
|
179
|
+
// // "model": "gemini-2.5-pro",
|
|
180
|
+
// }
|
|
181
|
+
|
|
182
|
+
"example_queries": [
|
|
183
|
+
"Tell me how LLMs work in a few sentences",
|
|
184
|
+
"Are there any weather alerts in California?",
|
|
185
|
+
"Read the news headlines on bbc.com",
|
|
186
|
+
],
|
|
187
|
+
|
|
188
|
+
"mcp_servers": {
|
|
189
|
+
// Local MCP server that uses `npx`
|
|
190
|
+
"weather": {
|
|
191
|
+
"command": "npx",
|
|
192
|
+
"args": [ "-y", "@h1deya/mcp-server-weather" ]
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
// Another local server that uses `uvx`
|
|
196
|
+
"fetch": {
|
|
197
|
+
"command": "uvx",
|
|
198
|
+
"args": [ "mcp-server-fetch" ]
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
"brave-search": {
|
|
202
|
+
"command": "npx",
|
|
203
|
+
"args": [ "-y", "@modelcontextprotocol/server-brave-search" ],
|
|
204
|
+
"env": { "BRAVE_API_KEY": "${BRAVE_API_KEY}" }
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
// Remote MCP server via URL
|
|
208
|
+
// Auto-detection: tries Streamable HTTP first, falls back to SSE
|
|
209
|
+
"remote-mcp-server": {
|
|
210
|
+
"url": "https://api.example.com/..."
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
// Server with authentication
|
|
214
|
+
"github": {
|
|
215
|
+
"type": "http", // recommended to specify the protocol explicitly when authentication is used
|
|
216
|
+
"url": "https://api.githubcopilot.com/mcp/",
|
|
217
|
+
"headers": {
|
|
218
|
+
"Authorization": "Bearer ${GITHUB_PERSONAL_ACCESS_TOKEN}"
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Environment Variables
|
|
226
|
+
|
|
227
|
+
Create a `.env` file for API keys:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
OPENAI_API_KEY=sk-ant-...
|
|
231
|
+
ANTHROPIC_API_KEY=sk-proj-...
|
|
232
|
+
GOOGLE_API_KEY=AI...
|
|
233
|
+
|
|
234
|
+
# Other services as needed
|
|
235
|
+
GITHUB_PERSONAL_ACCESS_TOKEN=github_pat_...
|
|
236
|
+
BRAVE_API_KEY=BSA...
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Popular MCP Servers to Try
|
|
240
|
+
|
|
241
|
+
There are quite a few useful MCP servers already available:
|
|
242
|
+
|
|
243
|
+
- [MCP Server Listing on the Official Site](https://github.com/modelcontextprotocol/servers?tab=readme-ov-file#model-context-protocol-servers)
|
|
244
|
+
|
|
245
|
+
## Troubleshooting
|
|
246
|
+
|
|
247
|
+
- Make sure your configuration and .env files are correct, especially the spelling of the API keys
|
|
248
|
+
- Check the local MCP server logs
|
|
249
|
+
- Use `--verbose` flag to view the detailed logs
|
|
250
|
+
- Refer to [Debugging Section in MCP documentation](https://modelcontextprotocol.io/docs/tools/debugging)
|
|
251
|
+
|
|
252
|
+
## License
|
|
253
|
+
|
|
254
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
255
|
+
|
|
256
|
+
## Contributing
|
|
257
|
+
|
|
258
|
+
Issues and pull requests welcome! This tool aims to make MCP server testing as simple as possible.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "dotenv/config";
|
|
3
|
+
import { createReactAgent } from "@langchain/langgraph/prebuilt";
|
|
4
|
+
import { MemorySaver } from "@langchain/langgraph";
|
|
5
|
+
import { HumanMessage } from "@langchain/core/messages";
|
|
6
|
+
import { convertMcpToLangchainTools } from "@h1deya/langchain-mcp-tools";
|
|
7
|
+
import { initChatModel } from "./init-chat-model.js";
|
|
8
|
+
import { loadConfig } from "./load-config.js";
|
|
9
|
+
import readline from "readline";
|
|
10
|
+
import yargs from "yargs";
|
|
11
|
+
import { hideBin } from "yargs/helpers";
|
|
12
|
+
import * as fs from "fs";
|
|
13
|
+
import * as path from "path";
|
|
14
|
+
// NOTE: without the following, I got this error:
|
|
15
|
+
// ReferenceError: WebSocket is not defined
|
|
16
|
+
// at <anonymous> (.../node_modules/@modelcontextprotocol/sdk/src/client/websocket.ts:29:26)
|
|
17
|
+
import WebSocket from 'ws';
|
|
18
|
+
global.WebSocket = WebSocket;
|
|
19
|
+
// // Remote server connection testing
|
|
20
|
+
// // Uncomment the following code snippet and add the configuration below to the configuration file.
|
|
21
|
+
// // weather: {
|
|
22
|
+
// // "url": "http://localhost:${SSE_SERVER_PORT}/sse"
|
|
23
|
+
// // },
|
|
24
|
+
// // This leverages the Supergateway to make the Weather Studio MCP Server accessible as an SSE server.
|
|
25
|
+
// import { startRemoteMcpServerLocally } from "./remote-server-utils.js";
|
|
26
|
+
// const [sseServerProcess, sseServerPort] = await startRemoteMcpServerLocally(
|
|
27
|
+
// "SSE", "npx -y @h1deya/mcp-server-weather");
|
|
28
|
+
// process.env.SSE_SERVER_PORT = `${sseServerPort}`;
|
|
29
|
+
// Constants
|
|
30
|
+
const COLORS = {
|
|
31
|
+
YELLOW: "\x1b[33m",
|
|
32
|
+
CYAN: "\x1b[36m",
|
|
33
|
+
GREEN: "\x1b[32m",
|
|
34
|
+
RESET: "\x1b[0m"
|
|
35
|
+
};
|
|
36
|
+
const parseArguments = () => {
|
|
37
|
+
return yargs(hideBin(process.argv))
|
|
38
|
+
.options({
|
|
39
|
+
config: {
|
|
40
|
+
type: "string",
|
|
41
|
+
description: "Path to config file",
|
|
42
|
+
demandOption: false,
|
|
43
|
+
default: "llm_mcp_config.json5",
|
|
44
|
+
alias: "c",
|
|
45
|
+
},
|
|
46
|
+
verbose: {
|
|
47
|
+
type: "boolean",
|
|
48
|
+
description: "Run with verbose logging",
|
|
49
|
+
demandOption: false,
|
|
50
|
+
default: false,
|
|
51
|
+
alias: "v",
|
|
52
|
+
},
|
|
53
|
+
logDir: {
|
|
54
|
+
type: "string",
|
|
55
|
+
description: "Directory to store MCP server log files",
|
|
56
|
+
demandOption: false,
|
|
57
|
+
default: ".",
|
|
58
|
+
alias: "l",
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
.help()
|
|
62
|
+
.alias("help", "h")
|
|
63
|
+
.parseSync();
|
|
64
|
+
};
|
|
65
|
+
// Input handling
|
|
66
|
+
const createReadlineInterface = () => {
|
|
67
|
+
return readline.createInterface({
|
|
68
|
+
input: process.stdin,
|
|
69
|
+
output: process.stdout
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
const getInput = (rl, prompt) => {
|
|
73
|
+
return new Promise((resolve) => rl.question(prompt, resolve));
|
|
74
|
+
};
|
|
75
|
+
async function getUserQuery(rl, remainingQueries) {
|
|
76
|
+
const input = await getInput(rl, `${COLORS.YELLOW}Query: `);
|
|
77
|
+
process.stdout.write(COLORS.RESET);
|
|
78
|
+
const query = input.trim();
|
|
79
|
+
if (query.toLowerCase() === "quit" || query.toLowerCase() === "q") {
|
|
80
|
+
rl.close();
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
if (query === "") {
|
|
84
|
+
const exampleQuery = remainingQueries.shift();
|
|
85
|
+
if (!exampleQuery) {
|
|
86
|
+
console.log("\nPlease type a query, or 'quit' or 'q' to exit\n");
|
|
87
|
+
return await getUserQuery(rl, remainingQueries);
|
|
88
|
+
}
|
|
89
|
+
process.stdout.write("\x1b[1A\x1b[2K"); // Move up and clear the line
|
|
90
|
+
console.log(`${COLORS.YELLOW}Example Query: ${exampleQuery}${COLORS.RESET}`);
|
|
91
|
+
return exampleQuery;
|
|
92
|
+
}
|
|
93
|
+
return query;
|
|
94
|
+
}
|
|
95
|
+
// Conversation loop
|
|
96
|
+
async function handleConversation(agent, remainingQueries) {
|
|
97
|
+
console.log("\nConversation started. Type 'quit' or 'q to end the conversation.\n");
|
|
98
|
+
if (remainingQueries && remainingQueries.length > 0) {
|
|
99
|
+
console.log("Exaample Queries (just type Enter to supply them one by one):");
|
|
100
|
+
remainingQueries.forEach(query => console.log(`- ${query}`));
|
|
101
|
+
console.log();
|
|
102
|
+
}
|
|
103
|
+
const rl = createReadlineInterface();
|
|
104
|
+
while (true) {
|
|
105
|
+
const query = await getUserQuery(rl, remainingQueries);
|
|
106
|
+
console.log();
|
|
107
|
+
if (!query) {
|
|
108
|
+
console.log(`${COLORS.CYAN}Goodbye!${COLORS.RESET}\n`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const agentFinalState = await agent.invoke({ messages: [new HumanMessage(query)] }, { configurable: { thread_id: "test-thread" } });
|
|
112
|
+
// the last message is an AIMessage
|
|
113
|
+
const result = agentFinalState.messages[agentFinalState.messages.length - 1].content;
|
|
114
|
+
const messageOneBefore = agentFinalState.messages[agentFinalState.messages.length - 2];
|
|
115
|
+
if (messageOneBefore.constructor.name === "ToolMessage") {
|
|
116
|
+
console.log(); // new line after tool call output
|
|
117
|
+
}
|
|
118
|
+
console.log(`${COLORS.CYAN}${result}${COLORS.RESET}\n`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function addLogFileWatcher(logPath, serverName) {
|
|
122
|
+
const stats = fs.statSync(logPath);
|
|
123
|
+
let lastSize = stats.size;
|
|
124
|
+
const watcher = fs.watch(logPath, (eventType, filename) => {
|
|
125
|
+
if (eventType === 'change') {
|
|
126
|
+
// console.log(`*** file updated: "${filename}"`);
|
|
127
|
+
const stats = fs.statSync(logPath);
|
|
128
|
+
const currentSize = stats.size;
|
|
129
|
+
if (currentSize > lastSize) {
|
|
130
|
+
const buffer = Buffer.alloc(currentSize - lastSize);
|
|
131
|
+
const fd = fs.openSync(logPath, 'r');
|
|
132
|
+
fs.readSync(fd, buffer, 0, buffer.length, lastSize);
|
|
133
|
+
fs.closeSync(fd);
|
|
134
|
+
const newContent = buffer.toString();
|
|
135
|
+
console.log(`${COLORS.GREEN}[MCP Server Log: "${serverName}"]${COLORS.RESET}`, newContent.trim());
|
|
136
|
+
lastSize = currentSize;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
return watcher;
|
|
141
|
+
}
|
|
142
|
+
// Application initialization
|
|
143
|
+
async function initializeReactAgent(config, verbose, logDir) {
|
|
144
|
+
const llmProvider = config.llm.model_provider;
|
|
145
|
+
console.log("Initializing model...", config.llm, "\n");
|
|
146
|
+
const llmConfig = {
|
|
147
|
+
modelProvider: llmProvider,
|
|
148
|
+
model: config.llm.model,
|
|
149
|
+
temperature: config.llm.temperature,
|
|
150
|
+
maxTokens: config.llm.max_tokens,
|
|
151
|
+
};
|
|
152
|
+
const llm = initChatModel(llmConfig);
|
|
153
|
+
console.log(`Initializing ${Object.keys(config.mcp_servers).length} MCP server(s)...\n`);
|
|
154
|
+
// Ensure log directory exists
|
|
155
|
+
if (!fs.existsSync(logDir)) {
|
|
156
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
157
|
+
console.log(`Created log directory: ${logDir}`);
|
|
158
|
+
}
|
|
159
|
+
// Set a file descriptor to which MCP server's stderr is redirected
|
|
160
|
+
const openedLogFiles = {};
|
|
161
|
+
Object.keys(config.mcp_servers).forEach(serverName => {
|
|
162
|
+
if (config.mcp_servers[serverName].command) {
|
|
163
|
+
const logPath = path.join(logDir, `mcp-server-${serverName}.log`);
|
|
164
|
+
console.log(`Writing MCP server log file: ${logPath}`);
|
|
165
|
+
const logFd = fs.openSync(logPath, "w");
|
|
166
|
+
config.mcp_servers[serverName].stderr = logFd;
|
|
167
|
+
const watcher = addLogFileWatcher(logPath, serverName);
|
|
168
|
+
openedLogFiles[logPath] = { fd: logFd, watcher };
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
const toolsAndCleanup = await convertMcpToLangchainTools(config.mcp_servers, { llmProvider, logLevel: verbose ? "debug" : "info" });
|
|
172
|
+
const tools = toolsAndCleanup.tools;
|
|
173
|
+
const mcpCleanup = toolsAndCleanup.cleanup;
|
|
174
|
+
const agent = createReactAgent({
|
|
175
|
+
llm,
|
|
176
|
+
tools,
|
|
177
|
+
checkpointSaver: new MemorySaver(),
|
|
178
|
+
});
|
|
179
|
+
async function cleanup() {
|
|
180
|
+
await mcpCleanup();
|
|
181
|
+
// close log files and their watchers
|
|
182
|
+
Object.keys(openedLogFiles).forEach(logPath => {
|
|
183
|
+
try {
|
|
184
|
+
openedLogFiles[logPath].watcher.close();
|
|
185
|
+
fs.closeSync(openedLogFiles[logPath].fd);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
console.error(`Error closing log file: ${logPath}:`, error);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
return { agent, cleanup };
|
|
193
|
+
}
|
|
194
|
+
// Main
|
|
195
|
+
async function main() {
|
|
196
|
+
let mcpCleanup;
|
|
197
|
+
const argv = parseArguments();
|
|
198
|
+
try {
|
|
199
|
+
const config = loadConfig(argv.config);
|
|
200
|
+
const { agent, cleanup } = await initializeReactAgent(config, argv.verbose, argv.logDir);
|
|
201
|
+
mcpCleanup = cleanup;
|
|
202
|
+
await handleConversation(agent, config.example_queries ?? []);
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
if (error instanceof Error) {
|
|
206
|
+
if (error.message.includes("Failed to load configuration")) {
|
|
207
|
+
console.error(error.message);
|
|
208
|
+
if (error.message.includes("ENOENT")) {
|
|
209
|
+
console.error(`Make sure the config file "${argv.config}" is available`);
|
|
210
|
+
console.error("Use the --config option to specify which JSON5 configuration file to read");
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
else if (error.message.includes("Failed to initialize chat model")) {
|
|
214
|
+
console.error(error.message);
|
|
215
|
+
console.error("Check the .env file for the API key settings");
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
console.error(error.message);
|
|
219
|
+
}
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
await mcpCleanup?.();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// Application entry point with error handling
|
|
229
|
+
main().catch((error) => {
|
|
230
|
+
console.error(error);
|
|
231
|
+
process.exit(1);
|
|
232
|
+
});
|
|
233
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BaseChatModel, BindToolsInput } from '@langchain/core/language_models/chat_models';
|
|
2
|
+
interface ChatModelConfig {
|
|
3
|
+
modelProvider: string;
|
|
4
|
+
model: string;
|
|
5
|
+
temperature?: number;
|
|
6
|
+
maxTokens?: number;
|
|
7
|
+
tools?: BindToolsInput[];
|
|
8
|
+
}
|
|
9
|
+
export declare function initChatModel(config: ChatModelConfig): BaseChatModel;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=init-chat-model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-chat-model.d.ts","sourceRoot":"","sources":["../src/init-chat-model.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,6CAA6C,CAAC;AAK5F,UAAU,eAAe;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;CAC1B;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,GAAG,aAAa,CAyCpE"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ChatAnthropic } from '@langchain/anthropic';
|
|
2
|
+
import { ChatOpenAI } from '@langchain/openai';
|
|
3
|
+
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
|
|
4
|
+
export function initChatModel(config) {
|
|
5
|
+
let model;
|
|
6
|
+
const { modelProvider, tools, ...llmConfig } = config;
|
|
7
|
+
try {
|
|
8
|
+
switch (modelProvider.toLowerCase()) {
|
|
9
|
+
case 'openai':
|
|
10
|
+
model = new ChatOpenAI(llmConfig);
|
|
11
|
+
break;
|
|
12
|
+
case 'anthropic':
|
|
13
|
+
model = new ChatAnthropic(llmConfig);
|
|
14
|
+
break;
|
|
15
|
+
case 'google_genai':
|
|
16
|
+
model = new ChatGoogleGenerativeAI(llmConfig);
|
|
17
|
+
break;
|
|
18
|
+
default:
|
|
19
|
+
throw new Error(`Unsupported model_provider: ${modelProvider}`);
|
|
20
|
+
}
|
|
21
|
+
if (typeof model?.bindTools === 'function') {
|
|
22
|
+
if (tools && tools.length > 0) {
|
|
23
|
+
// FIXME
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
25
|
+
model = model.bindTools(tools);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
throw new Error(`Tool calling unsupported by model_provider: ${modelProvider}`);
|
|
30
|
+
}
|
|
31
|
+
return model;
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
throw new Error(`Failed to initialize chat model: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=init-chat-model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-chat-model.js","sourceRoot":"","sources":["../src/init-chat-model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAcjE,MAAM,UAAU,aAAa,CAAC,MAAuB;IACnD,IAAI,KAAoB,CAAC;IAEzB,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,GAAG,MAAM,CAAC;IAEtD,IAAI,CAAC;QACH,QAAQ,aAAa,CAAC,WAAW,EAAE,EAAE,CAAC;YACpC,KAAK,QAAQ;gBACX,KAAK,GAAG,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC;gBAClC,MAAM;YAER,KAAK,WAAW;gBACd,KAAK,GAAG,IAAI,aAAa,CAAC,SAAS,CAAC,CAAC;gBACrC,MAAM;YAER,KAAK,cAAc;gBACjB,KAAK,GAAG,IAAI,sBAAsB,CAAC,SAAS,CAAC,CAAC;gBAC9C,MAAM;YAER;gBACE,MAAM,IAAI,KAAK,CACb,+BAA+B,aAAa,EAAE,CAC/C,CAAC;QACN,CAAC;QAED,IAAI,OAAO,KAAK,EAAE,SAAS,KAAK,UAAU,EAAE,CAAC;YAC3C,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC9B,QAAQ;gBACR,sEAAsE;gBACtE,KAAK,GAAI,KAAiC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CACb,+CAA+C,aAAa,EAAE,CAC/D,CAAC;QACJ,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,oCAAoC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC;IAClH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface LLMConfig {
|
|
2
|
+
model_provider: string;
|
|
3
|
+
model: string;
|
|
4
|
+
temperature?: number;
|
|
5
|
+
max_tokens?: number;
|
|
6
|
+
max_completion_tokens?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface CommandBasedConfig {
|
|
9
|
+
url?: never;
|
|
10
|
+
transport?: string;
|
|
11
|
+
type?: string;
|
|
12
|
+
headers?: never;
|
|
13
|
+
command: string;
|
|
14
|
+
args?: string[];
|
|
15
|
+
env?: Record<string, string>;
|
|
16
|
+
stderr?: number;
|
|
17
|
+
}
|
|
18
|
+
export interface UrlBasedConfig {
|
|
19
|
+
url: string;
|
|
20
|
+
transport?: string;
|
|
21
|
+
type?: string;
|
|
22
|
+
headers?: Record<string, string>;
|
|
23
|
+
command?: never;
|
|
24
|
+
args?: never;
|
|
25
|
+
env?: never;
|
|
26
|
+
stderr?: never;
|
|
27
|
+
}
|
|
28
|
+
export type MCPServerConfig = CommandBasedConfig | UrlBasedConfig;
|
|
29
|
+
export interface Config {
|
|
30
|
+
llm: LLMConfig;
|
|
31
|
+
example_queries?: string[];
|
|
32
|
+
mcp_servers: {
|
|
33
|
+
[key: string]: MCPServerConfig;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export declare function loadConfig(path: string): Config;
|
|
37
|
+
//# sourceMappingURL=load-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-config.d.ts","sourceRoot":"","sources":["../src/load-config.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,SAAS;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,EAAE,KAAK,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,KAAK,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,KAAK,CAAC;IAChB,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,GAAG,CAAC,EAAE,KAAK,CAAC;IACZ,MAAM,CAAC,EAAE,KAAK,CAAC;CAChB;AAED,MAAM,MAAM,eAAe,GAAG,kBAAkB,GAAG,cAAc,CAAC;AAElE,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,SAAS,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,WAAW,EAAE;QACX,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAAC;KAChC,CAAA;CACF;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAqB/C"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import JSON5 from 'json5';
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
export function loadConfig(path) {
|
|
4
|
+
try {
|
|
5
|
+
let json5Str = readFileSync(path, 'utf-8');
|
|
6
|
+
// Replace environment variables in the format ${VAR_NAME} with their values
|
|
7
|
+
Object.entries(process.env).forEach(([key, value]) => {
|
|
8
|
+
json5Str = json5Str.replace(`\${${key}}`, value || '');
|
|
9
|
+
});
|
|
10
|
+
const config = JSON5.parse(json5Str);
|
|
11
|
+
// Validate required fields
|
|
12
|
+
validateConfig(config);
|
|
13
|
+
return config;
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
if (error instanceof Error) {
|
|
17
|
+
throw new Error(`Failed to load configuration from "${path}": ${error.message}`);
|
|
18
|
+
}
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function validateConfig(config) {
|
|
23
|
+
if (typeof config !== 'object' || config === null) {
|
|
24
|
+
throw new Error('Configuration must be an object');
|
|
25
|
+
}
|
|
26
|
+
if (!('llm' in config)) {
|
|
27
|
+
throw new Error('LLM configuration is required');
|
|
28
|
+
}
|
|
29
|
+
validateLLMConfig(config.llm);
|
|
30
|
+
if ('example_queries' in config) {
|
|
31
|
+
if (!Array.isArray(config.example_queries)) {
|
|
32
|
+
throw new Error('example_queries must be an array if provided');
|
|
33
|
+
}
|
|
34
|
+
if (config.example_queries.some((query) => typeof query !== 'string')) {
|
|
35
|
+
throw new Error('All example queries must be strings');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (!('mcp_servers' in config)) {
|
|
39
|
+
throw new Error('mcp_servers configuration is required');
|
|
40
|
+
}
|
|
41
|
+
if (typeof config.mcp_servers !== 'object' || config.mcp_servers === null) {
|
|
42
|
+
throw new Error('mcp_servers must be an object');
|
|
43
|
+
}
|
|
44
|
+
Object.entries(config.mcp_servers).forEach(([key, value]) => {
|
|
45
|
+
try {
|
|
46
|
+
validateMCPServerConfig(value);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
throw new Error(`Invalid configuration for MCP server "${key}": ${error instanceof Error ? error.message : String(error)}`);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function validateLLMConfig(llmConfig) {
|
|
54
|
+
if (typeof llmConfig !== 'object' || llmConfig === null) {
|
|
55
|
+
throw new Error('LLM configuration must be an object');
|
|
56
|
+
}
|
|
57
|
+
if (!('model_provider' in llmConfig) || typeof llmConfig.model_provider !== 'string') {
|
|
58
|
+
throw new Error('LLM model_provider must be a string');
|
|
59
|
+
}
|
|
60
|
+
if (!('model' in llmConfig) || typeof llmConfig.model !== 'string') {
|
|
61
|
+
throw new Error('LLM model must be a string');
|
|
62
|
+
}
|
|
63
|
+
if ('temperature' in llmConfig && typeof llmConfig.temperature !== 'number') {
|
|
64
|
+
throw new Error('LLM temperature must be a number if provided');
|
|
65
|
+
}
|
|
66
|
+
if ('max_tokens' in llmConfig && typeof llmConfig.max_tokens !== 'number') {
|
|
67
|
+
throw new Error('LLM max_tokens must be a number if provided');
|
|
68
|
+
}
|
|
69
|
+
if ('max_completion_tokens' in llmConfig && typeof llmConfig.max_completion_tokens !== 'number') {
|
|
70
|
+
throw new Error('LLM max_completion_tokens must be a number if provided');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function validateMCPServerConfig(serverConfig) {
|
|
74
|
+
if (typeof serverConfig !== 'object' || serverConfig === null) {
|
|
75
|
+
throw new Error('MCP server configuration must be an object');
|
|
76
|
+
}
|
|
77
|
+
if (!('url' in serverConfig) && !('command' in serverConfig)) {
|
|
78
|
+
throw new Error('MCP server configuration must include command or url');
|
|
79
|
+
}
|
|
80
|
+
if ('url' in serverConfig && typeof serverConfig.url !== 'string') {
|
|
81
|
+
throw new Error('MCP server url must be a string');
|
|
82
|
+
}
|
|
83
|
+
if ('transport' in serverConfig && typeof serverConfig.transport !== 'string') {
|
|
84
|
+
throw new Error('MCP server transport must be a string');
|
|
85
|
+
}
|
|
86
|
+
if ('type' in serverConfig && typeof serverConfig.type !== 'string') {
|
|
87
|
+
throw new Error('MCP server type must be a string');
|
|
88
|
+
}
|
|
89
|
+
if ('headers' in serverConfig && serverConfig.headers !== undefined) {
|
|
90
|
+
if (typeof serverConfig.headers !== 'object' || serverConfig.headers === null) {
|
|
91
|
+
throw new Error('MCP server headers must be an object if provided');
|
|
92
|
+
}
|
|
93
|
+
// Validate that all env values are strings
|
|
94
|
+
for (const [, value] of Object.entries(serverConfig.headers)) {
|
|
95
|
+
if (typeof value !== 'string') {
|
|
96
|
+
throw new Error('All MCP server headers values must be strings');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if ('command' in serverConfig && typeof serverConfig.command !== 'string') {
|
|
101
|
+
throw new Error('MCP server command must be a string');
|
|
102
|
+
}
|
|
103
|
+
if ('args' in serverConfig) {
|
|
104
|
+
if (!Array.isArray(serverConfig.args)) {
|
|
105
|
+
throw new Error('MCP server args must be an array');
|
|
106
|
+
}
|
|
107
|
+
if (serverConfig.args.some((arg) => typeof arg !== 'string')) {
|
|
108
|
+
throw new Error('All MCP server args must be strings');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if ('env' in serverConfig && serverConfig.env !== undefined) {
|
|
112
|
+
if (typeof serverConfig.env !== 'object' || serverConfig.env === null) {
|
|
113
|
+
throw new Error('MCP server env must be an object if provided');
|
|
114
|
+
}
|
|
115
|
+
// Validate that all env values are strings
|
|
116
|
+
for (const [, value] of Object.entries(serverConfig.env)) {
|
|
117
|
+
if (typeof value !== 'string') {
|
|
118
|
+
throw new Error('All MCP server env values must be strings');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=load-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-config.js","sourceRoot":"","sources":["../src/load-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as child_process from 'child_process';
|
|
2
|
+
/**
|
|
3
|
+
* Find and return a free port on localhost.
|
|
4
|
+
* @returns A Promise resolving to an available port number
|
|
5
|
+
*/
|
|
6
|
+
export declare function findFreePort(): Promise<number>;
|
|
7
|
+
/**
|
|
8
|
+
* Start an MCP server process via supergateway with the specified transport
|
|
9
|
+
* type. Supergateway runs MCP stdio-based servers over SSE or WebSockets
|
|
10
|
+
* and is used here to run local SSE/WS servers for connection testing.
|
|
11
|
+
* Ref: https://github.com/supercorp-ai/supergateway
|
|
12
|
+
*
|
|
13
|
+
* @param transportType - The transport type, either 'SSE' or 'WS'
|
|
14
|
+
* @param mcpServerRunCommand - The command to run the MCP server
|
|
15
|
+
* @returns A Promise resolving to [serverProcess, serverPort]
|
|
16
|
+
*/
|
|
17
|
+
export declare function startRemoteMcpServerLocally(transportType: string, mcpServerRunCommand: string): Promise<[child_process.ChildProcess, number]>;
|
|
18
|
+
//# sourceMappingURL=remote-server-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-server-utils.d.ts","sourceRoot":"","sources":["../src/remote-server-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,aAAa,MAAM,eAAe,CAAC;AAO/C;;;GAGG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAYpD;AAED;;;;;;;;;GASG;AACH,wBAAsB,2BAA2B,CAC/C,aAAa,EAAE,MAAM,EACrB,mBAAmB,EAAE,MAAM,GAC1B,OAAO,CAAC,CAAC,aAAa,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CA4E/C"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as child_process from 'child_process';
|
|
2
|
+
import * as net from 'net';
|
|
3
|
+
// NOTE: Hard-coded dependency on the Supergateway message
|
|
4
|
+
// to easily identify the end of initialization.
|
|
5
|
+
const SUPERGATEWAY_READY_MESSAGE = "[supergateway] Listening on port";
|
|
6
|
+
/**
|
|
7
|
+
* Find and return a free port on localhost.
|
|
8
|
+
* @returns A Promise resolving to an available port number
|
|
9
|
+
*/
|
|
10
|
+
export async function findFreePort() {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
const server = net.createServer();
|
|
13
|
+
server.unref();
|
|
14
|
+
server.on('error', reject);
|
|
15
|
+
server.listen(0, () => {
|
|
16
|
+
const port = server.address().port;
|
|
17
|
+
server.close(() => {
|
|
18
|
+
resolve(port);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Start an MCP server process via supergateway with the specified transport
|
|
25
|
+
* type. Supergateway runs MCP stdio-based servers over SSE or WebSockets
|
|
26
|
+
* and is used here to run local SSE/WS servers for connection testing.
|
|
27
|
+
* Ref: https://github.com/supercorp-ai/supergateway
|
|
28
|
+
*
|
|
29
|
+
* @param transportType - The transport type, either 'SSE' or 'WS'
|
|
30
|
+
* @param mcpServerRunCommand - The command to run the MCP server
|
|
31
|
+
* @returns A Promise resolving to [serverProcess, serverPort]
|
|
32
|
+
*/
|
|
33
|
+
export async function startRemoteMcpServerLocally(transportType, mcpServerRunCommand) {
|
|
34
|
+
const serverPort = await findFreePort();
|
|
35
|
+
// Base command common to both server types
|
|
36
|
+
const command = [
|
|
37
|
+
"npx",
|
|
38
|
+
"-y",
|
|
39
|
+
"supergateway",
|
|
40
|
+
"--stdio",
|
|
41
|
+
mcpServerRunCommand,
|
|
42
|
+
"--port", serverPort.toString(),
|
|
43
|
+
];
|
|
44
|
+
// Add transport-specific arguments
|
|
45
|
+
if (transportType.toLowerCase() === 'sse') {
|
|
46
|
+
command.push("--baseUrl", `http://localhost:${serverPort}`, "--ssePath", "/sse", "--messagePath", "/message");
|
|
47
|
+
}
|
|
48
|
+
else if (transportType.toLowerCase() === 'ws') {
|
|
49
|
+
command.push("--outputTransport", "ws", "--messagePath", "/message");
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
throw new Error(`Unsupported transport type: ${transportType}`);
|
|
53
|
+
}
|
|
54
|
+
// Start the server process with stdout/stderr piped
|
|
55
|
+
const serverProcess = child_process.spawn(command[0], command.slice(1), {
|
|
56
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
57
|
+
});
|
|
58
|
+
console.log(`Starting ${transportType.toUpperCase()} MCP Server Process with PID: ${serverProcess.pid}`);
|
|
59
|
+
// Return a promise that resolves when the server is ready
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
// Set a reasonable timeout as fallback (30 seconds)
|
|
62
|
+
const timeoutId = setTimeout(() => {
|
|
63
|
+
reject(new Error(`Timed out waiting for ${transportType.toUpperCase()} server to start`));
|
|
64
|
+
}, 30000);
|
|
65
|
+
// Listen for the specific log message that indicates server is ready
|
|
66
|
+
serverProcess.stdout?.on('data', (data) => {
|
|
67
|
+
const output = data.toString();
|
|
68
|
+
console.log(output); // Still log the output to console
|
|
69
|
+
if (output.includes(SUPERGATEWAY_READY_MESSAGE)) {
|
|
70
|
+
clearTimeout(timeoutId);
|
|
71
|
+
console.log(`${transportType.toUpperCase()} MCP Server is ready on port ${serverPort}`);
|
|
72
|
+
resolve([serverProcess, serverPort]);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
// Handle errors
|
|
76
|
+
serverProcess.stderr?.on('data', (data) => {
|
|
77
|
+
console.error(`Server error: ${data}`);
|
|
78
|
+
});
|
|
79
|
+
serverProcess.on('error', (err) => {
|
|
80
|
+
clearTimeout(timeoutId);
|
|
81
|
+
reject(new Error(`Failed to start server: ${err.message}`));
|
|
82
|
+
});
|
|
83
|
+
serverProcess.on('exit', (code) => {
|
|
84
|
+
if (code !== 0 && code !== null) {
|
|
85
|
+
clearTimeout(timeoutId);
|
|
86
|
+
reject(new Error(`Server process exited with code ${code}`));
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=remote-server-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-server-utils.js","sourceRoot":"","sources":["../src/remote-server-utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@h1deya/mcp-client-cli",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Simple MCP Client to quickly test and explore MCP servers from the command line",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"mcp",
|
|
8
|
+
"model-context-protocol",
|
|
9
|
+
"client",
|
|
10
|
+
"cli",
|
|
11
|
+
"langchain",
|
|
12
|
+
"tools",
|
|
13
|
+
"typescript",
|
|
14
|
+
"simple",
|
|
15
|
+
"quick",
|
|
16
|
+
"explore",
|
|
17
|
+
"try",
|
|
18
|
+
"test"
|
|
19
|
+
],
|
|
20
|
+
"author": "hideya kawahara",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/hideya/mcp-client-langchain-ts.git"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/hideya/mcp-client-langchain-ts/issues"
|
|
27
|
+
},
|
|
28
|
+
"bin": {
|
|
29
|
+
"mcp-client-cli": "dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"main": "./dist/index.js",
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"README.md",
|
|
35
|
+
"LICENSE"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.0.0"
|
|
39
|
+
},
|
|
40
|
+
"type": "module",
|
|
41
|
+
"scripts": {
|
|
42
|
+
"start": "tsx src/index.ts",
|
|
43
|
+
"start:h": "tsx src/index.ts -h",
|
|
44
|
+
"start:v": "tsx src/index.ts -v",
|
|
45
|
+
"dev": "tsx watch src/index.ts",
|
|
46
|
+
"lint": "eslint src",
|
|
47
|
+
"clean": "git clean -fdxn -e .env && read -p 'OK?' && git clean -fdx -e .env",
|
|
48
|
+
"test": "echo \"No tests specified\" && exit 0",
|
|
49
|
+
"build": "tsc && chmod +x dist/index.js",
|
|
50
|
+
"publish:prepare": "npm run clean && npm install && npm run build",
|
|
51
|
+
"publish:test": "npm run publish:prepare && npm publish --access=public --dry-run",
|
|
52
|
+
"publish:do": "npm run publish:prepare && npm publish --access=public"
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@h1deya/langchain-mcp-tools": "^0.3.0",
|
|
56
|
+
"@langchain/anthropic": "^0.3.11",
|
|
57
|
+
"@langchain/core": "^0.3.61",
|
|
58
|
+
"@langchain/google-genai": "^0.2.12",
|
|
59
|
+
"@langchain/langgraph": "^0.3.6",
|
|
60
|
+
"@langchain/openai": "^0.3.16",
|
|
61
|
+
"@types/yargs": "^17.0.33",
|
|
62
|
+
"dotenv": "^16.4.7",
|
|
63
|
+
"json5": "^2.2.3",
|
|
64
|
+
"ws": "^8.18.2",
|
|
65
|
+
"yargs": "^17.7.2",
|
|
66
|
+
"zod": "^3.24.1"
|
|
67
|
+
},
|
|
68
|
+
"devDependencies": {
|
|
69
|
+
"@eslint/js": "^9.17.0",
|
|
70
|
+
"@types/node": "^22.10.5",
|
|
71
|
+
"@types/ws": "^8.18.1",
|
|
72
|
+
"@typescript-eslint/eslint-plugin": "^8.19.0",
|
|
73
|
+
"@typescript-eslint/parser": "^8.19.0",
|
|
74
|
+
"eslint": "^9.17.0",
|
|
75
|
+
"tsx": "^4.19.2",
|
|
76
|
+
"typescript": "^5.7.2",
|
|
77
|
+
"typescript-eslint": "^8.19.0"
|
|
78
|
+
}
|
|
79
|
+
}
|