Integrates via the GitHub MCP to perform repository management tasks such as creating issues and pull requests, with granular policy controls for approvals.
Enables the ability to send and manage emails, with sensitive write operations requiring manual 2FA approval via a Telegram bot.
Provides a unified integration for Google Workspace services, enabling AI tools to interact with Gmail, Calendar, and Drive through an aggregated endpoint.
Allows listing and managing calendar events, providing the AI with access to the user's schedule and events.
Provides tools for searching and managing files within Google Drive, governed by configurable permission policies.
Acts as a security and management layer, using a bot to handle 2FA approvals for sensitive operations, manage temporary access grants, and monitor server status.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@PorteroSend an email to john@example.com about the project status update."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Portero
A self-hosted MCP (Model Context Protocol) gateway that sits between Claude Code and multiple MCP servers, providing:
MCP Aggregation — Connect multiple MCPs and expose them as one unified endpoint
Data Anonymization — Bidirectional fake↔real data replacement for privacy
Async 2FA Approvals — Non-blocking Telegram approval flow with task tracking
Permission Policies — Allow/deny/require-approval per tool
Remote Access — HTTPS endpoint accessible from anywhere
Architecture
┌─────────────────────────────────────────────────────────────┐
│ TELEGRAM BOT │
│ /status, /grant, /revoke, /tasks, approval callbacks │
│ Executes approved tasks asynchronously │
└─────────────────────┬───────────────────────────────────────┘
│
┌─────────────────────▼───────────────────────────────────────┐
│ PORTERO │
│ ┌────────────────────────────────────────────────────────┐│
│ │ HTTP Server (Express) ││
│ │ - POST /mcp/message (JSON-RPC, Bearer auth) ││
│ │ - GET /health ││
│ └────────────────────────────────────────────────────────┘│
│ ┌────────────────────────────────────────────────────────┐│
│ │ Middleware Pipeline ││
│ │ 1. Anonymization (fake→real on requests) ││
│ │ 2. Policy Check (allow/deny/require-approval) ││
│ │ 3. If approval needed → create task, return pending ││
│ │ 4. If allowed → route to child MCP immediately ││
│ │ 5. Anonymization (real→fake on responses) ││
│ └────────────────────────────────────────────────────────┘│
│ ┌────────────────────────────────────────────────────────┐│
│ │ Task Store (data/tasks.json) ││
│ │ pending-approval → approved → executing → completed ││
│ └────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘
│ stdio
┌─────────────┼─────────────┬─────────────┐
▼ ▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ MCP 1 │ │ MCP 2 │ │ MCP 3 │ │ MCP 4 │
│(github) │ │(filesys)│ │(google) │ │(stripe) │
└─────────┘ └─────────┘ └─────────┘ └─────────┘Prerequisites
Node.js 20+ (LTS recommended)
Telegram Bot (create via @BotFather)
Your Telegram Chat ID (get from @userinfobot)
Quick Start
1. Clone and Install
git clone <your-repo-url>
cd portero
npm install2. Configure Environment
cp .env.example .env
# Edit .env with your settingsRequired settings in .env:
# Generate a secure token
BEARER_TOKEN=$(openssl rand -hex 32)
# Get from @BotFather
TELEGRAM_BOT_TOKEN=123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
# Get from @userinfobot
TELEGRAM_ADMIN_CHAT_ID=123456789
# Your real info (for anonymization)
REAL_NAME="Your Name"
REAL_EMAIL="your@email.com"3. Configure MCP Servers
Edit config/mcps.json to define which MCP servers to connect:
{
"mcps": [
{
"name": "filesystem",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/workspace"],
"env": {}
},
{
"name": "github",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
}
}
]
}4. Configure Google Workspace (Optional)
To add Gmail, Calendar, and Drive integration via workspace-mcp:
Create a Google Cloud project at console.cloud.google.com
Enable APIs: Gmail API, Google Calendar API, Google Drive API
Create OAuth 2.0 credentials: APIs & Services → Credentials → Create Credentials → OAuth client ID → Desktop app
Set environment variables in
.env:GOOGLE_OAUTH_CLIENT_ID=your-client-id.apps.googleusercontent.com GOOGLE_OAUTH_CLIENT_SECRET=your-client-secretFirst run: The workspace-mcp server will open a browser for OAuth consent. Approve the requested scopes.
Headless / Docker: Run once locally to complete the OAuth flow, then copy the token cache into the container.
If GOOGLE_OAUTH_CLIENT_ID and GOOGLE_OAUTH_CLIENT_SECRET are not set, Portero will skip the Google MCP and start without it.
The Google tools appear as google/send_email, google/list_events, google/search_files, etc. Write operations (send, create, delete) require Telegram approval; reads are allowed by default. See config/policies.json for the full list.
5. Configure Notion (Optional)
To add Notion integration:
Create a Notion integration at notion.so/my-integrations
Copy the Internal Integration Secret (starts with
ntn_)Share pages/databases with the integration: Open a page → ... → Connections → Add your integration
Set environment variable in
.env:NOTION_API_TOKEN=ntn_your-token-here
If NOTION_API_TOKEN is not set, Portero will skip the Notion MCP and start without it. Read operations (search, retrieve pages/blocks) are allowed by default; write operations (create/update/delete) require Telegram approval.
6. Configure Stripe (Optional)
To add Stripe integration for payment management:
Get your Stripe API key from dashboard.stripe.com/apikeys
Set environment variable in
.env:STRIPE_API_KEY=sk_test_your-key-here
If STRIPE_API_KEY is not set, Portero will skip the Stripe MCP and start without it.
Default policies:
Read tools (list/get customers, invoices, payments, subscriptions, balance) —
allowWrite tools (create customer, invoice, payment, refund, subscription) —
require-approval
7. Configure Data Anonymization
Edit config/replacements.json to define fake↔real mappings:
{
"replacements": [
{
"fake": "John Doe",
"real": "${REAL_NAME}",
"bidirectional": true
},
{
"fake": "john@example.com",
"real": "${REAL_EMAIL}",
"bidirectional": true,
"caseSensitive": false
}
]
}8. Configure Policies
Edit config/policies.json to set permission rules:
{
"policies": {
"github/create_issue": "allow",
"github/create_pull_request": "require-approval",
"filesystem/write_file": "require-approval",
"filesystem/read_file": "allow",
"filesystem/delete_file": "deny",
"*": "allow"
},
"defaultPolicy": "allow"
}9. Generate SSL Certificates (Optional)
./scripts/generate-certs.shOr skip SSL for local testing (uses HTTP).
10. Start the Gateway
# Development mode (with hot reload)
npm run dev
# Production mode
npm run build
npm startDocker Deployment
# Build and start with docker-compose
docker-compose up -d
# View logs
docker-compose logs -f
# Stop
docker-compose downConnect from Claude Code
Add to your Claude Code MCP configuration:
{
"mcpServers": {
"portero": {
"transport": "http",
"url": "https://your-server:8443/mcp/message",
"headers": {
"Authorization": "Bearer your-bearer-token-here"
}
}
}
}Add to Claude Code system prompt:
Your identity:
- Name: John Doe
- Email: john@example.com
Use these when asked for personal information.Telegram Bot Commands
Once running, message your bot:
/status- Show connected MCPs, active grants, pending approvals/grant <pattern> <duration>- Grant temporary accessExamples:
/grant github/* 30m,/grant * 1h
/revoke- Revoke all active grants/allow <pattern>- Persistently allow a tool/pattern (no approvals needed)/deny <pattern>- Persistently deny a tool/pattern/rules- List persistent rules/unrule <id>- Remove a persistent rule/tasks- Show recent tasks grouped by status/pending- Show pending approval requests/logs- Show recent audit logs/help- Show all commands
How Async Approval Works
Portero uses a fully asynchronous approval flow — the HTTP request is never blocked waiting for Telegram approval.
Claude Code calls a tool (e.g.,
github/create_pull_request)Gateway checks policy: requires approval
Gateway creates a task (status:
pending-approval), sends Telegram message with Approve/Deny buttons, and returns immediately with a task IDClaude Code receives
{ status: "pending-approval", taskId: "..." }and can continue workingAdmin approves/denies via Telegram buttons
If approved, Portero executes the tool in the background and stores the result
Claude Code calls
portero/check_taskwith the task ID to retrieve the resultIf not ready yet, Claude Code can call
portero/check_taskagain later
This means:
No timeout pressure — approvals can happen whenever
Claude Code stays responsive while waiting
Multiple approvals can be pending simultaneously
Virtual Tools
Portero injects these virtual tools alongside your MCP tools:
Tool | Description |
| Search available tools by keyword or category |
| Call any tool by its full name (useful for non-pinned tools) |
| Check status/result of a pending or completed async task |
| List recent tasks with optional status filter |
Configuration Reference
Data Anonymization
Replacements support:
Bidirectional — Replace in both directions (fake↔real)
One-way — Replace only fake→real, use
responseReplacementfor responsesCase sensitivity — Set
caseSensitive: falsefor case-insensitive matching
Permission Policies
Policy actions:
allow— Allow without approvaldeny— Block completelyrequire-approval— Request Telegram approval (async)
Patterns support wildcards:
github/*— All GitHub tools*/delete_*— All delete operations*— All tools
Policy priority (highest first):
Persistent rules (from Telegram
/allow,/denycommands)Config exact matches (from
config/policies.json)Config pattern matches (wildcards)
Default policy
Temporary Grants
Skip approval for a limited time:
/grant github/* 30m # Grant GitHub access for 30 minutes
/grant * 1h # Grant all access for 1 hour
/revoke # Revoke all grants immediatelySecurity Considerations
Bearer Token — Generate a strong random token:
openssl rand -hex 32SSL/TLS — Use HTTPS in production (Let's Encrypt, self-signed, or reverse proxy)
Telegram — Only your admin chat ID can control the bot
Firewall — Restrict gateway port (8443) to authorized IPs
Environment Variables — Never commit
.envto git
Development
Project Structure
portero/
├── src/
│ ├── index.ts # Entry point
│ ├── config/ # Config loader & types
│ ├── gateway/ # HTTP server & MCP handler
│ ├── mcp/ # MCP client management
│ ├── middleware/ # Anonymizer, policy, approval
│ ├── telegram/ # Telegram bot & admin store
│ ├── db/ # File-backed JSON storage
│ ├── storage/ # Atomic file operations & paths
│ └── utils/ # Logger, crypto
├── config/ # JSON config files
├── data/ # Runtime data (auto-created)
└── scripts/ # Helper scriptsBuild Commands
npm run dev # Development with hot reload
npm run build # Compile TypeScript
npm start # Start production buildStorage
File-backed JSON storage in ./data/:
approvals.json— Legacy pending approvals (kept for backward compatibility)tasks.json— Async task tracking (pending → approved → executing → completed)grants.json— Temporary access grantsrules.json— Persistent policy rules (from /allow, /deny commands)audit.ndjson— Append-only audit log (NDJSON format)
Troubleshooting
Gateway won't start
Check Node.js version:
node -v(should be 20+)Verify
.envfile exists and has all required variablesCheck logs in
./logs/combined.log
MCP connection fails
Verify MCP command is correct in
config/mcps.jsonCheck MCP is installed:
npx -y @modelcontextprotocol/server-github --versionCheck environment variables are set (e.g.,
GITHUB_TOKEN)MCPs with missing env vars are skipped automatically (non-blocking)
Telegram bot not responding
Verify bot token is correct
Check admin chat ID matches your Telegram ID
Ensure bot was started with
/start
Claude Code can't connect
Verify bearer token matches in Claude Code config
Check SSL certificates if using HTTPS
Test with
curl:curl -X POST https://localhost:8443/health
Contributing
Contributions welcome! Please:
Fork the repository
Create a feature branch
Make your changes
Submit a pull request
License
MIT License - see LICENSE file for details
Support
GitHub Issues: [Report bugs or request features]
Built for Claude Code users who want privacy, security, and control over their MCP connections.
This server cannot be installed
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.