Skip to content

Commit 9c2b0fc

Browse files
committed
docs: update README and MCP Tester to include support for HTTP SSE and enhanced configuration options
1 parent 17fdd78 commit 9c2b0fc

2 files changed

Lines changed: 149 additions & 32 deletions

File tree

README.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -273,14 +273,20 @@ For HTTP remote MCP servers:
273273
mini-a mcptest=true mcp="(type: remote, url: 'http://localhost:9090/mcp')"
274274
```
275275

276+
For SSE-based MCP servers:
277+
```bash
278+
mini-a mcptest=true mcp="(type: sse, url: 'http://localhost:9090/mcp')"
279+
```
280+
276281
### MCP Tester Features
277282

278283
The interactive tester provides:
279284

280-
- **Connection Management** - Connect to both STDIO (local command) and HTTP Remote MCP servers
285+
- **Connection Management** - Connect to STDIO, HTTP Remote, HTTP SSE, oJob, dummy, or raw `$mcp(...)` configurations
281286
- **Tool Discovery** - List all available tools from the connected MCP server
282287
- **Tool Inspection** - View detailed information about tool parameters, types, and descriptions
283288
- **Interactive Tool Calling** - Call any MCP tool with custom parameters through guided prompts
289+
- **Advanced Config Support** - Merge extra `$mcp` options such as `shared`, `clientInfo`, `auth`, `strict`, `blacklist`, or future transport flags via JSSLON/JSON
284290
- **Configuration Options** - Adjust settings like debug mode, tool selection display size, and result parsing
285291
- **Library Loading** - Load additional OpenAF libraries for extended functionality using `libs=` parameter
286292

@@ -297,11 +303,11 @@ The interactive tester provides:
297303
mini-a mcptest=true
298304

299305
# 1. Choose "New connection"
300-
# 2. Select "STDIO (local command)"
301-
# 3. Enter: ojob mcps/mcp-time.yaml
302-
# 4. Choose "List tools" to see available tools
303-
# 5. Choose "Call a tool" to test a specific tool
304-
# 6. Follow the prompts to enter parameters
306+
# 2. Select "HTTP SSE" or "Raw $mcp config" when you need newer transport/options support
307+
# 3. Enter the URL or the full JSSLON config
308+
# 4. Optionally merge extra $mcp options such as "(shared: true, clientInfo: (name: 'Mini-A MCP Tester'))"
309+
# 5. Choose "List tools" to see available tools
310+
# 6. Choose "Call a tool" to test a specific tool
305311
```
306312

307313
The tester includes automatic cleanup with shutdown handlers to properly close MCP connections when exiting.

mini-a-mcptest.js

Lines changed: 137 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Mini-A MCP Tester
2-
// Provides an interactive console workflow to test MCP servers (STDIO or HTTP remote)
3-
// and call their tools with custom parameters
2+
// Provides an interactive console workflow to test MCP servers across
3+
// the transports and config shapes supported by OpenAF's $mcp().
44
plugin("Console")
55

66
// Get command line arguments
@@ -15,14 +15,92 @@ if (isDef(args.libs) && args.libs.length > 0) {
1515
__miniALoadLibraries(args.libs, log, logErr)
1616
}
1717

