An MCP (Model Context Protocol) server in Go that exposes GitHub repositories starred by a user as resources for LLM applications.
Built with Bazel for reproducible, hermetic builds across all platforms.
- Expose starred GitHub repositories as MCP resources
- List all starred repositories for authenticated user
- Query individual starred repository details
- Query starred repositories for any GitHub user
- Full MCP compliance with JSON-RPC over stdio
- OAuth-secured GitHub API integration
- Modular architecture with dependency injection using uber-go/fx
- Bazel build system for reproducible builds
- Hermetic Go SDK management with rules_go
- Auto-generated BUILD files with Gazelle
- Bazel 8.4+ (install instructions)
- GitHub Personal Access Token with appropriate scopes:
public_reporead:user
- Go 1.22+ (tested with Go 1.25.5)
- Note: Bazel manages its own Go SDK (1.24.0) automatically
git clone https://github.com/timduly4/mcp-server.git
cd mcp-serverBazel provides reproducible, hermetic builds:
# Build the binary
bazel build //cmd/server:mcp-server
# Or use the convenience script
./bazel.sh buildThe binary will be at: bazel-bin/cmd/server/server_/server
# Install dependencies
go mod download
# Build the binary
go build -o bin/mcp-server ./cmd/server- Copy the example environment file:
cp .env.example .env- Edit
.envand add your GitHub Personal Access Token:
GITHUB_TOKEN=your_github_token_hereTo generate a GitHub token:
- Go to https://github.com/settings/tokens
- Click "Generate new token (classic)"
- Select scopes:
public_repo,read:user - Generate and copy the token
# Build and run using the convenience script (automatically loads .env)
./bazel.sh run
# Or manually
set -a && source .env && set +a
bazel-bin/cmd/server/server_/server# Load environment variables and run
source .env
./bin/mcp-serverOr set the environment variable inline:
GITHUB_TOKEN=your_token_here ./bin/mcp-serverThe server exposes the following resources:
URI: github://starred
Description: Returns a list of all GitHub repositories starred by the authenticated user.
Response Format:
[
{
"uri": "github://starred/owner/repo",
"name": "owner/repo",
"description": "Repository description",
"mimeType": "application/json",
"contents": {
"name": "repo",
"full_name": "owner/repo",
"owner": "owner",
"description": "Repository description",
"url": "https://api.github.com/repos/owner/repo",
"html_url": "https://github.com/owner/repo",
"language": "Go",
"stars": 42,
"forks": 10,
"updated_at": "2024-01-01"
}
}
]URI Template: github://starred/{owner}/{repo}
Description: Returns details of a specific starred repository.
Example: github://starred/mark3labs/mcp-go
URI Template: github://starred/users/{username}
Description: Returns all repositories starred by a specific GitHub user (requires public starred repos or appropriate permissions).
Example: github://starred/users/octocat
Response Format:
[
{
"uri": "github://starred/users/octocat/owner/repo",
"name": "owner/repo",
"description": "Repository description",
"mimeType": "application/json",
"contents": {
"name": "repo",
"full_name": "owner/repo",
"owner": "owner",
"description": "Repository description",
"url": "https://api.github.com/repos/owner/repo",
"html_url": "https://github.com/owner/repo",
"language": "Go",
"stars": 42,
"forks": 10,
"updated_at": "2024-01-01",
"starred_by": "octocat"
}
}
]Note: The response includes a starred_by field to identify which user starred the repositories.
The project follows a modular architecture with clear separation of concerns:
mcp-server/
├── cmd/server/ # Main application entry point
│ └── main.go # fx dependency injection setup
├── internal/
│ ├── config/ # Configuration management
│ │ └── config.go # Environment variable loading
│ ├── github/ # GitHub API client
│ │ ├── client.go # GitHub REST API wrapper
│ │ └── client_test.go # Unit tests
│ ├── resource/ # MCP resource adapter
│ │ ├── adapter.go # Maps GitHub data to MCP format
│ │ └── adapter_test.go # Unit tests
│ └── server/ # MCP server implementation
│ └── server.go # MCP protocol handling
├── bin/ # Compiled binaries
├── tests/ # Integration tests
├── .env.example # Example environment configuration
├── .gitignore # Git ignore rules
├── CLAUDE.md # Project requirements and design
├── go.mod # Go module dependencies
└── README.md # This file
-
Server Module (
internal/server)- Implements MCP server using
mcp-goframework - Registers resource endpoints
- Handles JSON-RPC requests over stdio
- Implements MCP server using
-
GitHub Client Module (
internal/github)- Wraps GitHub REST API v3
- Handles authentication with OAuth tokens
- Provides normalized data structures
- Supports pagination
-
Resource Adapter Module (
internal/resource)- Maps GitHub API responses to MCP resource schema
- Converts data to JSON format
- Handles error normalization
-
Dependency Injection (
cmd/server)- Uses uber-go/fx for wiring components
- Provides testable interfaces
- Manages application lifecycle
# Run all tests
bazel test //...
# Or use the convenience script
./bazel.sh test
# Run specific test
bazel test //internal/github:github_test
# Run with detailed output
bazel test --test_output=all //...# Run all tests
go test ./...
# Run tests with coverage
go test -cover ./...
# Run tests verbosely
go test -v ./internal/github/... ./internal/resource/...The MCP Inspector provides a web-based UI for testing and debugging your MCP server interactively. This is the recommended way to explore resources and verify functionality during development.
# Option 1: Pass token inline with the command
GITHUB_TOKEN="your_token_here" npx @modelcontextprotocol/inspector bazel-bin/cmd/server/server_/server
# Option 2: Source .env file first
source .env
npx @modelcontextprotocol/inspector bazel-bin/cmd/server/server_/server# Option 1: Pass token inline with the command
GITHUB_TOKEN="your_token_here" npx @modelcontextprotocol/inspector bin/mcp-server
# Option 2: Source .env file first
source .env
npx @modelcontextprotocol/inspector bin/mcp-serverThe Inspector will:
- Start your MCP server in the background
- Launch a web interface (typically at http://localhost:5173)
- Provide an interactive UI to explore resources and test functionality
- Show request/response details for debugging
This is much more convenient than manually crafting JSON-RPC requests!
# Build for current platform
bazel build //cmd/server:mcp-server
# Build with optimization
bazel build --config=opt //cmd/server:mcp-server
# Build for debugging
bazel build --config=debug //cmd/server:mcp-server
# Cross-compilation (requires platform configs)
bazel build --platforms=@io_bazel_rules_go//go/toolchain:linux_amd64 //cmd/server:mcp-server# Build for current platform
go build -o bin/mcp-server ./cmd/server
# Build for Linux
GOOS=linux GOARCH=amd64 go build -o bin/mcp-server-linux ./cmd/server
# Build for macOS
GOOS=darwin GOARCH=amd64 go build -o bin/mcp-server-darwin ./cmd/server
# Build for Windows
GOOS=windows GOARCH=amd64 go build -o bin/mcp-server.exe ./cmd/server# Update BUILD files automatically
bazel run //:gazelle
# Clean build artifacts
bazel clean
# Clean everything (including downloaded dependencies)
bazel clean --expunge
# Query all targets
bazel query //...
# View dependency graph
bazel query --output=graph //cmd/server:mcp-server
# Build everything
bazel build //...The bazel.sh script provides shortcuts:
./bazel.sh build # Build the binary
./bazel.sh test # Run all tests
./bazel.sh run # Build and run the server
./bazel.sh clean # Clean artifacts
./bazel.sh gazelle # Update BUILD files
./bazel.sh format # Format BUILD files
./bazel.sh query # Query all targets
./bazel.sh help # Show help- Keep business logic in
internal/packages - Write unit tests alongside implementation files (
*_test.go) - Use interfaces for testability
- Follow Go standard project layout
- Each package has a
BUILD.bazelfile defining build targets - Use Gazelle to auto-generate BUILD files:
bazel run //:gazelle
-
Update
go.mod:go get github.com/example/new-package
-
Update BUILD files:
bazel run //:gazelle
-
If needed, update
MODULE.bazelto explicitly declare dependencies -
Commit changes to
go.mod,go.sum, and allBUILD.bazelfiles
This server conforms to the MCP specification:
- ✅ JSON-RPC 2.0 protocol
- ✅ stdio transport
- ✅ Resource capabilities
- ✅ Static resources
- ✅ Dynamic resource templates (URI templates)
- ✅ Proper error handling
- ✅ OAuth security
- mcp-go (github.com/mark3labs/mcp-go) v0.43.1 - MCP server framework
- go-github (github.com/google/go-github/v57) v57.0.0 - GitHub API client
- oauth2 (golang.org/x/oauth2) v0.33.0 - OAuth 2.0 authentication
- fx (go.uber.org/fx) v1.24.0 - Dependency injection framework
- Bazel 8.4+ - Build system
- rules_go v0.50.1 - Bazel Go rules
- Gazelle v0.39.1 - BUILD file generator
- Go SDK 1.24.0 - Managed by Bazel (hermetic)
Potential extensions as outlined in CLAUDE.md:
- Additional GitHub resources (owned repos, issues, pull requests)
- Caching layer for improved performance
- Prompt templates for repo metadata summarization
- WebSocket/HTTP transport options
- Rate limiting and request throttling
- Metrics and observability
Error: "GITHUB_TOKEN environment variable is required"
- Solution: Ensure
.envfile exists with yourGITHUB_TOKENset. If running manually (not via./bazel.sh run), export the variable usingset -a && source .env && set +abefore running the binary.
Error: "failed to fetch starred repos: 401 Unauthorized"
- Solution: Verify your GitHub token is valid and has the required scopes.
Build fails with "package X is not in GOROOT"
- Solution: Upgrade Go to version 1.22 or higher using
brew upgrade go(macOS) or download from https://go.dev/dl/
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License.