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().
44plugin ( "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
1989function 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