18+
function parseOptionalJSSLON(label, rawValue, expectedType) {
19+
if (isUnDef(rawValue) || String(rawValue).trim() == "") return __
20+
21+
var parsed = af.fromJSSLON(String(rawValue).trim())
22+
if (expectedType == "map" && !isMap(parsed)) {
23+
throw new Error(label + " must be a map/object.")
24+
}
25+
if (expectedType == "array" && !isArray(parsed)) {
26+
throw new Error(label + " must be an array.")
27+
}
28+
return parsed
29+
}
30+
31+
function summarizeMCPConfig(config) {
32+
if (!isMap(config)) return "(invalid config)"
33+
34+
var type = String(config.type || (isDef(config.cmd) ? "stdio" : "remote")).toLowerCase()
35+
if (type == "http") type = "remote"
36+
if (type == "remote" && config.sse === true) type = "sse"
37+
38+
if (type == "stdio") return "stdio: " + String(config.cmd || "(missing cmd)")
39+
if (type == "ojob") {
40+
var jobName = isMap(config.options) && isDef(config.options.job) ? String(config.options.job) : "(missing job)"
41+
return "ojob: " + jobName
42+
}
43+
if (type == "dummy") return "dummy"
44+
45+
var target = isDef(config.url) ? String(config.url) : "(missing url)"
46+
return type + ": " + target
47+
}
48+
49+
function normalizeMCPConfigInput(rawConfig) {
50+
var config = rawConfig
51+
52+
if (isString(config)) config = af.fromJSSLON(config)
53+
54+
if (isArray(config)) {
55+
if (config.length == 0) throw new Error("No MCP configuration entries were provided.")
56+
if (config.length == 1) return config[0]
57+
58+
var choices = config.map((cfg, idx) => "[" + (idx + 1) + "] " + summarizeMCPConfig(cfg)).concat(["🔙 Cancel"])
59+
var chosen = askChoose("Select which MCP configuration to test: ", choices, Math.min(choices.length, 10))
60+
if (chosen >= config.length) return __
61+
return config[chosen]
62+
}
63+
64+
if (!isMap(config)) {
65+
throw new Error("MCP configuration should be an object/map or an array of objects/maps.")
66+
}
67+
68+
return config
69+
}
70+
71+
function enrichMCPConfig(config) {
72+
var _config = clone(config)
73+
74+
var _timeout = ask("Enter timeout in milliseconds (leave blank for default 30000): ")
75+
if (_timeout != "" && !isNaN(_timeout)) {
76+
_config.timeout = Number(_timeout)
77+
}
78+
79+
var _extra = ask("Extra $mcp options as JSSLON/JSON (blank to skip, e.g. \"(shared: true, clientInfo: (name: 'Mini-A MCP Tester', version: '1.0.0'))\"): ")
80+
if (isDef(_extra) && _extra.trim() != "") {
81+
var _extraParsed = parseOptionalJSSLON("Extra $mcp options", _extra, "map")
82+
_config = merge(_config, _extraParsed)
83+
}
84+
85+
return _config
86+
}
87+
1888
// Create MCP connection configuration
1989
function buildMCPConfig(args) {
2090
var _config = {}
2191

2292
print()
23-
var _connectionType = askChoose("Choose MCP connection type: ", ["STDIO (local command)", "HTTP Remote", "🔙 Cancel"])
24-
25-
if (_connectionType == 2) {
93+
var _connectionType = askChoose("Choose MCP connection type: ", [
94+
"STDIO (local command)",
95+
"HTTP Remote",
96+
"HTTP SSE",
97+
"oJob MCP",
98+
"Dummy MCP",
99+
"Raw $mcp config",
100+
"🔙 Cancel"
101+
])
102+
103+
if (_connectionType == 6) {
26104
return null
27105
}
28106

@@ -34,12 +112,6 @@ function buildMCPConfig(args) {
34112
return null
35113
}
36114
_config.cmd = _cmd.trim()
37-
38-
// Optional timeout
39-
var _timeout = ask("Enter timeout in milliseconds (leave blank for default 30000): ")
40-
if (_timeout != "" && !isNaN(_timeout)) {
41-
_config.timeout = Number(_timeout)
42-
}
43115
} else if (_connectionType == 1) {
44116
// HTTP Remote connection
45117
var _url = ask("Enter the MCP HTTP URL (e.g., 'http://localhost:9090/mcp'): ")
@@ -49,15 +121,59 @@ function buildMCPConfig(args) {
49121
}
50122
_config.type = "remote"
51123
_config.url = _url.trim()
124+
} else if (_connectionType == 2) {
125+
var _sseUrl = ask("Enter the MCP SSE URL (e.g., 'http://localhost:9090/mcp'): ")
126+
if (isUnDef(_sseUrl) || _sseUrl.trim() == "") {
127+
print(ansiColor("ITALIC,FG(196)", "!!") + ansiColor("FG(196)", " URL cannot be empty."))
128+
return null
129+
}
130+
_config.type = "sse"
131+
_config.sse = true
132+
_config.url = _sseUrl.trim()
133+
} else if (_connectionType == 3) {
134+
_config.type = "ojob"
135+
_config.options = {}
136+
137+
var _job = ask("Enter the oJob file path (e.g., 'mcps/mcp-time.yaml'): ")
138+
if (isUnDef(_job) || _job.trim() == "") {
139+
print(ansiColor("ITALIC,FG(196)", "!!") + ansiColor("FG(196)", " Job path cannot be empty."))
140+
return null
141+
}
142+
_config.options.job = _job.trim()
143+
144+
var _jobArgs = ask("Optional oJob args as JSSLON/JSON map (blank to skip): ")
145+
if (isDef(_jobArgs) && _jobArgs.trim() != "") {
146+
_config.options.args = parseOptionalJSSLON("oJob args", _jobArgs, "map")
147+
}
52148

53-
// Optional timeout
54-
var _timeout = ask("Enter timeout in milliseconds (leave blank for default 30000): ")
55-
if (_timeout != "" && !isNaN(_timeout)) {
56-
_config.timeout = Number(_timeout)
149+
var _jobInit = ask("Optional oJob init entry or array as JSSLON/JSON (blank to skip): ")
150+
if (isDef(_jobInit) && _jobInit.trim() != "") {
151+
var _parsedInit = parseOptionalJSSLON("oJob init", _jobInit)
152+
_config.options.init = _parsedInit
57153
}
154+
} else if (_connectionType == 4) {
155+
_config.type = "dummy"
156+
_config.options = {}
157+
158+
var _dummyFns = ask("Dummy MCP functions as JSSLON/JSON map (blank for empty): ")
159+
if (isDef(_dummyFns) && _dummyFns.trim() != "") {
160+
_config.options.fns = parseOptionalJSSLON("Dummy functions", _dummyFns, "map")
161+
}
162+
163+
var _dummyMeta = ask("Dummy MCP metadata as JSSLON/JSON map (blank for empty): ")
164+
if (isDef(_dummyMeta) && _dummyMeta.trim() != "") {
165+
_config.options.fnsMeta = parseOptionalJSSLON("Dummy metadata", _dummyMeta, "map")
166+
}
167+
} else if (_connectionType == 5) {
168+
var _raw = ask("Enter the full $mcp config as JSSLON/JSON: ")
169+
if (isUnDef(_raw) || _raw.trim() == "") {
170+
print(ansiColor("ITALIC,FG(196)", "!!") + ansiColor("FG(196)", " Config cannot be empty."))
171+
return null
172+
}
173+
_config = normalizeMCPConfigInput(_raw)
58174
}
59175

60-
return _config
176+
return enrichMCPConfig(_config)
61177
}
62178

63179
// Helper function to print elapsed time
@@ -414,7 +530,7 @@ function mainMCPTest(args) {
414530
} else {
415531
_currentConfig.debug = false
416532
}
417-
print("🔌 Connecting to MCP server from mcp= parameter...")
533+
print("🔌 Connecting to MCP server: " + summarizeMCPConfig(_currentConfig))
418534
_mcpClient = $mcp(_currentConfig)
419535
var startTime = now()
420536
_mcpClient.initialize()
@@ -460,7 +576,7 @@ function mainMCPTest(args) {
460576
} else {
461577
_currentConfig.debug = false
462578
}
463-
print("🔌 Connecting to MCP server from mcp= parameter...")
579+
print("🔌 Connecting to MCP server: " + summarizeMCPConfig(_currentConfig))
464580
_mcpClient = $mcp(_currentConfig)
465581
var startTime = now()
466582
_mcpClient.initialize()
@@ -524,20 +640,15 @@ function mainMCPTest(args) {
524640
if (isDef(args.mcp)) {
525641
var config = __
526642
try {
527-
// Parse mcp parameter (can be SLON/JSON string or object)
528-
if (isString(args.mcp)) {
529-
config = af.fromJSSLON(args.mcp)
530-
} else if (isMap(args.mcp)) {
531-
config = args.mcp
532-
}
643+
config = normalizeMCPConfigInput(args.mcp)
533644

534645
if (isDef(config)) {
535646
if (sessionOptions.debug) {
536647
config.debug = true
537648
} else {
538649
config.debug = false
539650
}
540-
print("🔌 Connecting to MCP server from mcp= parameter...")
651+
print("🔌 Connecting to MCP server from mcp= parameter: " + summarizeMCPConfig(config))
541652
_mcpClient = $mcp(config)
542653
var startTime = now()
543654
_mcpClient.initialize()
@@ -605,7 +716,7 @@ function mainMCPTest(args) {
605716
var config = buildMCPConfig(args)
606717
if (isDef(config)) {
607718
config = merge(config, { debug: sessionOptions.debug })
608-
print("\n🔌 Connecting to MCP server...")
719+
print("\n🔌 Connecting to MCP server: " + summarizeMCPConfig(config))
609720
try {
610721
_mcpClient = $mcp(config)
611722
var startTime = now()

0 commit comments

Comments
 (0)