@@ -948,6 +948,12 @@ func (c *Client) State() ConnectionState {
948948 return c .state
949949}
950950
951+ // ActualPort returns the TCP port the CLI server is listening on.
952+ // Returns 0 if the client is not connected or using stdio transport.
953+ func (c * Client ) ActualPort () int {
954+ return c .actualPort
955+ }
956+
951957// Ping sends a ping request to the server to verify connectivity.
952958//
953959// The message parameter is optional and will be echoed back in the response.
@@ -1282,12 +1288,13 @@ func (c *Client) connectViaTcp(ctx context.Context) error {
12821288 return nil
12831289}
12841290
1285- // setupNotificationHandler configures handlers for session events, tool calls, and permission requests.
1291+ // setupNotificationHandler configures handlers for session events and RPC requests.
1292+ // Tool calls and permission requests are handled via the broadcast event model (protocol v3):
1293+ // the server broadcasts external_tool.requested / permission.requested as session events,
1294+ // and clients respond via session.tools.handlePendingToolCall / session.permissions.handlePendingPermissionRequest RPCs.
12861295func (c * Client ) setupNotificationHandler () {
12871296 c .client .SetRequestHandler ("session.event" , jsonrpc2 .NotificationHandlerFor (c .handleSessionEvent ))
12881297 c .client .SetRequestHandler ("session.lifecycle" , jsonrpc2 .NotificationHandlerFor (c .handleLifecycleEvent ))
1289- c .client .SetRequestHandler ("tool.call" , jsonrpc2 .RequestHandlerFor (c .handleToolCallRequest ))
1290- c .client .SetRequestHandler ("permission.request" , jsonrpc2 .RequestHandlerFor (c .handlePermissionRequest ))
12911298 c .client .SetRequestHandler ("userInput.request" , jsonrpc2 .RequestHandlerFor (c .handleUserInputRequest ))
12921299 c .client .SetRequestHandler ("hooks.invoke" , jsonrpc2 .RequestHandlerFor (c .handleHooksInvoke ))
12931300}
@@ -1306,84 +1313,6 @@ func (c *Client) handleSessionEvent(req sessionEventRequest) {
13061313 }
13071314}
13081315
1309- // handleToolCallRequest handles a tool call request from the CLI server.
1310- func (c * Client ) handleToolCallRequest (req toolCallRequest ) (* toolCallResponse , * jsonrpc2.Error ) {
1311- if req .SessionID == "" || req .ToolCallID == "" || req .ToolName == "" {
1312- return nil , & jsonrpc2.Error {Code : - 32602 , Message : "invalid tool call payload" }
1313- }
1314-
1315- c .sessionsMux .Lock ()
1316- session , ok := c .sessions [req .SessionID ]
1317- c .sessionsMux .Unlock ()
1318- if ! ok {
1319- return nil , & jsonrpc2.Error {Code : - 32602 , Message : fmt .Sprintf ("unknown session %s" , req .SessionID )}
1320- }
1321-
1322- handler , ok := session .getToolHandler (req .ToolName )
1323- if ! ok {
1324- return & toolCallResponse {Result : buildUnsupportedToolResult (req .ToolName )}, nil
1325- }
1326-
1327- result := c .executeToolCall (req .SessionID , req .ToolCallID , req .ToolName , req .Arguments , handler )
1328- return & toolCallResponse {Result : result }, nil
1329- }
1330-
1331- // executeToolCall executes a tool handler and returns the result.
1332- func (c * Client ) executeToolCall (
1333- sessionID , toolCallID , toolName string ,
1334- arguments any ,
1335- handler ToolHandler ,
1336- ) (result ToolResult ) {
1337- invocation := ToolInvocation {
1338- SessionID : sessionID ,
1339- ToolCallID : toolCallID ,
1340- ToolName : toolName ,
1341- Arguments : arguments ,
1342- }
1343-
1344- defer func () {
1345- if r := recover (); r != nil {
1346- result = buildFailedToolResult (fmt .Sprintf ("tool panic: %v" , r ))
1347- }
1348- }()
1349-
1350- if handler != nil {
1351- var err error
1352- result , err = handler (invocation )
1353- if err != nil {
1354- result = buildFailedToolResult (err .Error ())
1355- }
1356- }
1357-
1358- return result
1359- }
1360-
1361- // handlePermissionRequest handles a permission request from the CLI server.
1362- func (c * Client ) handlePermissionRequest (req permissionRequestRequest ) (* permissionRequestResponse , * jsonrpc2.Error ) {
1363- if req .SessionID == "" {
1364- return nil , & jsonrpc2.Error {Code : - 32602 , Message : "invalid permission request payload" }
1365- }
1366-
1367- c .sessionsMux .Lock ()
1368- session , ok := c .sessions [req .SessionID ]
1369- c .sessionsMux .Unlock ()
1370- if ! ok {
1371- return nil , & jsonrpc2.Error {Code : - 32602 , Message : fmt .Sprintf ("unknown session %s" , req .SessionID )}
1372- }
1373-
1374- result , err := session .handlePermissionRequest (req .Request )
1375- if err != nil {
1376- // Return denial on error
1377- return & permissionRequestResponse {
1378- Result : PermissionRequestResult {
1379- Kind : PermissionRequestResultKindDeniedCouldNotRequestFromUser ,
1380- },
1381- }, nil
1382- }
1383-
1384- return & permissionRequestResponse {Result : result }, nil
1385- }
1386-
13871316// handleUserInputRequest handles a user input request from the CLI server.
13881317func (c * Client ) handleUserInputRequest (req userInputRequest ) (* userInputResponse , * jsonrpc2.Error ) {
13891318 if req .SessionID == "" || req .Question == "" {
@@ -1434,22 +1363,4 @@ func (c *Client) handleHooksInvoke(req hooksInvokeRequest) (map[string]any, *jso
14341363 return result , nil
14351364}
14361365
1437- // The detailed error is stored in the Error field but not exposed to the LLM for security.
1438- func buildFailedToolResult (internalError string ) ToolResult {
1439- return ToolResult {
1440- TextResultForLLM : "Invoking this tool produced an error. Detailed information is not available." ,
1441- ResultType : "failure" ,
1442- Error : internalError ,
1443- ToolTelemetry : map [string ]any {},
1444- }
1445- }
14461366
1447- // buildUnsupportedToolResult creates a failure ToolResult for an unsupported tool.
1448- func buildUnsupportedToolResult (toolName string ) ToolResult {
1449- return ToolResult {
1450- TextResultForLLM : fmt .Sprintf ("Tool '%s' is not supported by this client instance." , toolName ),
1451- ResultType : "failure" ,
1452- Error : fmt .Sprintf ("tool '%s' not supported" , toolName ),
1453- ToolTelemetry : map [string ]any {},
1454- }
1455- }
0 commit comments