|
1 | 1 | # Gateway Proxy Service Configuration |
2 | 2 |
|
3 | | -## Cross-Origin Resource Sharing (CORS) |
| 3 | +## Configuration Example |
| 4 | + |
| 5 | +Here is a complete configuration example, including routing, CORS, response handling, and other configurations: |
| 6 | + |
4 | 7 | ```yaml |
| 8 | +name: "mock-user-svc" # Proxy service name, globally unique |
| 9 | + |
| 10 | +# Routing configuration |
5 | 11 | routers: |
6 | | - - server: "user" |
7 | | - prefix: "/mcp/user" |
| 12 | + - server: "mock-user-svc" # Service name |
| 13 | + prefix: "/mcp/user" # Route prefix, globally unique, cannot be duplicated, recommended to distinguish by service or domain+module |
| 14 | + |
| 15 | + # CORS configuration |
8 | 16 | cors: |
9 | | - allowOrigins: |
| 17 | + allowOrigins: # Can be fully open in development/test environments, better to open selectively in production (most MCP Clients don't need CORS) |
10 | 18 | - "*" |
11 | | - allowMethods: |
| 19 | + allowMethods: # Allowed request methods, open as needed, for MCP (SSE and Streamable) usually only these 3 methods are needed |
12 | 20 | - "GET" |
13 | 21 | - "POST" |
14 | 22 | - "OPTIONS" |
15 | 23 | allowHeaders: |
16 | | - - "Content-Type" |
17 | | - - "Authorization" |
18 | | - - "Mcp-Session-Id" |
| 24 | + - "Content-Type" # Must be allowed |
| 25 | + - "Authorization" # Required if authentication is needed |
| 26 | + - "Mcp-Session-Id" # For MCP, must support this Key in requests, otherwise Streamable HTTP won't work properly |
19 | 27 | exposeHeaders: |
20 | | - - "Mcp-Session-Id" |
21 | | - allowCredentials: true |
| 28 | + - "Mcp-Session-Id" # For MCP, must expose this Key when CORS is enabled, otherwise Streamable HTTP won't work properly |
| 29 | + allowCredentials: true # Whether to add Access-Control-Allow-Credentials: true header |
| 30 | + |
| 31 | +# Service configuration |
| 32 | +servers: |
| 33 | + - name: "mock-user-svc" # Service name, must match server in routers |
| 34 | + namespace: "user-service" # Service namespace, used for service grouping |
| 35 | + description: "Mock User Service" # Service description |
| 36 | + allowedTools: # List of allowed tools (subset of tools) |
| 37 | + - "register_user" |
| 38 | + - "get_user_by_email" |
| 39 | + - "update_user_preferences" |
| 40 | + config: # Service-level configuration, can be referenced in tools via {{.Config}} |
| 41 | + Cookie: 123 # Hard-coded configuration |
| 42 | + Authorization: 'Bearer {{ env "AUTH_TOKEN" }}' # Configuration from environment variables, usage is '{{ env "ENV_VAR_NAME" }}' |
| 43 | + |
| 44 | +# Tool configuration |
| 45 | +tools: |
| 46 | + - name: "register_user" # Tool name |
| 47 | + description: "Register a new user" # Tool description |
| 48 | + method: "POST" # HTTP method for requesting target (upstream, backend) service |
| 49 | + endpoint: "http://localhost:5236/users" # Target service address |
| 50 | + headers: # Request header configuration, headers to carry when requesting target service |
| 51 | + Content-Type: "application/json" # Hard-coded header |
| 52 | + Authorization: "{{.Request.Headers.Authorization}}" # Use Authorization header extracted from client request (for passthrough scenarios) |
| 53 | + Cookie: "{{.Config.Cookie}}" # Use value from service configuration |
| 54 | + args: # Parameter configuration |
| 55 | + - name: "username" # Parameter name |
| 56 | + position: "body" # Parameter position: header, query, path, body |
| 57 | + required: true # Whether parameter is required |
| 58 | + type: "string" # Parameter type |
| 59 | + description: "Username" # Parameter description |
| 60 | + default: "" # Default value |
| 61 | + - name: "email" |
| 62 | + position: "body" |
| 63 | + required: true |
| 64 | + type: "string" |
| 65 | + description: "Email" |
| 66 | + default: "" |
| 67 | + requestBody: |- # Request body template, for dynamically generating request body, e.g., values extracted from parameters (MCP request arguments) |
| 68 | + { |
| 69 | + "username": "{{.Args.username}}", |
| 70 | + "email": "{{.Args.email}}" |
| 71 | + } |
| 72 | + responseBody: |- # Response body template, for dynamically generating response body, e.g., values extracted from response |
| 73 | + { |
| 74 | + "id": "{{.Response.Data.id}}", |
| 75 | + "username": "{{.Response.Data.username}}", |
| 76 | + "email": "{{.Response.Data.email}}", |
| 77 | + "createdAt": "{{.Response.Data.createdAt}}" |
| 78 | + } |
| 79 | +
|
| 80 | + - name: "get_user_by_email" |
| 81 | + description: "Get user by email" |
| 82 | + method: "GET" |
| 83 | + endpoint: "http://localhost:5236/users/email/{{.Args.email}}" |
| 84 | + args: |
| 85 | + - name: "email" |
| 86 | + position: "path" |
| 87 | + required: true |
| 88 | + type: "string" |
| 89 | + description: "Email" |
| 90 | + default: "" |
| 91 | + responseBody: |- |
| 92 | + { |
| 93 | + "id": "{{.Response.Data.id}}", |
| 94 | + "username": "{{.Response.Data.username}}", |
| 95 | + "email": "{{.Response.Data.email}}", |
| 96 | + "createdAt": "{{.Response.Data.createdAt}}" |
| 97 | + } |
| 98 | +
|
| 99 | + - name: "update_user_preferences" |
| 100 | + description: "Update user preferences" |
| 101 | + method: "PUT" |
| 102 | + endpoint: "http://localhost:5236/users/{{.Args.email}}/preferences" |
| 103 | + headers: |
| 104 | + Content-Type: "application/json" |
| 105 | + Authorization: "{{.Request.Headers.Authorization}}" |
| 106 | + Cookie: "{{.Config.Cookie}}" |
| 107 | + args: |
| 108 | + - name: "email" |
| 109 | + position: "path" |
| 110 | + required: true |
| 111 | + type: "string" |
| 112 | + description: "Email" |
| 113 | + default: "" |
| 114 | + - name: "isPublic" |
| 115 | + position: "body" |
| 116 | + required: true |
| 117 | + type: "boolean" |
| 118 | + description: "Whether the user profile is public" |
| 119 | + default: "false" |
| 120 | + - name: "showEmail" |
| 121 | + position: "body" |
| 122 | + required: true |
| 123 | + type: "boolean" |
| 124 | + description: "Whether to show email in profile" |
| 125 | + default: "true" |
| 126 | + - name: "theme" |
| 127 | + position: "body" |
| 128 | + required: true |
| 129 | + type: "string" |
| 130 | + description: "User interface theme" |
| 131 | + default: "light" |
| 132 | + - name: "tags" |
| 133 | + position: "body" |
| 134 | + required: true |
| 135 | + type: "array" |
| 136 | + items: |
| 137 | + type: "string" |
| 138 | + enum: ["developer", "designer", "manager", "tester"] |
| 139 | + description: "User role tags" |
| 140 | + default: "[]" |
| 141 | + requestBody: |- |
| 142 | + { |
| 143 | + "isPublic": {{.Args.isPublic}}, |
| 144 | + "showEmail": {{.Args.showEmail}}, |
| 145 | + "theme": "{{.Args.theme}}", |
| 146 | + "tags": {{.Args.tags}} |
| 147 | + } |
| 148 | + responseBody: |- |
| 149 | + { |
| 150 | + "id": "{{.Response.Data.id}}", |
| 151 | + "username": "{{.Response.Data.username}}", |
| 152 | + "email": "{{.Response.Data.email}}", |
| 153 | + "createdAt": "{{.Response.Data.createdAt}}", |
| 154 | + "preferences": { |
| 155 | + "isPublic": {{.Response.Data.preferences.isPublic}}, |
| 156 | + "showEmail": {{.Response.Data.preferences.showEmail}}, |
| 157 | + "theme": "{{.Response.Data.preferences.theme}}", |
| 158 | + "tags": {{.Response.Data.preferences.tags}} |
| 159 | + } |
| 160 | + } |
22 | 161 | ``` |
23 | 162 |
|
24 | | -> **Note:** You must explicitly configure `Mcp-Session-Id` in both `allowHeaders` and `exposeHeaders`, otherwise the client will not be able to correctly request and read the `Mcp-Session-Id` in the response headers. |
| 163 | +## Configuration Description |
| 164 | +
|
| 165 | +### 1. Basic Configuration |
| 166 | +
|
| 167 | +- `name`: Proxy service name, globally unique, used to identify different proxy services |
| 168 | +- `routers`: List of routing configurations, defining request forwarding rules |
| 169 | +- `servers`: List of service configurations, defining service metadata and allowed tools |
| 170 | +- `tools`: List of tool configurations, defining specific API call rules |
25 | 171 |
|
26 | | -## Response Handling |
| 172 | +A configuration can be considered as a namespace, recommended to distinguish by service or domain, where a service contains many API interfaces, each API interface corresponding to a Tool |
27 | 173 |
|
28 | | -Currently, there are **two response handling modes** supported: |
| 174 | +### 2. Routing Configuration |
29 | 175 |
|
30 | | -### 1. Pass-through Response Body |
| 176 | +Routing configuration is used to define request forwarding rules: |
31 | 177 |
|
32 | | -No processing is done on the backend response, and it is directly forwarded to the client. Template example: |
| 178 | +```yaml |
| 179 | +routers: |
| 180 | + - server: "mock-user-svc" # Service name, must match name in servers |
| 181 | + prefix: "/mcp/user" # Route prefix, globally unique, cannot be duplicated |
| 182 | +``` |
| 183 | + |
| 184 | +By default, three endpoints will be derived from the `prefix`: |
| 185 | +- SSE: `${prefix}/sse`, e.g., `/mcp/user/sse` |
| 186 | +- SSE: `${prefix}/message`, e.g., `/mcp/user/message` |
| 187 | +- StreamableHTTP: `${prefix}/mcp`, e.g., `/mcp/user/mcp` |
| 188 | + |
| 189 | +### 3. CORS Configuration |
| 190 | + |
| 191 | +Cross-Origin Resource Sharing (CORS) configuration is used to control cross-origin request access permissions: |
33 | 192 |
|
34 | 193 | ```yaml |
35 | | -responseBody: |- |
36 | | - {{.Response.Body}} |
| 194 | +cors: |
| 195 | + allowOrigins: # Can be fully open in development/test environments, better to open selectively in production (most MCP Clients don't need CORS) |
| 196 | + - "*" |
| 197 | + allowMethods: # Allowed request methods, open as needed, for MCP (SSE and Streamable) usually only these 3 methods are needed |
| 198 | + - "GET" |
| 199 | + - "POST" |
| 200 | + - "OPTIONS" |
| 201 | + allowHeaders: |
| 202 | + - "Content-Type" # Must be allowed |
| 203 | + - "Authorization" # Required if authentication is needed |
| 204 | + - "Mcp-Session-Id" # For MCP, must support this Key in requests, otherwise Streamable HTTP won't work properly |
| 205 | + exposeHeaders: |
| 206 | + - "Mcp-Session-Id" # For MCP, must expose this Key when CORS is enabled, otherwise Streamable HTTP won't work properly |
| 207 | + allowCredentials: true # Whether to add Access-Control-Allow-Credentials: true header |
37 | 208 | ``` |
38 | 209 |
|
39 | | -### 2. Custom Field Response (Field Mapping) |
| 210 | +> **Usually, MCP Clients don't need CORS enabled** |
| 211 | + |
| 212 | +### 4. Service Configuration |
40 | 213 |
|
41 | | -Parse the backend response body as JSON structure, extract specific fields, and then return. Template example: |
| 214 | +Service configuration is used to define service metadata, associated tool list, and service-level configuration: |
42 | 215 |
|
43 | 216 | ```yaml |
44 | | -responseBody: |- |
45 | | - { |
46 | | - "id": "{{.Response.Data.id}}", |
47 | | - "username": "{{.Response.Data.username}}", |
48 | | - "email": "{{.Response.Data.email}}", |
49 | | - "createdAt": "{{.Response.Data.createdAt}}" |
50 | | - } |
51 | | -``` |
| 217 | +servers: |
| 218 | + - name: "mock-user-svc" # Service name, must match server in routers |
| 219 | + namespace: "user-service" # Service namespace, used for service grouping |
| 220 | + description: "Mock User Service" # Service description |
| 221 | + allowedTools: # List of allowed tools (subset of tools) |
| 222 | + - "register_user" |
| 223 | + - "get_user_by_email" |
| 224 | + - "update_user_preferences" |
| 225 | + config: # Service-level configuration, can be referenced in tools via {{.Config}} |
| 226 | + Cookie: 123 # Hard-coded configuration |
| 227 | + Authorization: 'Bearer {{ env "AUTH_TOKEN" }}' # Configuration from environment variables, usage is '{{ env "ENV_VAR_NAME" }}' |
| 228 | +``` |
| 229 | + |
| 230 | +Service-level configuration can be referenced in tools via `{{.Config}}`. Here you can either hard-code values in the configuration file or get them from environment variables. For environment variable injection, use the format `{{ env "ENV_VAR_NAME" }}` |
| 231 | + |
| 232 | +### 5. Tool Configuration |
| 233 | + |
| 234 | +Tool configuration is used to define specific API call rules: |
| 235 | + |
| 236 | +```yaml |
| 237 | +tools: |
| 238 | + - name: "register_user" # Tool name |
| 239 | + description: "Register a new user" # Tool description |
| 240 | + method: "POST" # HTTP method for requesting target (upstream, backend) service |
| 241 | + endpoint: "http://localhost:5236/users" # Target service address |
| 242 | + headers: # Request header configuration, headers to carry when requesting target service |
| 243 | + Content-Type: "application/json" # Hard-coded header |
| 244 | + Authorization: "{{.Request.Headers.Authorization}}" # Use Authorization header extracted from client request (for passthrough scenarios) |
| 245 | + Cookie: "{{.Config.Cookie}}" # Use value from service configuration |
| 246 | + args: # Parameter configuration |
| 247 | + - name: "username" # Parameter name |
| 248 | + position: "body" # Parameter position: header, query, path, body |
| 249 | + required: true # Whether parameter is required |
| 250 | + type: "string" # Parameter type |
| 251 | + description: "Username" # Parameter description |
| 252 | + default: "" # Default value |
| 253 | + - name: "email" |
| 254 | + position: "body" |
| 255 | + required: true |
| 256 | + type: "string" |
| 257 | + description: "Email" |
| 258 | + default: "" |
| 259 | + requestBody: |- # Request body template, for dynamically generating request body, e.g., values extracted from parameters (MCP request arguments) |
| 260 | + { |
| 261 | + "username": "{{.Args.username}}", |
| 262 | + "email": "{{.Args.email}}" |
| 263 | + } |
| 264 | + responseBody: |- # Response body template, for dynamically generating response body, e.g., values extracted from response |
| 265 | + { |
| 266 | + "id": "{{.Response.Data.id}}", |
| 267 | + "username": "{{.Response.Data.username}}", |
| 268 | + "email": "{{.Response.Data.email}}", |
| 269 | + "createdAt": "{{.Response.Data.createdAt}}" |
| 270 | + } |
| 271 | +``` |
| 272 | + |
| 273 | +#### 5.1 Request Parameter Assembly |
| 274 | + |
| 275 | +When requesting the target service, parameters need to be assembled. Currently, there are several sources: |
| 276 | +1. `.Config`: Extract values from service-level configuration |
| 277 | +2. `.Args`: Extract values directly from request parameters |
| 278 | +3. `.Request`: Extract values from the request, including headers `.Request.Headers`, body `.Request.Body`, etc. |
| 279 | + |
| 280 | +Assembly is done in `requestBody`, for example: |
| 281 | +```yaml |
| 282 | + requestBody: |- |
| 283 | + { |
| 284 | + "isPublic": {{.Args.isPublic}}, |
| 285 | + "showEmail": {{.Args.showEmail}}, |
| 286 | + "theme": "{{.Args.theme}}", |
| 287 | + "tags": {{.Args.tags}} |
| 288 | + } |
| 289 | +``` |
| 290 | + |
| 291 | +Including `endpoint` (target address) can also use the above sources to extract values, for example `http://localhost:5236/users/{{.Args.email}}/preferences` extracts values from request parameters |
| 292 | + |
| 293 | +#### 5.2 Response Parameter Assembly |
| 294 | + |
| 295 | +Response body assembly is similar to request body assembly: |
| 296 | +1. `.Response.Data`: Extract values from response, response must be in JSON format to extract |
| 297 | +2. `.Response.Body`: Directly pass through the entire response body, ignoring response content format, directly passed to client |
| 298 | + |
| 299 | +All are extracted via `.Response`, for example: |
| 300 | +```yaml |
| 301 | + responseBody: |- |
| 302 | + { |
| 303 | + "id": "{{.Response.Data.id}}", |
| 304 | + "username": "{{.Response.Data.username}}", |
| 305 | + "email": "{{.Response.Data.email}}", |
| 306 | + "createdAt": "{{.Response.Data.createdAt}}" |
| 307 | + } |
| 308 | +``` |
| 309 | + |
| 310 | +## Configuration Storage |
| 311 | + |
| 312 | +Gateway proxy configuration can be stored in two ways: |
| 313 | + |
| 314 | +1. Database storage (recommended): |
| 315 | + - Supports SQLite3, PostgreSQL, MySQL |
| 316 | + - Each configuration stored as a record |
| 317 | + - Supports dynamic updates and hot reload |
| 318 | + |
| 319 | +2. File storage: |
| 320 | + - Each configuration stored as a separate YAML file |
| 321 | + - Similar to Nginx's vhost configuration approach |
| 322 | + - Filename recommended to use service name, e.g., `mock-user-svc.yaml` |
0 commit comments