Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ FROM gcr.io/distroless/base-debian12
# Add required MCP server annotation
LABEL io.modelcontextprotocol.server.name="io.github.github/github-mcp-server"

# Expose port 8080 for HTTP mode
EXPOSE 8080

# Set the working directory
WORKDIR /server
# Copy the binary from the build stage
Expand Down
243 changes: 243 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,249 @@ the hostname for GitHub Enterprise Server or GitHub Enterprise Cloud with data r
}
```

### HTTP Server Mode

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.

#### Starting the HTTP Server

Start the HTTP server with the `http` command:

```bash
# Start HTTP server on default port (8080)
github-mcp-server http

# Start HTTP server on custom port
github-mcp-server http --port 3000

# With Docker
docker run -p 8080:8080 ghcr.io/github/github-mcp-server http

# With Docker on custom port
docker run -p 3000:3000 ghcr.io/github/github-mcp-server http --port 3000
```

> **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.

#### Authentication with Authorization Header

Clients authenticate by including their GitHub Personal Access Token in the `Authorization` header of each request:

```
Authorization: Bearer ghp_your_github_token_here
```

This "Bring Your Own Token" (BYOT) approach enables:
- **Multi-tenancy**: Different users can use their own tokens with proper permissions
- **Security**: Tokens are never stored on the server
- **Flexibility**: Users can revoke/rotate tokens independently

#### Client Configuration Examples

##### VS Code with GitHub Copilot

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):

```json
{
"github.copilot.mcp.enabled": true,
"github.copilot.mcp.servers": {
"github-http": {
"type": "http",
"url": "http://localhost:8080",
"headers": {
"Authorization": "Bearer ${input:github_token}"
}
}
}
}
```

VS Code will prompt for the `github_token` input when connecting.

For a remote server:

```json
{
"github.copilot.mcp.enabled": true,
"github.copilot.mcp.servers": {
"github-http": {
"type": "http",
"url": "https://your-mcp-server.example.com:8080",
"headers": {
"Authorization": "Bearer ${input:github_token}"
}
}
}
}
```

##### Claude Desktop

Add the following to your Claude Desktop MCP configuration file (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS or `%APPDATA%\Claude\claude_desktop_config.json` on Windows):

```json
{
"mcpServers": {
"github-http": {
"type": "http",
"url": "http://localhost:8080",
"headers": {
"Authorization": "Bearer YOUR_GITHUB_TOKEN_HERE"
}
}
}
}
```

> **Security Note:** When using hardcoded tokens in configuration files, ensure proper file permissions (e.g., `chmod 600`) to protect your token.

##### Other MCP Clients

For other MCP clients that support HTTP transport, ensure they:
1. Connect to the server's HTTP endpoint (e.g., `http://localhost:8080`)
2. Include the `Authorization: Bearer <token>` header in all requests
3. Use the MCP streamable HTTP transport protocol

Example with curl for testing:

```bash
# Test server health (this should fail without proper MCP request structure)
curl -H "Authorization: Bearer ghp_your_token" http://localhost:8080

# Proper MCP client implementation required for actual tool calls
```

#### Docker Deployment

##### Basic HTTP Server

Run the HTTP server in Docker with port mapping:

```bash
docker run -d \
--name github-mcp-http \
-p 8080:8080 \
ghcr.io/github/github-mcp-server http
```

##### With Logging

Enable file logging for debugging:

```bash
docker run -d \
--name github-mcp-http \
-p 8080:8080 \
-v $(pwd)/logs:/logs \
ghcr.io/github/github-mcp-server http --log-file /logs/server.log
```

##### With Custom Configuration

Use additional flags for configuration:

```bash
docker run -d \
--name github-mcp-http \
-p 8080:8080 \
ghcr.io/github/github-mcp-server http \
--port 8080 \
--toolsets actions,issues,pull_requests \
--read-only \
--log-file /var/log/github-mcp.log
```

##### Production Deployment with Docker Compose

Create a `docker-compose.yml` file:

```yaml
version: '3.8'
services:
github-mcp-server:
image: ghcr.io/github/github-mcp-server
command: http --port 8080 --log-file /logs/server.log
ports:
- "8080:8080"
volumes:
- ./logs:/logs
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--spider", "http://localhost:8080"]
interval: 30s
timeout: 10s
retries: 3
```

Then start with:

```bash
docker-compose up -d
```

#### GitHub Enterprise Support

HTTP mode works with GitHub Enterprise Server and GitHub Enterprise Cloud with data residency:

