@@ -21,6 +21,7 @@ const crypto = require("crypto");
2121const fs = require ( "fs" ) ;
2222const http = require ( "http" ) ;
2323const net = require ( "net" ) ;
24+ const os = require ( "os" ) ;
2425const path = require ( "path" ) ;
2526const { Worker } = require ( "worker_threads" ) ;
2627const {
@@ -101,6 +102,84 @@ function getBackendAccessHost() {
101102 return host || "127.0.0.1" ;
102103}
103104
105+ function getInterfacePenalty ( name ) {
106+ const lower = String ( name || "" ) . toLowerCase ( ) ;
107+ if ( / ( d o c k e r | h y p e r - v | l o o p b a c k | n p c a p | t a i l s c a l e | v i r t u a l | v i r t u a l b o x | v m w a r e | v e t h e r n e t | w s l | z e r o t i e r ) / i. test ( lower ) ) {
108+ return 30 ;
109+ }
110+ if ( / ( e t h e r n e t | w i - f i | w i f i | w i r e l e s s | w l a n | 以 太 | 无 线 ) / i. test ( lower ) ) {
111+ return 0 ;
112+ }
113+ return 10 ;
114+ }
115+
116+ function isReachableClientIpv4 ( address ) {
117+ const text = String ( address || "" ) . trim ( ) ;
118+ const parts = text . split ( "." ) ;
119+ if ( parts . length !== 4 ) return false ;
120+ const nums = parts . map ( ( part ) => Number ( part ) ) ;
121+ if ( ! nums . every ( ( n ) => Number . isInteger ( n ) && n >= 0 && n <= 255 ) ) return false ;
122+ if ( nums [ 0 ] === 0 || nums [ 0 ] === 127 || nums [ 0 ] >= 224 ) return false ;
123+ if ( nums [ 0 ] === 169 && nums [ 1 ] === 254 ) return false ;
124+ return true ;
125+ }
126+
127+ function isPrivateIpv4 ( address ) {
128+ const nums = String ( address || "" ) . trim ( ) . split ( "." ) . map ( ( part ) => Number ( part ) ) ;
129+ if ( nums . length !== 4 || ! nums . every ( ( n ) => Number . isInteger ( n ) ) ) return false ;
130+ return (
131+ nums [ 0 ] === 10 ||
132+ ( nums [ 0 ] === 172 && nums [ 1 ] >= 16 && nums [ 1 ] <= 31 ) ||
133+ ( nums [ 0 ] === 192 && nums [ 1 ] === 168 )
134+ ) ;
135+ }
136+
137+ function getLanAccessHost ( defaultHost = DEFAULT_BACKEND_HOST ) {
138+ const candidates = [ ] ;
139+ const seen = new Set ( ) ;
140+ const addCandidate = ( address , interfaceName = "" , sourceOrder = 0 ) => {
141+ const value = String ( address || "" ) . trim ( ) ;
142+ if ( ! isReachableClientIpv4 ( value ) || seen . has ( value ) ) return ;
143+ seen . add ( value ) ;
144+ candidates . push ( [
145+ isPrivateIpv4 ( value ) ? 0 : 1 ,
146+ getInterfacePenalty ( interfaceName ) ,
147+ sourceOrder ,
148+ value ,
149+ ] ) ;
150+ } ;
151+
152+ try {
153+ const interfaces = os . networkInterfaces ( ) ;
154+ for ( const [ name , addresses ] of Object . entries ( interfaces || { } ) ) {
155+ for ( const item of addresses || [ ] ) {
156+ if ( ! item || ( item . family !== "IPv4" && item . family !== 4 ) || item . internal ) continue ;
157+ addCandidate ( item . address , name , 0 ) ;
158+ }
159+ }
160+ } catch { }
161+
162+ candidates . sort ( ( a , b ) => a [ 0 ] - b [ 0 ] || a [ 1 ] - b [ 1 ] || a [ 2 ] - b [ 2 ] ) ;
163+ return candidates [ 0 ] ?. [ 3 ] || defaultHost ;
164+ }
165+
166+ function getMcpAccessHost ( bindHost = getBackendBindHost ( ) ) {
167+ const host = String ( bindHost || "" ) . trim ( ) ;
168+ if ( host === LAN_BACKEND_HOST || host === "::" ) return getLanAccessHost ( DEFAULT_BACKEND_HOST ) ;
169+ return host || DEFAULT_BACKEND_HOST ;
170+ }
171+
172+ function getMcpAccessInfo ( bindHost = getBackendBindHost ( ) , port = getBackendPort ( ) ) {
173+ const accessHost = getMcpAccessHost ( bindHost ) ;
174+ const origin = `http://${ formatHostForUrl ( accessHost ) } :${ port } ` ;
175+ return {
176+ accessHost,
177+ mcpEndpoint : `${ origin } /mcp` ,
178+ skillBundleUrl : `${ origin } /mcp/skill/bundle` ,
179+ skillMarkdownUrl : `${ origin } /mcp/skill` ,
180+ } ;
181+ }
182+
104183function getBackendPort ( ) {
105184 const envPort = parsePort ( process . env . WECHAT_TOOL_PORT ) ;
106185 if ( envPort != null ) return envPort ;
@@ -2340,19 +2419,24 @@ function registerWindowIpc() {
23402419
23412420 ipcMain . handle ( "backend:getMcpLanAccess" , ( ) => {
23422421 try {
2422+ const host = getBackendBindHost ( ) ;
2423+ const port = getBackendPort ( ) ;
23432424 return {
23442425 enabled : getMcpLanAccessEnabled ( ) ,
2345- host : getBackendBindHost ( ) ,
2346- port : getBackendPort ( ) ,
2426+ host,
2427+ port,
23472428 uiUrl : getDesktopUiUrl ( ) ,
2429+ ...getMcpAccessInfo ( host , port ) ,
23482430 } ;
23492431 } catch ( err ) {
23502432 logMain ( `[main] backend:getMcpLanAccess failed: ${ err ?. message || err } ` ) ;
2433+ const port = DEFAULT_BACKEND_PORT ;
23512434 return {
23522435 enabled : false ,
23532436 host : DEFAULT_BACKEND_HOST ,
2354- port : DEFAULT_BACKEND_PORT ,
2437+ port,
23552438 uiUrl : getDesktopUiUrl ( ) ,
2439+ ...getMcpAccessInfo ( DEFAULT_BACKEND_HOST , port ) ,
23562440 } ;
23572441 }
23582442 } ) ;
@@ -2363,13 +2447,16 @@ function registerWindowIpc() {
23632447 const nextEnabled = ! ! enabled ;
23642448 const prevEnabled = getMcpLanAccessEnabled ( ) ;
23652449 if ( nextEnabled === prevEnabled ) {
2450+ const host = getBackendBindHost ( ) ;
2451+ const port = getBackendPort ( ) ;
23662452 return {
23672453 success : true ,
23682454 changed : false ,
23692455 enabled : prevEnabled ,
2370- host : getBackendBindHost ( ) ,
2371- port : getBackendPort ( ) ,
2456+ host,
2457+ port,
23722458 uiUrl : getDesktopUiUrl ( ) ,
2459+ ...getMcpAccessInfo ( host , port ) ,
23732460 } ;
23742461 }
23752462
@@ -2396,6 +2483,7 @@ function registerWindowIpc() {
23962483 host : getBackendBindHost ( ) ,
23972484 port : getBackendPort ( ) ,
23982485 uiUrl,
2486+ ...getMcpAccessInfo ( ) ,
23992487 } ;
24002488 } finally {
24012489 backendPortChangeInProgress = false ;
0 commit comments