@mcpjam/inspector 0.1.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/cli/build/cli.js +195 -0
- package/cli/build/client/connection.js +33 -0
- package/cli/build/client/index.js +6 -0
- package/cli/build/client/prompts.js +23 -0
- package/cli/build/client/resources.js +30 -0
- package/cli/build/client/tools.js +64 -0
- package/cli/build/client/types.js +1 -0
- package/cli/build/error-handler.js +18 -0
- package/cli/build/index.js +166 -0
- package/cli/build/transport.js +47 -0
- package/client/bin/client.js +57 -0
- package/client/bin/start.js +124 -0
- package/client/dist/assets/OAuthCallback-D9GEIvKa.js +56 -0
- package/client/dist/assets/OAuthDebugCallback-zUpX3mpm.js +45 -0
- package/client/dist/assets/index-CIxCnj5w.css +2312 -0
- package/client/dist/assets/index-DTUWTApO.js +37142 -0
- package/client/dist/assets/oauthUtils-DTcoXpSP.js +33 -0
- package/client/dist/index.html +14 -0
- package/client/dist/mcp.svg +12 -0
- package/package.json +63 -0
- package/server/build/index.js +300 -0
- package/server/build/mcpProxy.js +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Anthropic, PBC
|
|
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
|
+
# MCPJam Inspector
|
|
2
|
+
|
|
3
|
+
The MCPJam inspector is a dev tool for testing and debugging MCP servers. The MCPJam inspector is a fork of the mcp-inspector with additional improvements.
|
|
4
|
+
|
|
5
|
+
## Running the Inspector
|
|
6
|
+
|
|
7
|
+
### Requirements
|
|
8
|
+
|
|
9
|
+
- Node.js: ^22.7.5
|
|
10
|
+
|
|
11
|
+
### From an MCP server repository
|
|
12
|
+
|
|
13
|
+
To inspect an MCP server implementation, there's no need to clone this repo. Instead, use `npx`. For example, if your server is built at `build/index.js`:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx @mcpjam/inspector node build/index.js
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
You can pass both arguments and environment variables to your MCP server. Arguments are passed directly to your server, while environment variables can be set using the `-e` flag:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Pass arguments only
|
|
23
|
+
npx @mcpjam/inspector node build/index.js arg1 arg2
|
|
24
|
+
|
|
25
|
+
# Pass environment variables only
|
|
26
|
+
npx @mcpjam/inspector -e key=value -e key2=$VALUE2 node build/index.js
|
|
27
|
+
|
|
28
|
+
# Pass both environment variables and arguments
|
|
29
|
+
npx @modelcontextprotocol/inspector -e key=value -e key2=$VALUE2 node build/index.js arg1 arg2
|
|
30
|
+
|
|
31
|
+
# Use -- to separate inspector flags from server arguments
|
|
32
|
+
npx @modelcontextprotocol/inspector -e key=$VALUE -- node build/index.js -e server-flag
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The inspector runs both an MCP Inspector (MCPI) client UI (default port 6274) and an MCP Proxy (MCPP) server (default port 6277). Open the MCPI client UI in your browser to use the inspector. (These ports are derived from the T9 dialpad mapping of MCPI and MCPP respectively, as a mnemonic). You can customize the ports if needed:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
CLIENT_PORT=8080 SERVER_PORT=9000 npx @modelcontextprotocol/inspector node build/index.js
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
For more details on ways to use the inspector, see the [Inspector section of the MCP docs site](https://modelcontextprotocol.io/docs/tools/inspector). For help with debugging, see the [Debugging guide](https://modelcontextprotocol.io/docs/tools/debugging).
|
|
42
|
+
|
|
43
|
+
### Servers File Export
|
|
44
|
+
|
|
45
|
+
The MCP Inspector provides convenient buttons to export server launch configurations for use in clients such as Cursor, Claude Code, or the Inspector's CLI. The file is usually called `mcp.json`.
|
|
46
|
+
|
|
47
|
+
- **Server Entry** - Copies a single server configuration entry to your clipboard. This can be added to your `mcp.json` file inside the `mcpServers` object with your preferred server name.
|
|
48
|
+
|
|
49
|
+
**STDIO transport example:**
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"command": "node",
|
|
54
|
+
"args": ["build/index.js", "--debug"],
|
|
55
|
+
"env": {
|
|
56
|
+
"API_KEY": "your-api-key",
|
|
57
|
+
"DEBUG": "true"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**SSE transport example:**
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"type": "sse",
|
|
67
|
+
"url": "http://localhost:3000/events",
|
|
68
|
+
"note": "For SSE connections, add this URL directly in Client"
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
- **Servers File** - Copies a complete MCP configuration file structure to your clipboard, with your current server configuration added as `default-server`. This can be saved directly as `mcp.json`.
|
|
73
|
+
|
|
74
|
+
**STDIO transport example:**
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"mcpServers": {
|
|
79
|
+
"default-server": {
|
|
80
|
+
"command": "node",
|
|
81
|
+
"args": ["build/index.js", "--debug"],
|
|
82
|
+
"env": {
|
|
83
|
+
"API_KEY": "your-api-key",
|
|
84
|
+
"DEBUG": "true"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**SSE transport example:**
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"mcpServers": {
|
|
96
|
+
"default-server": {
|
|
97
|
+
"type": "sse",
|
|
98
|
+
"url": "http://localhost:3000/events",
|
|
99
|
+
"note": "For SSE connections, add this URL directly in Client"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
These buttons appear in the Inspector UI after you've configured your server settings, making it easy to save and reuse your configurations.
|
|
106
|
+
|
|
107
|
+
For SSE transport connections, the Inspector provides similar functionality for both buttons. The "Server Entry" button copies the SSE URL configuration that can be added to your existing configuration file, while the "Servers File" button creates a complete configuration file containing the SSE URL for direct use in clients.
|
|
108
|
+
|
|
109
|
+
You can paste the Server Entry into your existing `mcp.json` file under your chosen server name, or use the complete Servers File payload to create a new configuration file.
|
|
110
|
+
|
|
111
|
+
### Authentication
|
|
112
|
+
|
|
113
|
+
The inspector supports bearer token authentication for SSE connections. Enter your token in the UI when connecting to an MCP server, and it will be sent in the Authorization header. You can override the header name using the input field in the sidebar.
|
|
114
|
+
|
|
115
|
+
### Security Considerations
|
|
116
|
+
|
|
117
|
+
The MCP Inspector includes a proxy server that can run and communicate with local MCP processes. The proxy server should not be exposed to untrusted networks as it has permissions to spawn local processes and can connect to any specified MCP server.
|
|
118
|
+
|
|
119
|
+
### Configuration
|
|
120
|
+
|
|
121
|
+
The MCP Inspector supports the following configuration settings. To change them, click on the `Configuration` button in the MCP Inspector UI:
|
|
122
|
+
|
|
123
|
+
| Setting | Description | Default |
|
|
124
|
+
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ------- |
|
|
125
|
+
| `MCP_SERVER_REQUEST_TIMEOUT` | Timeout for requests to the MCP server (ms) | 10000 |
|
|
126
|
+
| `MCP_REQUEST_TIMEOUT_RESET_ON_PROGRESS` | Reset timeout on progress notifications | true |
|
|
127
|
+
| `MCP_REQUEST_MAX_TOTAL_TIMEOUT` | Maximum total timeout for requests sent to the MCP server (ms) (Use with progress notifications) | 60000 |
|
|
128
|
+
| `MCP_PROXY_FULL_ADDRESS` | Set this if you are running the MCP Inspector Proxy on a non-default address. Example: http://10.1.1.22:5577 | "" |
|
|
129
|
+
| `MCP_AUTO_OPEN_ENABLED` | Enable automatic browser opening when inspector starts. Only as environment var, not configurable in browser. | true |
|
|
130
|
+
|
|
131
|
+
These settings can be adjusted in real-time through the UI and will persist across sessions.
|
|
132
|
+
|
|
133
|
+
The inspector also supports configuration files to store settings for different MCP servers. This is useful when working with multiple servers or complex configurations:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
npx @modelcontextprotocol/inspector --config path/to/config.json --server everything
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Example server configuration file:
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"mcpServers": {
|
|
144
|
+
"everything": {
|
|
145
|
+
"command": "npx",
|
|
146
|
+
"args": ["@modelcontextprotocol/server-everything"],
|
|
147
|
+
"env": {
|
|
148
|
+
"hello": "Hello MCP!"
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
"my-server": {
|
|
152
|
+
"command": "node",
|
|
153
|
+
"args": ["build/index.js", "arg1", "arg2"],
|
|
154
|
+
"env": {
|
|
155
|
+
"key": "value",
|
|
156
|
+
"key2": "value2"
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
> **Tip:** You can easily generate this configuration format using the **Server Entry** and **Servers File** buttons in the Inspector UI, as described in the Servers File Export section above.
|
|
164
|
+
|
|
165
|
+
You can also set the initial `transport` type, `serverUrl`, `serverCommand`, and `serverArgs` via query params, for example:
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
http://localhost:6274/?transport=sse&serverUrl=http://localhost:8787/sse
|
|
169
|
+
http://localhost:6274/?transport=streamable-http&serverUrl=http://localhost:8787/mcp
|
|
170
|
+
http://localhost:6274/?transport=stdio&serverCommand=npx&serverArgs=arg1%20arg2
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
You can also set initial config settings via query params, for example:
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
http://localhost:6274/?MCP_SERVER_REQUEST_TIMEOUT=10000&MCP_REQUEST_TIMEOUT_RESET_ON_PROGRESS=false&MCP_PROXY_FULL_ADDRESS=http://10.1.1.22:5577
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Note that if both the query param and the corresponding localStorage item are set, the query param will take precedence.
|
|
180
|
+
|
|
181
|
+
### From this repository
|
|
182
|
+
|
|
183
|
+
If you're working on the inspector itself:
|
|
184
|
+
|
|
185
|
+
Development mode:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
npm run dev
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
> **Note for Windows users:**
|
|
192
|
+
> On Windows, use the following command instead:
|
|
193
|
+
>
|
|
194
|
+
> ```bash
|
|
195
|
+
> npm run dev:windows
|
|
196
|
+
> ```
|
|
197
|
+
|
|
198
|
+
Production mode:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
npm run build
|
|
202
|
+
npm start
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### CLI Mode
|
|
206
|
+
|
|
207
|
+
CLI mode enables programmatic interaction with MCP servers from the command line, ideal for scripting, automation, and integration with coding assistants. This creates an efficient feedback loop for MCP server development.
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
npx @modelcontextprotocol/inspector --cli node build/index.js
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
The CLI mode supports most operations across tools, resources, and prompts. A few examples:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
# Basic usage
|
|
217
|
+
npx @modelcontextprotocol/inspector --cli node build/index.js
|
|
218
|
+
|
|
219
|
+
# With config file
|
|
220
|
+
npx @modelcontextprotocol/inspector --cli --config path/to/config.json --server myserver
|
|
221
|
+
|
|
222
|
+
# List available tools
|
|
223
|
+
npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/list
|
|
224
|
+
|
|
225
|
+
# Call a specific tool
|
|
226
|
+
npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/call --tool-name mytool --tool-arg key=value --tool-arg another=value2
|
|
227
|
+
|
|
228
|
+
# List available resources
|
|
229
|
+
npx @modelcontextprotocol/inspector --cli node build/index.js --method resources/list
|
|
230
|
+
|
|
231
|
+
# List available prompts
|
|
232
|
+
npx @modelcontextprotocol/inspector --cli node build/index.js --method prompts/list
|
|
233
|
+
|
|
234
|
+
# Connect to a remote MCP server
|
|
235
|
+
npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com
|
|
236
|
+
|
|
237
|
+
# Call a tool on a remote server
|
|
238
|
+
npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --method tools/call --tool-name remotetool --tool-arg param=value
|
|
239
|
+
|
|
240
|
+
# List resources from a remote server
|
|
241
|
+
npx @modelcontextprotocol/inspector --cli https://my-mcp-server.example.com --method resources/list
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### UI Mode vs CLI Mode: When to Use Each
|
|
245
|
+
|
|
246
|
+
| Use Case | UI Mode | CLI Mode |
|
|
247
|
+
| ------------------------ | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
248
|
+
| **Server Development** | Visual interface for interactive testing and debugging during development | Scriptable commands for quick testing and continuous integration; creates feedback loops with AI coding assistants like Cursor for rapid development |
|
|
249
|
+
| **Resource Exploration** | Interactive browser with hierarchical navigation and JSON visualization | Programmatic listing and reading for automation and scripting |
|
|
250
|
+
| **Tool Testing** | Form-based parameter input with real-time response visualization | Command-line tool execution with JSON output for scripting |
|
|
251
|
+
| **Prompt Engineering** | Interactive sampling with streaming responses and visual comparison | Batch processing of prompts with machine-readable output |
|
|
252
|
+
| **Debugging** | Request history, visualized errors, and real-time notifications | Direct JSON output for log analysis and integration with other tools |
|
|
253
|
+
| **Automation** | N/A | Ideal for CI/CD pipelines, batch processing, and integration with coding assistants |
|
|
254
|
+
| **Learning MCP** | Rich visual interface helps new users understand server capabilities | Simplified commands for focused learning of specific endpoints |
|
|
255
|
+
|
|
256
|
+
## License
|
|
257
|
+
|
|
258
|
+
This project is licensed under the MIT License—see the [LICENSE](LICENSE) file for details.
|
package/cli/build/cli.js
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { dirname, resolve } from "path";
|
|
6
|
+
import { spawnPromise } from "spawn-rx";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
function handleError(error) {
|
|
10
|
+
let message;
|
|
11
|
+
if (error instanceof Error) {
|
|
12
|
+
message = error.message;
|
|
13
|
+
}
|
|
14
|
+
else if (typeof error === "string") {
|
|
15
|
+
message = error;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
message = "Unknown error";
|
|
19
|
+
}
|
|
20
|
+
console.error(message);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
function delay(ms) {
|
|
24
|
+
return new Promise((resolve) => setTimeout(resolve, ms, true));
|
|
25
|
+
}
|
|
26
|
+
async function runWebClient(args) {
|
|
27
|
+
const inspectorServerPath = resolve(__dirname, "../../", "server", "build", "index.js");
|
|
28
|
+
// Path to the client entry point
|
|
29
|
+
const inspectorClientPath = resolve(__dirname, "../../", "client", "bin", "client.js");
|
|
30
|
+
const CLIENT_PORT = process.env.CLIENT_PORT ?? "6274";
|
|
31
|
+
const SERVER_PORT = process.env.SERVER_PORT ?? "6277";
|
|
32
|
+
console.log("Starting MCP inspector...");
|
|
33
|
+
const abort = new AbortController();
|
|
34
|
+
let cancelled = false;
|
|
35
|
+
process.on("SIGINT", () => {
|
|
36
|
+
cancelled = true;
|
|
37
|
+
abort.abort();
|
|
38
|
+
});
|
|
39
|
+
let server;
|
|
40
|
+
let serverOk;
|
|
41
|
+
try {
|
|
42
|
+
server = spawnPromise("node", [
|
|
43
|
+
inspectorServerPath,
|
|
44
|
+
...(args.command ? [`--env`, args.command] : []),
|
|
45
|
+
...(args.args ? [`--args=${args.args.join(" ")}`] : []),
|
|
46
|
+
], {
|
|
47
|
+
env: {
|
|
48
|
+
...process.env,
|
|
49
|
+
PORT: SERVER_PORT,
|
|
50
|
+
MCP_ENV_VARS: JSON.stringify(args.envArgs),
|
|
51
|
+
},
|
|
52
|
+
signal: abort.signal,
|
|
53
|
+
echoOutput: true,
|
|
54
|
+
});
|
|
55
|
+
// Make sure server started before starting client
|
|
56
|
+
serverOk = await Promise.race([server, delay(2 * 1000)]);
|
|
57
|
+
}
|
|
58
|
+
catch (error) { }
|
|
59
|
+
if (serverOk) {
|
|
60
|
+
try {
|
|
61
|
+
await spawnPromise("node", [inspectorClientPath], {
|
|
62
|
+
env: { ...process.env, PORT: CLIENT_PORT },
|
|
63
|
+
signal: abort.signal,
|
|
64
|
+
echoOutput: true,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
if (!cancelled || process.env.DEBUG)
|
|
69
|
+
throw e;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function runCli(args) {
|
|
74
|
+
const projectRoot = resolve(__dirname, "..");
|
|
75
|
+
const cliPath = resolve(projectRoot, "build", "index.js");
|
|
76
|
+
const abort = new AbortController();
|
|
77
|
+
let cancelled = false;
|
|
78
|
+
process.on("SIGINT", () => {
|
|
79
|
+
cancelled = true;
|
|
80
|
+
abort.abort();
|
|
81
|
+
});
|
|
82
|
+
try {
|
|
83
|
+
await spawnPromise("node", [cliPath, args.command, ...args.args], {
|
|
84
|
+
env: { ...process.env, ...args.envArgs },
|
|
85
|
+
signal: abort.signal,
|
|
86
|
+
echoOutput: true,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
if (!cancelled || process.env.DEBUG) {
|
|
91
|
+
throw e;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function loadConfigFile(configPath, serverName) {
|
|
96
|
+
try {
|
|
97
|
+
const resolvedConfigPath = path.isAbsolute(configPath)
|
|
98
|
+
? configPath
|
|
99
|
+
: path.resolve(process.cwd(), configPath);
|
|
100
|
+
if (!fs.existsSync(resolvedConfigPath)) {
|
|
101
|
+
throw new Error(`Config file not found: ${resolvedConfigPath}`);
|
|
102
|
+
}
|
|
103
|
+
const configContent = fs.readFileSync(resolvedConfigPath, "utf8");
|
|
104
|
+
const parsedConfig = JSON.parse(configContent);
|
|
105
|
+
if (!parsedConfig.mcpServers || !parsedConfig.mcpServers[serverName]) {
|
|
106
|
+
const availableServers = Object.keys(parsedConfig.mcpServers || {}).join(", ");
|
|
107
|
+
throw new Error(`Server '${serverName}' not found in config file. Available servers: ${availableServers}`);
|
|
108
|
+
}
|
|
109
|
+
const serverConfig = parsedConfig.mcpServers[serverName];
|
|
110
|
+
return serverConfig;
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
if (err instanceof SyntaxError) {
|
|
114
|
+
throw new Error(`Invalid JSON in config file: ${err.message}`);
|
|
115
|
+
}
|
|
116
|
+
throw err;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function parseKeyValuePair(value, previous = {}) {
|
|
120
|
+
const parts = value.split("=");
|
|
121
|
+
const key = parts[0];
|
|
122
|
+
const val = parts.slice(1).join("=");
|
|
123
|
+
if (val === undefined || val === "") {
|
|
124
|
+
throw new Error(`Invalid parameter format: ${value}. Use key=value format.`);
|
|
125
|
+
}
|
|
126
|
+
return { ...previous, [key]: val };
|
|
127
|
+
}
|
|
128
|
+
function parseArgs() {
|
|
129
|
+
const program = new Command();
|
|
130
|
+
const argSeparatorIndex = process.argv.indexOf("--");
|
|
131
|
+
let preArgs = process.argv;
|
|
132
|
+
let postArgs = [];
|
|
133
|
+
if (argSeparatorIndex !== -1) {
|
|
134
|
+
preArgs = process.argv.slice(0, argSeparatorIndex);
|
|
135
|
+
postArgs = process.argv.slice(argSeparatorIndex + 1);
|
|
136
|
+
}
|
|
137
|
+
program
|
|
138
|
+
.name("inspector-bin")
|
|
139
|
+
.allowExcessArguments()
|
|
140
|
+
.allowUnknownOption()
|
|
141
|
+
.option("-e <env>", "environment variables in KEY=VALUE format", parseKeyValuePair, {})
|
|
142
|
+
.option("--config <path>", "config file path")
|
|
143
|
+
.option("--server <n>", "server name from config file")
|
|
144
|
+
.option("--cli", "enable CLI mode");
|
|
145
|
+
// Parse only the arguments before --
|
|
146
|
+
program.parse(preArgs);
|
|
147
|
+
const options = program.opts();
|
|
148
|
+
const remainingArgs = program.args;
|
|
149
|
+
// Add back any arguments that came after --
|
|
150
|
+
const finalArgs = [...remainingArgs, ...postArgs];
|
|
151
|
+
// Validate that config and server are provided together
|
|
152
|
+
if ((options.config && !options.server) ||
|
|
153
|
+
(!options.config && options.server)) {
|
|
154
|
+
throw new Error("Both --config and --server must be provided together. If you specify one, you must specify the other.");
|
|
155
|
+
}
|
|
156
|
+
// If config file is specified, load and use the options from the file. We must merge the args
|
|
157
|
+
// from the command line and the file together, or we will miss the method options (--method,
|
|
158
|
+
// etc.)
|
|
159
|
+
if (options.config && options.server) {
|
|
160
|
+
const config = loadConfigFile(options.config, options.server);
|
|
161
|
+
return {
|
|
162
|
+
command: config.command,
|
|
163
|
+
args: [...(config.args || []), ...finalArgs],
|
|
164
|
+
envArgs: { ...(config.env || {}), ...(options.e || {}) },
|
|
165
|
+
cli: options.cli || false,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
// Otherwise use command line arguments
|
|
169
|
+
const command = finalArgs[0] || "";
|
|
170
|
+
const args = finalArgs.slice(1);
|
|
171
|
+
return {
|
|
172
|
+
command,
|
|
173
|
+
args,
|
|
174
|
+
envArgs: options.e || {},
|
|
175
|
+
cli: options.cli || false,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
async function main() {
|
|
179
|
+
process.on("uncaughtException", (error) => {
|
|
180
|
+
handleError(error);
|
|
181
|
+
});
|
|
182
|
+
try {
|
|
183
|
+
const args = parseArgs();
|
|
184
|
+
if (args.cli) {
|
|
185
|
+
runCli(args);
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
await runWebClient(args);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
handleError(error);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
main();
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const validLogLevels = [
|
|
2
|
+
"trace",
|
|
3
|
+
"debug",
|
|
4
|
+
"info",
|
|
5
|
+
"warn",
|
|
6
|
+
"error",
|
|
7
|
+
];
|
|
8
|
+
export async function connect(client, transport) {
|
|
9
|
+
try {
|
|
10
|
+
await client.connect(transport);
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
throw new Error(`Failed to connect to MCP server: ${error instanceof Error ? error.message : String(error)}`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export async function disconnect(transport) {
|
|
17
|
+
try {
|
|
18
|
+
await transport.close();
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
throw new Error(`Failed to disconnect from MCP server: ${error instanceof Error ? error.message : String(error)}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Set logging level
|
|
25
|
+
export async function setLoggingLevel(client, level) {
|
|
26
|
+
try {
|
|
27
|
+
const response = await client.setLoggingLevel(level);
|
|
28
|
+
return response;
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
throw new Error(`Failed to set logging level: ${error instanceof Error ? error.message : String(error)}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// List available prompts
|
|
2
|
+
export async function listPrompts(client) {
|
|
3
|
+
try {
|
|
4
|
+
const response = await client.listPrompts();
|
|
5
|
+
return response;
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
throw new Error(`Failed to list prompts: ${error instanceof Error ? error.message : String(error)}`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
// Get a prompt
|
|
12
|
+
export async function getPrompt(client, name, args) {
|
|
13
|
+
try {
|
|
14
|
+
const response = await client.getPrompt({
|
|
15
|
+
name,
|
|
16
|
+
arguments: args || {},
|
|
17
|
+
});
|
|
18
|
+
return response;
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
throw new Error(`Failed to get prompt: ${error instanceof Error ? error.message : String(error)}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// List available resources
|
|
2
|
+
export async function listResources(client) {
|
|
3
|
+
try {
|
|
4
|
+
const response = await client.listResources();
|
|
5
|
+
return response;
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
throw new Error(`Failed to list resources: ${error instanceof Error ? error.message : String(error)}`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
// Read a resource
|
|
12
|
+
export async function readResource(client, uri) {
|
|
13
|
+
try {
|
|
14
|
+
const response = await client.readResource({ uri });
|
|
15
|
+
return response;
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
throw new Error(`Failed to read resource ${uri}: ${error instanceof Error ? error.message : String(error)}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// List resource templates
|
|
22
|
+
export async function listResourceTemplates(client) {
|
|
23
|
+
try {
|
|
24
|
+
const response = await client.listResourceTemplates();
|
|
25
|
+
return response;
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
throw new Error(`Failed to list resource templates: ${error instanceof Error ? error.message : String(error)}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export async function listTools(client) {
|
|
2
|
+
try {
|
|
3
|
+
const response = await client.listTools();
|
|
4
|
+
return response;
|
|
5
|
+
}
|
|
6
|
+
catch (error) {
|
|
7
|
+
throw new Error(`Failed to list tools: ${error instanceof Error ? error.message : String(error)}`);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function convertParameterValue(value, schema) {
|
|
11
|
+
if (!value) {
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
if (schema.type === "number" || schema.type === "integer") {
|
|
15
|
+
return Number(value);
|
|
16
|
+
}
|
|
17
|
+
if (schema.type === "boolean") {
|
|
18
|
+
return value.toLowerCase() === "true";
|
|
19
|
+
}
|
|
20
|
+
if (schema.type === "object" || schema.type === "array") {
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(value);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
function convertParameters(tool, params) {
|
|
31
|
+
const result = {};
|
|
32
|
+
const properties = tool.inputSchema.properties || {};
|
|
33
|
+
for (const [key, value] of Object.entries(params)) {
|
|
34
|
+
const paramSchema = properties[key];
|
|
35
|
+
if (paramSchema) {
|
|
36
|
+
result[key] = convertParameterValue(value, paramSchema);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
// If no schema is found for this parameter, keep it as string
|
|
40
|
+
result[key] = value;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
export async function callTool(client, name, args) {
|
|
46
|
+
try {
|
|
47
|
+
const toolsResponse = await listTools(client);
|
|
48
|
+
const tools = toolsResponse.tools;
|
|
49
|
+
const tool = tools.find((t) => t.name === name);
|
|
50
|
+
let convertedArgs = args;
|
|
51
|
+
if (tool) {
|
|
52
|
+
// Convert parameters based on the tool's schema
|
|
53
|
+
convertedArgs = convertParameters(tool, args);
|
|
54
|
+
}
|
|
55
|
+
const response = await client.callTool({
|
|
56
|
+
name: name,
|
|
57
|
+
arguments: convertedArgs,
|
|
58
|
+
});
|
|
59
|
+
return response;
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
throw new Error(`Failed to call tool ${name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
function formatError(error) {
|
|
2
|
+
let message;
|
|
3
|
+
if (error instanceof Error) {
|
|
4
|
+
message = error.message;
|
|
5
|
+
}
|
|
6
|
+
else if (typeof error === "string") {
|
|
7
|
+
message = error;
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
message = "Unknown error";
|
|
11
|
+
}
|
|
12
|
+
return message;
|
|
13
|
+
}
|
|
14
|
+
export function handleError(error) {
|
|
15
|
+
const errorMessage = formatError(error);
|
|
16
|
+
console.error(errorMessage);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|