@@ -677,6 +677,56 @@ describe("mcp loopback server", () => {
677677 } ) ;
678678 } ) ;
679679
680+ it ( "uses channelId as the message target before falling back to request context" , async ( ) => {
681+ resolveGatewayScopedToolsMock . mockReturnValue ( {
682+ agentId : "main" ,
683+ tools : [
684+ {
685+ name : "message" ,
686+ description : "send a message" ,
687+ parameters : { type : "object" , properties : { } } ,
688+ execute : vi . fn ( async ( ) => ( {
689+ content : [ { type : "text" , text : "uploaded" } ] ,
690+ } ) ) ,
691+ } ,
692+ ] ,
693+ } ) ;
694+ server = await startMcpLoopbackServer ( 0 ) ;
695+ const runtime = getActiveMcpLoopbackRuntime ( ) ;
696+
697+ const response = await sendRaw ( {
698+ port : server . port ,
699+ token : runtime ? resolveMcpLoopbackBearerToken ( runtime , false ) : undefined ,
700+ headers : {
701+ "content-type" : "application/json" ,
702+ "x-session-key" : "agent:main:slack:C0" ,
703+ "x-openclaw-run-id" : "run-channel-id" ,
704+ "x-openclaw-message-channel" : "slack" ,
705+ "x-openclaw-message-to" : "C0" ,
706+ } ,
707+ body : JSON . stringify ( {
708+ jsonrpc : "2.0" ,
709+ id : 1 ,
710+ method : "tools/call" ,
711+ params : {
712+ name : "message" ,
713+ arguments : {
714+ action : "upload-file" ,
715+ channelId : "C1" ,
716+ path : "file:///tmp/out.png" ,
717+ } ,
718+ } ,
719+ } ) ,
720+ } ) ;
721+
722+ expect ( response . status ) . toBe ( 200 ) ;
723+ expect ( drainCliMessagingToolSends ( "agent:main:slack:C0" , "run-channel-id" ) ) . toEqual ( {
724+ targets : [ { tool : "message" , provider : "slack" , to : "C1" } ] ,
725+ texts : [ ] ,
726+ mediaUrls : [ "file:///tmp/out.png" ] ,
727+ } ) ;
728+ } ) ;
729+
680730 it ( "records sessions_send text for CLI duplicate suppression" , async ( ) => {
681731 resolveGatewayScopedToolsMock . mockReturnValue ( {
682732 agentId : "main" ,
0 commit comments