```bash
# GitHub Enterprise Server
docker run -d \
-p 8080:8080 \
ghcr.io/github/github-mcp-server http \
--gh-host https://github.yourcompany.com \
--port 8080

# GitHub Enterprise Cloud with data residency
docker run -d \
-p 8080:8080 \
ghcr.io/github/github-mcp-server http \
--gh-host https://octocorp.ghe.com \
--port 8080
```

Clients still provide their tokens via the `Authorization` header.

#### Security Considerations

When deploying the HTTP server:

1. **Use HTTPS in Production**: Always use a reverse proxy (nginx, Caddy, etc.) to terminate TLS
2. **Network Security**:
- Bind to localhost (`127.0.0.1`) for local-only access
- Use firewalls to restrict access to trusted networks
- Consider VPN or IP allowlisting for remote deployments
3. **Token Management**:
- Tokens are validated per-request and never stored
- Use fine-grained tokens with minimum required permissions
- Rotate tokens regularly
4. **Rate Limiting**: Consider adding rate limiting at the reverse proxy level
5. **Monitoring**: Enable logging to track usage and potential security issues

#### Troubleshooting HTTP Mode

**Server won't start:**
- Check if port 8080 (or your custom port) is already in use
- Ensure Docker port mapping is correct (`-p host_port:container_port`)

**Client connection fails:**
- Verify the server is running: `curl http://localhost:8080` (should return an error but connect)
- Check firewall rules allow connections to the port
- Verify the URL in client configuration matches the server address

**Authentication errors:**
- Ensure the `Authorization` header is properly formatted: `Bearer <token>`
- Verify the GitHub token is valid and not expired
- Check token has required permissions for the operations being performed

**Enable debug logging:**
```bash
github-mcp-server http --log-file debug.log
# Or with Docker:
docker run -p 8080:8080 -v $(pwd):/logs \
ghcr.io/github/github-mcp-server http --log-file /logs/debug.log
```

## Installation

### Install in GitHub Copilot on VS Code
Expand Down
53 changes: 53 additions & 0 deletions cmd/github-mcp-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,54 @@ var (
return ghmcp.RunStdioServer(stdioServerConfig)
},
}

httpCmd = &cobra.Command{
Use: "http",
Short: "Start HTTP server",
Long: `Start an HTTP server that supports multiple concurrent clients with per-request authentication.`,
RunE: func(_ *cobra.Command, _ []string) error {
// Parse toolsets
var enabledToolsets []string
if viper.IsSet("toolsets") {
if err := viper.UnmarshalKey("toolsets", &enabledToolsets); err != nil {
return fmt.Errorf("failed to unmarshal toolsets: %w", err)
}
}

// Parse tools
var enabledTools []string
if viper.IsSet("tools") {
if err := viper.UnmarshalKey("tools", &enabledTools); err != nil {
return fmt.Errorf("failed to unmarshal tools: %w", err)
}
}

// Parse enabled features
var enabledFeatures []string
if viper.IsSet("features") {
if err := viper.UnmarshalKey("features", &enabledFeatures); err != nil {
return fmt.Errorf("failed to unmarshal features: %w", err)
}
}

ttl := viper.GetDuration("repo-access-cache-ttl")
httpServerConfig := ghmcp.HTTPServerConfig{
Version: version,
Host: viper.GetString("host"),
Port: viper.GetInt("port"),
EnabledToolsets: enabledToolsets,
EnabledTools: enabledTools,
EnabledFeatures: enabledFeatures,
DynamicToolsets: viper.GetBool("dynamic_toolsets"),
ReadOnly: viper.GetBool("read-only"),
LogFilePath: viper.GetString("log-file"),
ContentWindowSize: viper.GetInt("content-window-size"),
LockdownMode: viper.GetBool("lockdown-mode"),
RepoAccessCacheTTL: &ttl,
}
return ghmcp.RunHTTPServer(httpServerConfig)
},
}
)

func init() {
Expand Down Expand Up @@ -124,8 +172,13 @@ func init() {
_ = viper.BindPFlag("lockdown-mode", rootCmd.PersistentFlags().Lookup("lockdown-mode"))
_ = viper.BindPFlag("repo-access-cache-ttl", rootCmd.PersistentFlags().Lookup("repo-access-cache-ttl"))

// Add HTTP-specific flags
httpCmd.Flags().Int("port", 8080, "Port to listen on for HTTP server")
_ = viper.BindPFlag("port", httpCmd.Flags().Lookup("port"))

// Add subcommands
rootCmd.AddCommand(stdioCmd)
rootCmd.AddCommand(httpCmd)
}

func initConfig() {
Expand Down
Loading