Skip to content

Commit e0ce7fc

Browse files
committed
feat: http mode for centralized deployments
1 parent afc2396 commit e0ce7fc

4 files changed

Lines changed: 449 additions & 0 deletions

File tree

Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ FROM gcr.io/distroless/base-debian12
2222
# Add required MCP server annotation
2323
LABEL io.modelcontextprotocol.server.name="io.github.github/github-mcp-server"
2424

25+
# Expose port 8080 for HTTP mode
26+
EXPOSE 8080
27+
2528
# Set the working directory
2629
WORKDIR /server
2730
# Copy the binary from the build stage

README.md

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,219 @@ the hostname for GitHub Enterprise Server or GitHub Enterprise Cloud with data r
266266
}
267267
```
268268

269+
### HTTP Server Mode
270+
271+
The GitHub MCP Server supports HTTP mode for serving multiple concurrent clients with per-request authentication. This is ideal for enterprise deployments where a centralized MCP server serves multiple users or applications.
272+
273+
#### Starting the HTTP Server
274+
275+
Start the HTTP server with the `http` command:
276+
277+
```bash
278+
# Start HTTP server on default port (8080)
279+
github-mcp-server http
280+
281+
# Start HTTP server on custom port
282+
github-mcp-server http --port 3000
283+
284+
# With Docker
285+
docker run -p 8080:8080 ghcr.io/github/github-mcp-server http
286+
287+
# With Docker on custom port
288+
docker run -p 3000:3000 ghcr.io/github/github-mcp-server http --port 3000
289+
```
290+
291+
> **Note:** Unlike stdio mode, HTTP mode does not require a `GITHUB_PERSONAL_ACCESS_TOKEN` environment variable at startup. Instead, each client provides their token via the `Authorization` header.
292+
#### Authentication with Authorization Header
293+
294+
Clients authenticate by including their GitHub Personal Access Token in the `Authorization` header of each request:
295+
296+
```
297+
Authorization: Bearer ghp_your_github_token_here
298+
```
299+
300+
This "Bring Your Own Token" (BYOT) approach enables:
301+
- **Multi-tenancy**: Different users can use their own tokens with proper permissions
302+
- **Security**: Tokens are never stored on the server
303+
- **Flexibility**: Users can revoke/rotate tokens independently
304+
305+
#### Client Configuration Examples
306+
307+
##### VS Code with GitHub Copilot
308+
309+
Configure VS Code to connect to your HTTP server by adding the following to your VS Code MCP settings (`.vscode/settings.json` or user settings):
310+
311+
```json
312+
{
313+
"servers": {
314+
"github-http": {
315+
"type": "http",
316+
"url": "http://your-mcp-server.example.com:8080",
317+
"headers": {
318+
"Authorization": "Bearer ${input:github_token}"
319+
}
320+
}
321+
},
322+
"inputs": [
323+
{
324+
"type": "promptString",
325+
"id": "github_token",
326+
"description": "GitHub Personal Access Token",
327+
"password": true
328+
}
329+
]
330+
}
331+
```
332+
333+
VS Code will prompt for the `github_token` input when connecting.
334+
335+
336+
337+
> **Security Note:** When using hardcoded tokens in configuration files, ensure proper file permissions (e.g., `chmod 600`) to protect your token.
338+
##### Other MCP Clients
339+
340+
For other MCP clients that support HTTP transport, ensure they:
341+
1. Connect to the server's HTTP endpoint (e.g., `http://localhost:8080`)
342+
2. Include the `Authorization: Bearer <token>` header in all requests
343+
3. Use the MCP streamable HTTP transport protocol
344+
345+
Example with curl for testing:
346+
347+
```bash
348+
# Test server health (this should fail without proper MCP request structure)
349+
curl -H "Authorization: Bearer ghp_your_token" http://localhost:8080
350+
351+
# Proper MCP client implementation required for actual tool calls
352+
```
353+
354+
#### Docker Deployment
355+
356+
##### Basic HTTP Server
357+
358+
Run the HTTP server in Docker with port mapping:
359+
360+
```bash
361+
docker run -d \
362+
--name github-mcp-http \
363+
-p 8080:8080 \
364+
ghcr.io/github/github-mcp-server http
365+
```
366+
367+
##### With Logging
368+
369+
Enable file logging for debugging:
370+
371+
```bash
372+
docker run -d \
373+
--name github-mcp-http \
374+
-p 8080:8080 \
375+
-v $(pwd)/logs:/logs \
376+
ghcr.io/github/github-mcp-server http --log-file /logs/server.log
377+
```
378+
379+
##### With Custom Configuration
380+
381+
Use additional flags for configuration:
382+
383+
```bash
384+
docker run -d \
385+
--name github-mcp-http \
386+
-p 8080:8080 \
387+
ghcr.io/github/github-mcp-server http \
388+
--port 8080 \
389+
--toolsets actions,issues,pull_requests \
390+
--read-only \
391+
--log-file /var/log/github-mcp.log
392+
```
393+
394+
##### Production Deployment with Docker Compose
395+
396+
Create a `docker-compose.yml` file:
397+
398+
```yaml
399+
version: '3.8'
400+
services:
401+
github-mcp-server:
402+
image: ghcr.io/github/github-mcp-server
403+
command: http --port 8080 --log-file /logs/server.log
404+
ports:
405+
- "8080:8080"
406+
volumes:
407+
- ./logs:/logs
408+
restart: unless-stopped
409+
healthcheck:
410+
test: ["CMD", "wget", "--spider", "http://localhost:8080"]
411+
interval: 30s
412+
timeout: 10s
413+
retries: 3
414+
```
415+
Then start with:
416+
```bash
417+
docker-compose up -d
418+
```
419+
420+
#### GitHub Enterprise Support
421+
422+
HTTP mode works with GitHub Enterprise Server and GitHub Enterprise Cloud with data residency:
423+
424+
```bash
425+
# GitHub Enterprise Server
426+
docker run -d \
427+
-p 8080:8080 \
428+
ghcr.io/github/github-mcp-server http \
429+
--gh-host https://github.yourcompany.com \
430+
--port 8080
431+
432+
# GitHub Enterprise Cloud with data residency
433+
docker run -d \
434+
-p 8080:8080 \
435+
ghcr.io/github/github-mcp-server http \
436+
--gh-host https://octocorp.ghe.com \
437+
--port 8080
438+
```
439+
440+
Clients still provide their tokens via the `Authorization` header.
441+
442+
#### Security Considerations
443+
444+
When deploying the HTTP server:
445+
446+
1. **Use HTTPS in Production**: Always use a reverse proxy (nginx, Caddy, etc.) to terminate TLS
447+
2. **Network Security**:
448+
- Bind to localhost (`127.0.0.1`) for local-only access
449+
- Use firewalls to restrict access to trusted networks
450+
- Consider VPN or IP allowlisting for remote deployments
451+
3. **Token Management**:
452+
- Tokens are validated per-request and never stored
453+
- Use fine-grained tokens with minimum required permissions
454+
- Rotate tokens regularly
455+
4. **Rate Limiting**: Consider adding rate limiting at the reverse proxy level
456+
5. **Monitoring**: Enable logging to track usage and potential security issues
457+
458+
#### Troubleshooting HTTP Mode
459+
460+
**Server won't start:**
461+
- Check if port 8080 (or your custom port) is already in use
462+
- Ensure Docker port mapping is correct (`-p host_port:container_port`)
463+
464+
**Client connection fails:**
465+
- Verify the server is running: `curl http://localhost:8080` (should return an error but connect)
466+
- Check firewall rules allow connections to the port
467+
- Verify the URL in client configuration matches the server address
468+
469+
**Authentication errors:**
470+
- Ensure the `Authorization` header is properly formatted: `Bearer <token>`
471+
- Verify the GitHub token is valid and not expired
472+
- Check token has required permissions for the operations being performed
473+
474+
**Enable debug logging:**
475+
```bash
476+
github-mcp-server http --log-file debug.log
477+
# Or with Docker:
478+
docker run -p 8080:8080 -v $(pwd):/logs \
479+
ghcr.io/github/github-mcp-server http --log-file /logs/debug.log
480+
```
481+
269482
## Installation
270483

