@@ -130,6 +130,8 @@ export function SettingsPage() {
130130 // gateway with no access key — used by the one-click per-workspace install.
131131 const [ authDisabled , setAuthDisabled ] = useState ( false ) ;
132132 const [ savingAuthDisabled , setSavingAuthDisabled ] = useState ( false ) ;
133+ const [ networkAccess , setNetworkAccess ] = useState ( false ) ;
134+ const [ savingNetworkAccess , setSavingNetworkAccess ] = useState ( false ) ;
133135
134136 // Meta-tools master switch — gates the entire `mcpmux_*` namespace.
135137
@@ -397,6 +399,34 @@ export function SettingsPage() {
397399 . catch ( ( err ) => console . error ( 'Failed to load auth setting:' , err ) ) ;
398400 } , [ ] ) ;
399401
402+ // Load the network-access (0.0.0.0 bind) toggle on mount.
403+ useEffect ( ( ) => {
404+ invoke < boolean > ( 'get_gateway_network_access' )
405+ . then ( setNetworkAccess )
406+ . catch ( ( err ) => console . error ( 'Failed to load network-access setting:' , err ) ) ;
407+ } , [ ] ) ;
408+
409+ const updateNetworkAccess = async ( enabled : boolean ) => {
410+ const prev = networkAccess ;
411+ setNetworkAccess ( enabled ) ;
412+ setSavingNetworkAccess ( true ) ;
413+ try {
414+ await invoke ( 'set_gateway_network_access' , { enabled } ) ;
415+ success (
416+ 'Settings saved' ,
417+ enabled
418+ ? 'Gateway will bind 0.0.0.0 — restart it to become reachable on your network.'
419+ : 'Gateway will bind 127.0.0.1 — restart it to return to this machine only.'
420+ ) ;
421+ } catch ( err ) {
422+ const msg = err instanceof Error ? err . message : 'Unknown error' ;
423+ error ( 'Failed to update network access' , msg ) ;
424+ setNetworkAccess ( prev ) ;
425+ } finally {
426+ setSavingNetworkAccess ( false ) ;
427+ }
428+ } ;
429+
400430 const updateAuthDisabled = async ( disabled : boolean ) => {
401431 const prev = authDisabled ;
402432 setAuthDisabled ( disabled ) ;
@@ -785,6 +815,98 @@ export function SettingsPage() {
785815 </ div >
786816 </ div >
787817
818+ < div className = "border-t border-[rgb(var(--border-subtle))] pt-4" >
819+ < div className = "flex items-center justify-between gap-4" >
820+ < div className = "flex min-w-0 flex-1 items-start gap-3" >
821+ < Network className = "mt-0.5 h-5 w-5 flex-shrink-0 text-[rgb(var(--muted))]" />
822+ < div className = "min-w-0" >
823+ < label className = "text-sm font-medium" >
824+ Allow access from other devices
825+ </ label >
826+ < p className = "mt-1 text-xs text-[rgb(var(--muted))]" >
827+ Bind the gateway to all network interfaces (
828+ < span className = "font-mono" > 0.0.0.0</ span > ) so other machines on your
829+ network can connect to the same MCP servers. Off keeps it on{ ' ' }
830+ < span className = "font-mono" > 127.0.0.1</ span > (this machine only).
831+ Restart the gateway to apply.
832+ </ p >
833+ </ div >
834+ </ div >
835+ < Switch
836+ checked = { networkAccess }
837+ onCheckedChange = { updateNetworkAccess }
838+ disabled = { savingNetworkAccess }
839+ data-testid = "network-access-switch"
840+ />
841+ </ div >
842+
843+ { networkAccess ? (
844+ < div
845+ className = { `mt-3 flex items-start gap-2 rounded-lg border p-3 text-xs ${
846+ authDisabled
847+ ? 'border-red-300 bg-red-50 dark:border-red-700/60 dark:bg-red-900/20'
848+ : 'border-amber-300 bg-amber-50 dark:border-amber-700/60 dark:bg-amber-900/20'
849+ } `}
850+ data-testid = "network-access-warning"
851+ >
852+ < AlertCircle
853+ className = { `mt-0.5 h-4 w-4 flex-shrink-0 ${
854+ authDisabled
855+ ? 'text-red-600 dark:text-red-400'
856+ : 'text-amber-600 dark:text-amber-400'
857+ } `}
858+ />
859+ < div className = "flex-1" >
860+ { authDisabled ? (
861+ < >
862+ < p className = "font-semibold text-red-800 dark:text-red-200" >
863+ Exposed without authentication
864+ </ p >
865+ < p className = "mt-0.5 text-red-700 dark:text-red-300" >
866+ Authentication is off and the gateway is reachable on your network —
867+ anyone who can reach this machine can use every connected MCP server
868+ and its stored credentials. Turn authentication back on under
869+ Security, or only enable this on a network you trust.
870+ </ p >
871+ </ >
872+ ) : (
873+ < >
874+ < p className = "font-semibold text-amber-800 dark:text-amber-200" >
875+ Reachable on your network
876+ </ p >
877+ < p className = "mt-0.5 text-amber-700 dark:text-amber-300" >
878+ Connecting clients still need to be approved, but traffic is plain
879+ HTTP — only enable this on a network you trust. From another device,
880+ replace < span className = "font-mono" > localhost</ span > with this
881+ machine's LAN IP, e.g.{ ' ' }
882+ < span className = "font-mono" >
883+ http://192.168.1.x:
884+ { portSettings . activePort ?? portSettings . defaultPort } /mcp
885+ </ span >
886+ .
887+ </ p >
888+ < p className = "mt-1 text-amber-700 dark:text-amber-300" >
889+ Per-client OAuth approval happens on this machine, so a remote
890+ client that signs in via OAuth (e.g. ChatGPT) can't finish approval
891+ over the network yet — front the gateway with the public URL + a
892+ tunnel for that. For plain LAN sharing, pair this with
893+ authentication disabled.
894+ </ p >
895+ </ >
896+ ) }
897+ </ div >
898+ < Button
899+ variant = "secondary"
900+ size = "sm"
901+ onClick = { handleRestartGateway }
902+ data-testid = "network-access-restart-btn"
903+ >
904+ Restart gateway
905+ </ Button >
906+ </ div >
907+ ) : null }
908+ </ div >
909+
788910 { publicUrlSettings ?. activePublicBaseUrl &&
789911 ( publicUrlSettings . configuredPublicBaseUrl ?? publicUrlSettings . localBaseUrl ) &&
790912 publicUrlSettings . activePublicBaseUrl !==
0 commit comments