@@ -4,9 +4,50 @@ import {
44 BailianError ,
55 bailianMcpPath ,
66 detectOutputFormat ,
7+ type FlagsDef ,
8+ type ParsedFlags ,
79} from "bailian-cli-core" ;
810import { emitResult } from "bailian-cli-runtime" ;
911
12+ const CALL_FLAGS = {
13+ target : {
14+ type : "string" ,
15+ valueHint : "<server.tool>" ,
16+ description :
17+ "Server code and tool name joined by a dot, e.g. market-cmapi00073529.SmartStockSelection" ,
18+ required : true ,
19+ } ,
20+ arg : {
21+ type : "array" ,
22+ valueHint : "<kv>" ,
23+ description : "Tool argument (repeatable). Values parsed as JSON if possible, else string." ,
24+ } ,
25+ json : {
26+ type : "string" ,
27+ valueHint : "<obj>" ,
28+ description : "Full arguments object as JSON; merged with --arg (arg wins)." ,
29+ } ,
30+ query : {
31+ type : "string" ,
32+ valueHint : "<text>" ,
33+ description : "Shortcut for --arg query=<text> (mirrors many DashScope MCP tools)." ,
34+ } ,
35+ url : {
36+ type : "string" ,
37+ valueHint : "<url>" ,
38+ description : "Override the MCP endpoint URL (for non-Bailian servers)" ,
39+ } ,
40+ } satisfies FlagsDef ;
41+ type CallFlags = ParsedFlags < typeof CALL_FLAGS > ;
42+
43+ function parseTarget ( target : string ) : { serverCode : string ; toolName : string } {
44+ const dot = target . indexOf ( "." ) ;
45+ if ( dot <= 0 || dot === target . length - 1 ) {
46+ throw new UsageError ( `target must be <server-code>.<tool>, got "${ target } ".` ) ;
47+ }
48+ return { serverCode : target . slice ( 0 , dot ) , toolName : target . slice ( dot + 1 ) } ;
49+ }
50+
1051function parseArgFlags ( raw : string [ ] ) : Record < string , unknown > {
1152 const out : Record < string , unknown > = { } ;
1253 for ( const item of raw ) {
@@ -25,70 +66,52 @@ function parseArgFlags(raw: string[]): Record<string, unknown> {
2566 return out ;
2667}
2768
69+ function parseJsonArg ( raw : string ) : Record < string , unknown > {
70+ let parsed : unknown ;
71+ try {
72+ parsed = JSON . parse ( raw ) ;
73+ } catch ( err ) {
74+ throw new UsageError ( `--json is not valid JSON — ${ ( err as Error ) . message } ` ) ;
75+ }
76+ if ( ! parsed || typeof parsed !== "object" || Array . isArray ( parsed ) ) {
77+ throw new UsageError ( "--json must decode to an object." ) ;
78+ }
79+ return parsed as Record < string , unknown > ;
80+ }
81+
82+ function buildToolArgs ( flags : CallFlags ) : Record < string , unknown > {
83+ const toolArgs = flags . json ? parseJsonArg ( flags . json ) : { } ;
84+ Object . assign ( toolArgs , parseArgFlags ( flags . arg ?? [ ] ) ) ;
85+ if ( flags . query !== undefined ) toolArgs . query = flags . query ;
86+ return toolArgs ;
87+ }
88+
89+ function validateCallFlags ( flags : CallFlags ) : string | undefined {
90+ try {
91+ parseTarget ( flags . target ) ;
92+ buildToolArgs ( flags ) ;
93+ } catch ( err ) {
94+ if ( err instanceof UsageError ) return err . message ;
95+ throw err ;
96+ }
97+ return undefined ;
98+ }
99+
28100export default defineCommand ( {
29101 description : "Call a tool on an MCP server (tools/call)" ,
30102 auth : "apiKey" ,
31103 usageArgs : "--target <server.tool> [--arg k=v ...] [--json '{...}'] [--url <url>]" ,
32- flags : {
33- target : {
34- type : "string" ,
35- valueHint : "<server.tool>" ,
36- description :
37- "Server code and tool name joined by a dot, e.g. market-cmapi00073529.SmartStockSelection" ,
38- required : true ,
39- } ,
40- arg : {
41- type : "array" ,
42- valueHint : "<kv>" ,
43- description : "Tool argument (repeatable). Values parsed as JSON if possible, else string." ,
44- } ,
45- json : {
46- type : "string" ,
47- valueHint : "<obj>" ,
48- description : "Full arguments object as JSON; merged with --arg (arg wins)." ,
49- } ,
50- query : {
51- type : "string" ,
52- valueHint : "<text>" ,
53- description : "Shortcut for --arg query=<text> (mirrors many DashScope MCP tools)." ,
54- } ,
55- url : {
56- type : "string" ,
57- valueHint : "<url>" ,
58- description : "Override the MCP endpoint URL (for non-Bailian servers)" ,
59- } ,
60- } ,
104+ flags : CALL_FLAGS ,
61105 exampleArgs : [
62106 '--target market-cmapi00073529.SmartStockSelection --query "Screen consumer stocks with ROE > 15%"' ,
63107 '--target market-cmapi00073529.FinQuery --json \'{"q":"Guizhou Maotai","limit":5}\'' ,
64108 "--target market-cmapi00073529.SmartFundSelection --arg riskLevel=R3 --arg minScale=10" ,
65109 ] ,
110+ validate : validateCallFlags ,
66111 async run ( ctx ) {
67112 const { settings, flags } = ctx ;
68- const target = flags . target ;
69-
70- const dot = target . indexOf ( "." ) ;
71- if ( dot <= 0 || dot === target . length - 1 ) {
72- throw new UsageError ( `target must be <server-code>.<tool>, got "${ target } ".` ) ;
73- }
74- const serverCode = target . slice ( 0 , dot ) ;
75- const toolName = target . slice ( dot + 1 ) ;
76-
77- let toolArgs : Record < string , unknown > = { } ;
78- if ( flags . json ) {
79- let parsed : unknown ;
80- try {
81- parsed = JSON . parse ( flags . json ) ;
82- } catch ( err ) {
83- throw new UsageError ( `--json is not valid JSON — ${ ( err as Error ) . message } ` ) ;
84- }
85- if ( ! parsed || typeof parsed !== "object" || Array . isArray ( parsed ) ) {
86- throw new UsageError ( "--json must decode to an object." ) ;
87- }
88- toolArgs = parsed as Record < string , unknown > ;
89- }
90- Object . assign ( toolArgs , parseArgFlags ( flags . arg ?? [ ] ) ) ;
91- if ( flags . query !== undefined ) toolArgs . query = flags . query ;
113+ const { serverCode, toolName } = parseTarget ( flags . target ) ;
114+ const toolArgs = buildToolArgs ( flags ) ;
92115
93116 const url = flags . url || ctx . client . url ( bailianMcpPath ( serverCode ) ) ;
94117 const format = detectOutputFormat ( settings . output ) ;
0 commit comments