271484
### Install in GitHub Copilot on VS Code

cmd/github-mcp-server/main.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,54 @@ var (
8989
return ghmcp.RunStdioServer(stdioServerConfig)
9090
},
9191
}
92+
93+
httpCmd = &cobra.Command{
94+
Use: "http",
95+
Short: "Start HTTP server",
96+
Long: `Start an HTTP server that supports multiple concurrent clients with per-request authentication.`,
97+
RunE: func(_ *cobra.Command, _ []string) error {
98+
// Parse toolsets
99+
var enabledToolsets []string
100+
if viper.IsSet("toolsets") {
101+
if err := viper.UnmarshalKey("toolsets", &enabledToolsets); err != nil {
102+
return fmt.Errorf("failed to unmarshal toolsets: %w", err)
103+
}
104+
}
105+
106+
// Parse tools
107+
var enabledTools []string
108+
if viper.IsSet("tools") {
109+
if err := viper.UnmarshalKey("tools", &enabledTools); err != nil {
110+
return fmt.Errorf("failed to unmarshal tools: %w", err)
111+
}
112+
}
113+
114+
// Parse enabled features
115+
var enabledFeatures []string
116+
if viper.IsSet("features") {
117+
if err := viper.UnmarshalKey("features", &enabledFeatures); err != nil {
118+
return fmt.Errorf("failed to unmarshal features: %w", err)
119+
}
120+
}
121+
122+
ttl := viper.GetDuration("repo-access-cache-ttl")
123+
httpServerConfig := ghmcp.HTTPServerConfig{
124+
Version: version,
125+
Host: viper.GetString("host"),
126+
Port: viper.GetInt("port"),
127+
EnabledToolsets: enabledToolsets,
128+
EnabledTools: enabledTools,
129+
EnabledFeatures: enabledFeatures,
130+
DynamicToolsets: viper.GetBool("dynamic_toolsets"),
131+
ReadOnly: viper.GetBool("read-only"),
132+
LogFilePath: viper.GetString("log-file"),
133+
ContentWindowSize: viper.GetInt("content-window-size"),
134+
LockdownMode: viper.GetBool("lockdown-mode"),
135+
RepoAccessCacheTTL: &ttl,
136+
}
137+
return ghmcp.RunHTTPServer(httpServerConfig)
138+
},
139+
}
92140
)
93141

94142
func init() {
@@ -127,8 +175,13 @@ func init() {
127175
_ = viper.BindPFlag("insider-mode", rootCmd.PersistentFlags().Lookup("insider-mode"))
128176
_ = viper.BindPFlag("repo-access-cache-ttl", rootCmd.PersistentFlags().Lookup("repo-access-cache-ttl"))
129177

178+
// Add HTTP-specific flags
179+
httpCmd.Flags().Int("port", 8080, "Port to listen on for HTTP server")
180+
_ = viper.BindPFlag("port", httpCmd.Flags().Lookup("port"))
181+
130182
// Add subcommands
131183
rootCmd.AddCommand(stdioCmd)
184+
rootCmd.AddCommand(httpCmd)
132185
}
133186

134187
func initConfig() {

0 commit comments

Comments
 (0)