1- import { useState } from "react" ;
1+ import { useEffect , useState } from "react" ;
22import { BASE } from "../lib/api" ;
33
44const SERVER_BASE = BASE . replace ( / \/ a p i $ / , "" ) ;
5+ const MCP_URL_FALLBACK = "http://192.168.1.35/mcp-server/mcp" ;
6+ const MCP_URL_GENERAL_HINT = "Enter the MCP server URL (e.g. https://<host>/mcp-server/mcp)" ;
57
68export default function Jenkins ( ) {
79 const [ question , setQuestion ] = useState ( "" ) ;
810 const [ answer , setAnswer ] = useState ( "" ) ;
911 const [ error , setError ] = useState < string | null > ( null ) ;
1012 const [ loading , setLoading ] = useState ( false ) ;
13+ const [ mcpUrl , setMcpUrl ] = useState ( MCP_URL_FALLBACK ) ;
14+ const [ jenkinsToken , setJenkinsToken ] = useState ( "" ) ;
15+ const [ mcpUrlHint , setMcpUrlHint ] = useState ( MCP_URL_FALLBACK ) ;
16+ const [ tokenHint , setTokenHint ] = useState ( "" ) ;
17+ const [ configError , setConfigError ] = useState < string | null > ( null ) ;
18+
19+ useEffect ( ( ) => {
20+ let isMounted = true ;
21+ async function loadHints ( ) {
22+ try {
23+ const res = await fetch ( `${ SERVER_BASE } /jenkins/config` , {
24+ credentials : "include" ,
25+ } ) ;
26+ if ( ! res . ok ) {
27+ throw new Error ( res . statusText ) ;
28+ }
29+ const data = await res . json ( ) . catch ( ( ) => ( { } ) ) ;
30+ if ( ! isMounted ) return ;
31+ const hintUrl = data ?. mcpUrlHint || MCP_URL_FALLBACK ;
32+ const hintToken = data ?. tokenHint || "" ;
33+ setMcpUrlHint ( hintUrl ) ;
34+ setTokenHint ( hintToken ) ;
35+ setMcpUrl ( hintUrl ) ;
36+ setJenkinsToken ( hintToken ) ;
37+ setConfigError ( null ) ;
38+ } catch ( err : any ) {
39+ if ( ! isMounted ) return ;
40+ setConfigError ( err ?. message || "Unable to load Jenkins defaults" ) ;
41+ }
42+ }
43+
44+ loadHints ( ) ;
45+ return ( ) => {
46+ isMounted = false ;
47+ } ;
48+ } , [ ] ) ;
1149
1250 async function handleAsk ( ) {
13- if ( ! question . trim ( ) ) return ;
51+ if ( ! question . trim ( ) || ! mcpUrl . trim ( ) || ! jenkinsToken . trim ( ) ) return ;
1452 setLoading ( true ) ;
1553 setError ( null ) ;
1654 setAnswer ( "" ) ;
@@ -19,7 +57,11 @@ export default function Jenkins() {
1957 method : "POST" ,
2058 headers : { "Content-Type" : "application/json" } ,
2159 credentials : "include" ,
22- body : JSON . stringify ( { question : question . trim ( ) } ) ,
60+ body : JSON . stringify ( {
61+ question : question . trim ( ) ,
62+ mcpUrl : mcpUrl . trim ( ) || undefined ,
63+ token : jenkinsToken . trim ( ) || undefined ,
64+ } ) ,
2365 } ) ;
2466 const data = await res . json ( ) . catch ( ( ) => ( { } ) ) ;
2567 if ( ! res . ok ) throw new Error ( ( data as any ) ?. error || res . statusText ) ;
@@ -34,6 +76,32 @@ export default function Jenkins() {
3476 return (
3577 < section style = { { display : "grid" , gap : 12 } } >
3678 < h1 > Jenkins</ h1 >
79+ < label style = { { display : "grid" , gap : 4 } } >
80+ < span style = { { fontWeight : 500 } } > MCP URL</ span >
81+ < input
82+ type = "text"
83+ value = { mcpUrl }
84+ onChange = { ( e ) => setMcpUrl ( e . target . value ) }
85+ placeholder = { MCP_URL_GENERAL_HINT }
86+ style = { { width : "100%" , padding : 8 , fontSize : 14 } }
87+ />
88+ < span style = { { fontSize : 12 , color : "#555" } } >
89+ Hint: { MCP_URL_GENERAL_HINT }
90+ </ span >
91+ </ label >
92+ < label style = { { display : "grid" , gap : 4 } } >
93+ < span style = { { fontWeight : 500 } } > Jenkins Token</ span >
94+ < input
95+ type = "password"
96+ value = { jenkinsToken }
97+ onChange = { ( e ) => setJenkinsToken ( e . target . value ) }
98+ placeholder = { tokenHint || "Enter Jenkins token" }
99+ style = { { width : "100%" , padding : 8 , fontSize : 14 } }
100+ />
101+ < span style = { { fontSize : 12 , color : "#555" } } >
102+ Hint: { tokenHint ? tokenHint : "Set JENKINS_TOKEN to prefill" }
103+ </ span >
104+ </ label >
37105 < textarea
38106 rows = { 4 }
39107 value = { question }
@@ -42,7 +110,12 @@ export default function Jenkins() {
42110 style = { { width : "100%" , padding : 8 , fontSize : 14 } }
43111 />
44112 < div style = { { display : "flex" , gap : 8 } } >
45- < button onClick = { handleAsk } disabled = { loading || ! question . trim ( ) } >
113+ < button
114+ onClick = { handleAsk }
115+ disabled = {
116+ loading || ! question . trim ( ) || ! mcpUrl . trim ( ) || ! jenkinsToken . trim ( )
117+ }
118+ >
46119 { loading ? "Sending..." : "Ask" }
47120 </ button >
48121 < button
@@ -51,11 +124,18 @@ export default function Jenkins() {
51124 setQuestion ( "" ) ;
52125 setAnswer ( "" ) ;
53126 setError ( null ) ;
127+ setMcpUrl ( mcpUrlHint ) ;
128+ setJenkinsToken ( tokenHint ) ;
54129 } }
55130 >
56131 Clear
57132 </ button >
58133 </ div >
134+ { configError && (
135+ < div style = { { color : "#a67c00" , fontSize : 13 } } >
136+ Using fallback MCP defaults: { configError }
137+ </ div >
138+ ) }
59139 { error && < div style = { { color : "red" , fontSize : 13 } } > { error } </ div > }
60140 < textarea
61141 readOnly
0 commit comments