GitLab Zero-Leak MCP Server
April 2, 2025 · View on GitHub
A security-hardened GitLab API MCP Server with zero source code leakage risk. Built for self-hosted use with Claude Desktop or other LLM-based agents.
Features
- ❌ Disables all source-reading operations (e.g.,
get_file_contents) - ✅ Zero-read default policy
- ✅ Env-based handler control via
.env - ✅ Project-level allowlist for read access
- ✅ Dockerized & Self-hostable
- ✅ Claude/Desktop compatible MCP Server implementation
- ✅ All original GitLab MCP Server features
- ✅ Comprehensive audit logging
Architecture
Standard Setup (Local MCP)
Claude Desktop
|
v
GitLab Zero-Leak MCP Server (Local)
|
v
GitLab API (self-hosted)
Tools
-
create_or_update_file- Create or update a single file in a project
- Inputs:
project_id(string): Project ID or URL-encoded pathfile_path(string): Path where to create/update the filecontent(string): Content of the filecommit_message(string): Commit messagebranch(string): Branch to create/update the file inprevious_path(optional string): Path of the file to move/rename
- Returns: File content and commit details
-
push_files- Push multiple files in a single commit
- Inputs:
project_id(string): Project ID or URL-encoded pathbranch(string): Branch to push tofiles(array): Files to push, each withfile_pathandcontentcommit_message(string): Commit message
- Returns: Updated branch reference
-
search_repositories- Search for GitLab projects
- Inputs:
search(string): Search querypage(optional number): Page number for paginationper_page(optional number): Results per page (default 20)
- Returns: Project search results
-
create_repository- Create a new GitLab project
- Inputs:
name(string): Project namedescription(optional string): Project descriptionvisibility(optional string): 'private', 'internal', or 'public'initialize_with_readme(optional boolean): Initialize with README
- Returns: Created project details
-
get_file_contents- Get contents of a file or directory
- Inputs:
project_id(string): Project ID or URL-encoded pathfile_path(string): Path to file/directoryref(optional string): Branch/tag/commit to get contents from
- Returns: File/directory contents
- Note: Disabled by default for security reasons
-
create_issue- Create a new issue
- Inputs:
project_id(string): Project ID or URL-encoded pathtitle(string): Issue titledescription(optional string): Issue descriptionassignee_ids(optional number[]): User IDs to assignlabels(optional string[]): Labels to addmilestone_id(optional number): Milestone ID
- Returns: Created issue details
-
create_merge_request- Create a new merge request
- Inputs:
project_id(string): Project ID or URL-encoded pathtitle(string): MR titledescription(optional string): MR descriptionsource_branch(string): Branch containing changestarget_branch(string): Branch to merge intodraft(optional boolean): Create as draft MRallow_collaboration(optional boolean): Allow commits from upstream members
- Returns: Created merge request details
-
fork_repository- Fork a project
- Inputs:
project_id(string): Project ID or URL-encoded pathnamespace(optional string): Namespace to fork to
- Returns: Forked project details
-
create_branch- Create a new branch
- Inputs:
project_id(string): Project ID or URL-encoded pathbranch(string): Name for new branchref(optional string): Source branch/commit for new branch
- Returns: Created branch reference
Getting Started
Local Deployment
-
Clone this repository:
git clone https://github.com/yourusername/gitlab-zero-leak-mcp.git cd gitlab-zero-leak-mcp -
Create a
.envfile based on.env.example:cat > .env << EOF GITLAB_PERSONAL_ACCESS_TOKEN=your_token_here GITLAB_API_URL=https://gitlab.com/api/v4 DISABLED_HANDLERS=get_file_contents ALLOWED_READ_PROJECTS=your-group/public-repo EOF -
Run MCP Server using Docker:
docker build -t gitlab-zero-leak-mcp . docker run -i --env-file .env gitlab-zero-leak-mcp
Setup
Personal Access Token
Create a GitLab Personal Access Token with appropriate permissions:
- Go to User Settings > Access Tokens in GitLab
- Select the required scopes:
apifor full API accessread_apifor read-only accesswrite_repositoryfor repository operations- Avoid
read_repositorywhen possible for maximum security
- Create the token and save it securely
Usage with Claude Desktop
Add the following to your claude_desktop_config.json:
Docker
{
"mcpServers": {
"gitlab-zero-leak": {
"command": "docker",
"args": [
"run",
"--rm",
"-i",
"-e", "GITLAB_PERSONAL_ACCESS_TOKEN=<YOUR_TOKEN>",
"-e", "GITLAB_API_URL=https://gitlab.com/api/v4",
"-e", "DISABLED_HANDLERS=get_file_contents",
"-e", "ALLOWED_READ_PROJECTS=your-org/public-repo,your-org/docs-repo",
"gitlab-zero-leak-mcp"
]
}
}
}
NPX
{
"mcpServers": {
"gitlab-zero-leak": {
"command": "npx",
"args": [
"-y",
"gitlab-zero-leak-mcp"
],
"env": {
"GITLAB_PERSONAL_ACCESS_TOKEN": "<YOUR_TOKEN>",
"GITLAB_API_URL": "https://gitlab.com/api/v4",
"DISABLED_HANDLERS": "get_file_contents",
"ALLOWED_READ_PROJECTS": "your-org/public-repo,your-org/docs-repo"
}
}
}
}
Build and Run
Using Docker Compose
-
Create a
docker-compose.ymlfile:version: '3' services: gitlab-mcp: build: . container_name: gitlab-zero-leak-mcp restart: always env_file: - .env ports: - "127.0.0.1:8081:3000" # Only expose to localhost by default user: "node" # Run as non-root user -
Create a
.envfile based on.env.example:GITLAB_PERSONAL_ACCESS_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxx GITLAB_API_URL=https://gitlab.com/api/v4 # Zero-Leak DISABLED_HANDLERS=get_file_contents ALLOWED_READ_PROJECTS=my-group/public-repo -
Run with Docker Compose:
docker-compose up -d
Manual Docker Build
docker build -t gitlab-zero-leak-mcp .
docker run -i --env-file .env gitlab-zero-leak-mcp
Environment Variables
| Variable | Description |
|---|---|
| GITLAB_PERSONAL_ACCESS_TOKEN | Your GitLab Token |
| GITLAB_API_URL | Your GitLab API endpoint |
| DISABLED_HANDLERS | Comma-separated disabled operations |
| ALLOWED_READ_PROJECTS | Comma-separated whitelist |
Security Model
Threat Model & Mitigations
| Threat | Mitigation |
|---|---|
| AI/LLM client accesses source code | get_file_contents handler disabled by default |
| Unintended read access to any repo | Read operations restricted by ALLOWED_READ_PROJECTS allowlist |
| Leaked GitLab token or misuse | .env excluded from version control; recommend least-privilege tokens |
| External access to MCP Server | Docker binds only to internal IP (e.g., 192.168.x.x); not exposed publicly |
| Elevated privileges in container | Dockerfile runs as non-root user |
| Using standard MCP server | Server-side deployment restricts all MCP access through Zero-Leak instance |
| Misconfigured API tokens | Recommend tokens without read_repository permission |
Recommended Practices
- Use tokens without
read_repositorypermission - Always set
DISABLED_HANDLERS=get_file_contents - Carefully manage your
ALLOWED_READ_PROJECTSlist - Never commit
.envfiles to version control - Use token scopes that exclude admin-level privileges
- Regularly review audit logs for unauthorized access attempts
- Periodically review and rotate API tokens
Testing
- Test if handler disabling works:
echo '{"id":"1","method":"listTools","params":{}}' | GITLAB_PERSONAL_ACCESS_TOKEN=your_token node dist/index.js
# Verify get_file_contents is missing
- Test allowlist restrictions:
echo '{"id":"2","method":"callTool","params":{"name":"get_file_contents","arguments":{"project_id":"unauthorized-project","file_path":"file.txt"}}}' | DISABLED_HANDLERS="" GITLAB_PERSONAL_ACCESS_TOKEN=your_token node dist/index.js
# Should fail with "not allowed" message
- Test other operations:
echo '{"id":"3","method":"callTool","params":{"name":"search_repositories","arguments":{"search":"test"}}}' | GITLAB_PERSONAL_ACCESS_TOKEN=your_token node dist/index.js
# Should return search results
Troubleshooting
Common Issues
-
Authentication Errors
- Verify your Personal Access Token is valid and has appropriate permissions
- Check for typos in the token
-
Missing Tools
- If tools are missing from the listTools response, verify your
DISABLED_HANDLERSsetting
- If tools are missing from the listTools response, verify your
-
Permission Denied
- Ensure your token has the necessary permissions for the operation
- Check that projects needing read access are in the
ALLOWED_READ_PROJECTSlist
Logs
-
View Docker logs:
docker logs gitlab-zero-leak-mcp -
Check for audit log entries:
docker logs gitlab-zero-leak-mcp | grep "[AUDIT]"
Security Model
See SECURITY.md for more detailed security information.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This MCP server is licensed under the MIT License. This means you are free to use, modify, and distribute the software, subject to the terms and conditions of the MIT License. For more details, please see the LICENSE file in the project repository.