1
1
#!/usr/bin/env node
2
2
import { parseArgs } from "node:util" ;
3
+ import * as readline from "node:readline/promises" ;
4
+ import { stdin , stdout } from "node:process" ;
3
5
import { z } from "zod" ;
4
6
import { PROVIDERS_OR_POLICIES } from "@huggingface/inference" ;
5
7
import { Agent } from "@huggingface/mcp-client" ;
6
8
import { version as packageVersion } from "../package.json" ;
7
- import { ServerConfigSchema } from "./lib/types" ;
8
- import { debug , error } from "./lib/utils" ;
9
+ import { InputConfigSchema , ServerConfigSchema } from "./lib/types" ;
10
+ import { debug , error , ANSI } from "./lib/utils" ;
9
11
import { mainCliLoop } from "./lib/mainCliLoop" ;
10
12
import { loadConfigFrom } from "./lib/loadConfigFrom" ;
11
13
@@ -70,6 +72,7 @@ async function main() {
70
72
provider : z . enum ( PROVIDERS_OR_POLICIES ) . optional ( ) ,
71
73
endpointUrl : z . string ( ) . optional ( ) ,
72
74
apiKey : z . string ( ) . optional ( ) ,
75
+ inputs : z . array ( InputConfigSchema ) . optional ( ) ,
73
76
servers : z . array ( ServerConfigSchema ) ,
74
77
} )
75
78
. refine ( ( data ) => data . provider !== undefined || data . endpointUrl !== undefined , {
@@ -85,6 +88,111 @@ async function main() {
85
88
process . exit ( 1 ) ;
86
89
}
87
90
91
+ // Handle inputs (i.e. env variables injection)
92
+ if ( config . inputs && config . inputs . length > 0 ) {
93
+ const rl = readline . createInterface ( { input : stdin , output : stdout } ) ;
94
+
95
+ stdout . write ( ANSI . BLUE ) ;
96
+ stdout . write ( "Some initial inputs are required by the agent. " ) ;
97
+ stdout . write ( "Please provide a value or leave empty to load from env." ) ;
98
+ stdout . write ( ANSI . RESET ) ;
99
+ stdout . write ( "\n" ) ;
100
+
101
+ for ( const inputItem of config . inputs ) {
102
+ const inputId = inputItem . id ;
103
+ const description = inputItem . description ;
104
+ const envSpecialValue = `\${input:${ inputId } }` ; // Special value to indicate env variable injection
105
+
106
+ // Check env variables that will use this input
107
+ const inputVars = new Set < string > ( ) ;
108
+ for ( const server of config . servers ) {
109
+ if ( server . type === "stdio" && server . config . env ) {
110
+ for ( const [ key , value ] of Object . entries ( server . config . env ) ) {
111
+ if ( value === envSpecialValue ) {
112
+ inputVars . add ( key ) ;
113
+ }
114
+ }
115
+ }
116
+ if ( ( server . type === "http" || server . type === "sse" ) && server . config . options ?. requestInit ?. headers ) {
117
+ for ( const [ key , value ] of Object . entries ( server . config . options . requestInit . headers ) ) {
118
+ if ( value . includes ( envSpecialValue ) ) {
119
+ inputVars . add ( key ) ;
120
+ }
121
+ }
122
+ }
123
+ }
124
+
125
+ if ( inputVars . size === 0 ) {
126
+ stdout . write ( ANSI . YELLOW ) ;
127
+ stdout . write ( `Input ${ inputId } defined in config but not used by any server.` ) ;
128
+ stdout . write ( ANSI . RESET ) ;
129
+ stdout . write ( "\n" ) ;
130
+ continue ;
131
+ }
132
+
133
+ // Prompt user for input
134
+ stdout . write ( ANSI . BLUE ) ;
135
+ stdout . write ( ` • ${ inputId } ` ) ;
136
+ stdout . write ( ANSI . RESET ) ;
137
+ stdout . write ( `: ${ description } . (default: load from ${ Array . from ( inputVars ) . join ( ", " ) } ) ` ) ;
138
+
139
+ const userInput = ( await rl . question ( "" ) ) . trim ( ) ;
140
+
141
+ // Inject user input (or env variable) into servers' env
142
+ for ( const server of config . servers ) {
143
+ if ( server . type === "stdio" && server . config . env ) {
144
+ for ( const [ key , value ] of Object . entries ( server . config . env ) ) {
145
+ if ( value === envSpecialValue ) {
146
+ if ( userInput ) {
147
+ server . config . env [ key ] = userInput ;
148
+ } else {
149
+ const valueFromEnv = process . env [ key ] || "" ;
150
+ server . config . env [ key ] = valueFromEnv ;
151
+ if ( valueFromEnv ) {
152
+ stdout . write ( ANSI . GREEN ) ;
153
+ stdout . write ( `Value successfully loaded from '${ key } '` ) ;
154
+ stdout . write ( ANSI . RESET ) ;
155
+ stdout . write ( "\n" ) ;
156
+ } else {
157
+ stdout . write ( ANSI . YELLOW ) ;
158
+ stdout . write ( `No value found for '${ key } ' in environment variables. Continuing.` ) ;
159
+ stdout . write ( ANSI . RESET ) ;
160
+ stdout . write ( "\n" ) ;
161
+ }
162
+ }
163
+ }
164
+ }
165
+ }
166
+ if ( ( server . type === "http" || server . type === "sse" ) && server . config . options ?. requestInit ?. headers ) {
167
+ for ( const [ key , value ] of Object . entries ( server . config . options . requestInit . headers ) ) {
168
+ if ( value . includes ( envSpecialValue ) ) {
169
+ if ( userInput ) {
170
+ server . config . options . requestInit . headers [ key ] = value . replace ( envSpecialValue , userInput ) ;
171
+ } else {
172
+ const valueFromEnv = process . env [ key ] || "" ;
173
+ server . config . options . requestInit . headers [ key ] = value . replace ( envSpecialValue , valueFromEnv ) ;
174
+ if ( valueFromEnv ) {
175
+ stdout . write ( ANSI . GREEN ) ;
176
+ stdout . write ( `Value successfully loaded from '${ key } '` ) ;
177
+ stdout . write ( ANSI . RESET ) ;
178
+ stdout . write ( "\n" ) ;
179
+ } else {
180
+ stdout . write ( ANSI . YELLOW ) ;
181
+ stdout . write ( `No value found for '${ key } ' in environment variables. Continuing.` ) ;
182
+ stdout . write ( ANSI . RESET ) ;
183
+ stdout . write ( "\n" ) ;
184
+ }
185
+ }
186
+ }
187
+ }
188
+ }
189
+ }
190
+ }
191
+
192
+ stdout . write ( "\n" ) ;
193
+ rl . close ( ) ;
194
+ }
195
+
88
196
const agent = new Agent (
89
197
config . endpointUrl
90
198
? {
0 commit comments