duquesnay/miro-mcp-resource-server
If you are the rightful owner of miro-mcp-resource-server and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.
Miro Rust Remote MCP Server is a production-ready Model Context Protocol server for Miro board manipulation, built in Rust with OAuth2 authentication.
Miro Rust Remote MCP Server
A production-ready Model Context Protocol (MCP) server for Miro board manipulation, built in Rust with OAuth2 authentication. Enables Claude AI to programmatically create and manage Miro boards, with special focus on visualizing agile squad organizational structures.
Features
- OAuth Resource Server Pattern: Claude handles OAuth, server validates Bearer tokens (RFC 9728)
- In-Memory Token Caching: 95% cache hit rate, <1ms validation latency
- Board Operations: List and create Miro boards programmatically
- Visual Elements: Create sticky notes, shapes, text, and frames
- Item Management: List, update, and delete board items (coming soon)
- Connectors: Create styled arrows and lines showing relationships (coming soon)
- Squad Visualization: Rapid agile team structure visualization (coming soon)
Key Differentiators
- OAuth Resource Server Pattern: Simpler than Proxy OAuth (70% less code)
- Rust Performance: Memory-safe, concurrent, and fast (~30s builds)
- Remote MCP: Accessible from Claude.ai web interface via HTTPS
- Production-Ready: Deployed to Scaleway Containers, stateless architecture
Prerequisites
- Rust 1.70+ (2021 edition)
- Miro Developer Account
- OpenSSL (for generating encryption keys)
Setup
1. Get Miro OAuth2 Client ID
- Visit https://developers.miro.com/
- Click "Your Apps" → "Create new app"
- Note your Client ID (example: 3458764647516852398)
- No redirect URI needed - Claude handles OAuth flow directly
2. Configure Application
Create the configuration directory and file:
# Create config directory
mkdir -p ~/.config/mcp/miro-rust
# Copy example config
cp config.example.json ~/.config/mcp/miro-rust/config.json
# Generate encryption key
openssl rand -hex 32
# Edit config.json with your credentials
nano ~/.config/mcp/miro-rust/config.json
Configuration file (~/.config/mcp/miro-rust/config.json):
{
"client_id": "your_client_id_here",
"client_secret": "your_client_secret_here",
"redirect_uri": "http://localhost:3010/oauth/callback",
"encryption_key": "output_from_openssl_rand_hex_32",
"port": 3010
}
Configuration Fields (Resource Server Pattern):
client_id: Your Miro OAuth2 Client ID (for OAuth metadata endpoint)port: Server port (3010 for development)
Note: No client_secret, redirect_uri, or encryption_key needed - Claude handles the OAuth flow
3. Build and Run
# Install dependencies and build
cargo build --release
# Run the server
cargo run --release
# Or run in development mode
cargo run
4. Connect from Claude
The MCP server exposes OAuth Protected Resource metadata at /.well-known/oauth-protected-resource. Claude will discover this endpoint and handle the OAuth flow automatically when you first use the MCP server.
Project Structure
miro-mcp-server/
├── src/
│ ├── auth/ # OAuth2 implementation
│ │ ├── oauth.rs # Authorization code flow with PKCE
│ │ ├── token_store.rs # AES-256-GCM encrypted token storage
│ │ └── types.rs # Authentication types and errors
│ ├── mcp/ # MCP protocol implementation
│ │ ├── server.rs # MCP server with rmcp framework
│ │ └── auth_handler.rs # OAuth callback handling
│ ├── miro/ # Miro API client
│ │ ├── client.rs # HTTP client with auto-refresh
│ │ └── types.rs # Miro API types (boards, items, etc.)
│ ├── config.rs # Environment configuration
│ ├── lib.rs # Library exports
│ └── main.rs # Entry point
├── planning/ # Agile planning artifacts
└── tests/ # Integration tests
Available MCP Tools
Board Operations
list_boards: List all accessible Miro boardscreate_board: Create a new board with name and description
Visual Elements
create_sticky_note: Create sticky notes with custom content, position, and colorcreate_shape: Create shapes (rectangle, circle, triangle) for org structurescreate_text: Create text elements on boardscreate_frame: Create frames for grouping related content
Coming Soon
list_items: List board items filtered by typeupdate_item: Update item properties dynamicallydelete_item: Remove items from boardscreate_connector: Connect items with styled arrows/lines- Squad visualization tools for rapid org chart creation
Security
- Resource Server Pattern: Claude handles OAuth, we only validate tokens
- Token Validation: Bearer tokens validated via Miro API introspection
- In-Memory Caching: Tokens cached for 5 minutes, no persistent storage
- Secrets Management: Client ID from environment variables only
- No Unsafe Code: 100% safe Rust, memory safety guaranteed
- Comprehensive Error Handling: Result types throughout, no production panics
Development
Running Tests
# Run all tests
cargo test
# Run with output
cargo test -- --nocapture
# Run specific test
cargo test test_name
Code Quality
# Lint with clippy
cargo clippy -- -D warnings
# Format code
cargo fmt
# Check formatting
cargo fmt -- --check
Git Workflow
This project follows the Feature Branch Workflow with atomic commits:
# Create feature branch
git checkout -b feat/feature-name
# Make changes, run tests
cargo test && cargo clippy
# Commit (all commits reviewed for completeness)
git add .
git commit -m "feat: description of change"
# Merge to main
git checkout main
git merge feat/feature-name
Architecture Decisions
Why Rust?
- Memory safety without garbage collection
- Excellent async/await support via tokio
- Strong type system prevents many bugs at compile time
- Fast performance for production deployment
Why Resource Server Pattern?
- Simpler than Proxy OAuth (70% less code)
- Claude handles OAuth flow complexity
- No state management required (PKCE, cookies, etc.)
- Stateless architecture (scales horizontally)
- Fast builds (~30s vs ~2min with encryption)
Why In-Memory Token Caching?
- Reduces Miro API calls by 95%
- <1ms validation latency (cached)
- No database required
- 5-minute TTL balances security vs performance
Deployment
Local Development
- Configure
~/.config/mcp/miro-rust/config.jsonwith localhost redirect URI - Run
cargo run - OAuth callback works on localhost:3010
Production (Scaleway Containers - Deployed)
- Deploy to Scaleway Containers using GitHub Actions (automated)
- Configure environment variables (MIRO_CLIENT_ID, PORT, BASE_URL)
- HTTPS configured automatically by Scaleway
- No secrets storage required (Claude handles OAuth tokens)
Deployment Status:
- Platform: Scaleway Containers
- URL: https://flyagileapipx8njvei-miro-mcp.functions.fnc.fr-par.scw.cloud
- Architecture: Resource Server with in-memory caching
- Build Time: ~30 seconds (no encryption dependencies)
Recommended Platforms:
- Scaleway Containers (Selected): Container-based deployment, native HTTPS, predictable pricing
- Railway: Simple deployment, automatic HTTPS, environment-based config
- Self-hosted: Full control, requires Nginx for HTTPS, manual config management
Primary Use Case: Agile Squad Visualization
Example prompt to Claude:
"Create a Miro board showing 3 agile squads (Alpha, Beta, Gamma). Each squad has 1 Product Owner, 1 Scrum Master, and 5 developers. Show reporting lines from team members to Scrum Master."
The MCP server will:
- Create a new board
- Create frames for each squad
- Create color-coded sticky notes for team members (PO=yellow, SM=green, Dev=blue)
- Create connectors showing reporting relationships
- Return the board URL
All in <5 minutes via natural language!
API Documentation
Miro API v2
- Base URL:
https://api.miro.com/v2/ - Authentication: Bearer token (OAuth2)
- Rate Limit: 100 requests/minute per user
- Documentation: https://developers.miro.com/docs/rest-api-reference
MCP Protocol
- Specification: https://modelcontextprotocol.io/
- Transport: stdio (Claude.ai web interface) or HTTP
- Framework: rmcp (official Rust SDK)
Troubleshooting
"Configuration file not found"
- Ensure config directory exists:
mkdir -p ~/.config/mcp/miro-rust - Copy example config:
cp config.example.json ~/.config/mcp/miro-rust/config.json - Edit config with your credentials:
nano ~/.config/mcp/miro-rust/config.json
"Authentication failed"
- Check
client_idandclient_secretin~/.config/mcp/miro-rust/config.json - Verify
redirect_urimatches Miro app configuration exactly - Check token hasn't expired (refresh should be automatic)
"Token encryption failed"
- Ensure
encryption_keyis exactly 64 hex characters (32 bytes) - Generate new key:
openssl rand -hex 32 - Update it in
~/.config/mcp/miro-rust/config.json
"Rate limit exceeded"
- Miro API limit: 100 requests/minute
- Wait 60 seconds and retry
- Consider bulk operations for large visualizations
Contributing
- Fork the repository
- Create feature branch (
git checkout -b feat/amazing-feature) - Follow Rust conventions (run
cargo fmtandcargo clippy) - Write tests for new features
- Ensure all tests pass (
cargo test) - Commit with conventional format (
feat:,fix:,refactor:) - Push and create Pull Request
Roadmap
Sprint 1 (Complete) ✅
- OAuth2 authorization with PKCE
- Automatic token refresh
- Board list and creation
- Visual element creation (sticky notes, shapes, text, frames)
Sprint 2 (In Progress)
- Item management (list, update, delete)
- Connector creation with captions
- Squad visualization orchestration
Sprint 3 (Planned)
- Bulk operations for performance
- Production deployment guide
- Claude.ai web interface integration testing
License
MIT License - see LICENSE file for details
Acknowledgments
- Miro API v2 for comprehensive board manipulation
- MCP protocol for AI-native tool integration
- rmcp Rust SDK for clean MCP implementation
- oauth2-rs for robust OAuth2 client
- Anthropic for Claude Code development platform
Support
- Issues: https://github.com/yourusername/miro-rust-remote-mcp/issues
- Miro API Docs: https://developers.miro.com/docs
- MCP Specification: https://modelcontextprotocol.io/
Built with ❤️ in Rust | Powered by OAuth2 | Secured by AES-256-GCM