fx0-mcp-server

function0llc/fx0-mcp-server

3.2

If you are the rightful owner of fx0-mcp-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.

FX0 MCP Server is a production-ready Model Context Protocol server that integrates Microsoft Graph capabilities for Email, Calendar, To Do, and Google Custom Search, supporting both STDIO and HTTP transport modes.

Tools
21
Resources
0
Prompts
0

FX0 MCP Server

A production-ready Model Context Protocol (MCP) server that exposes Microsoft Graph capabilities for Email, Calendar, To Do (Reminders), and Google Custom Search. (Other tools comming soon!) This server supports both STDIO and HTTP transport modes. Can be run local or in Docker.

Python 3.11+ License: MIT Code style: black

🚀 Features

  • Email Management: List, search, send, reply, forward messages with attachment support
  • Calendar Integration: Event management, meeting scheduling, availability checks
  • Web Search: Google Custom Search API integration for web information retrieval
  • File Utilities (fx0.tool.write_*): Write TXT, CSV, DOCX, XLSX, PDF, and PPTX files to the server temp directory
  • To Do Lists: Task creation, management, and tracking with due dates
  • Microsoft Teams: Channel/chat messaging, presence lookup, meeting creation
  • SharePoint/OneDrive: File discovery, uploads/downloads, sharing links
  • Auth Tools: Device code login initiation, polling, status, logout
  • Production Ready: OAuth2 with MSAL, encrypted token caching, comprehensive error handling
  • Multiple Transports: STDIO (primary) and HTTP/SSE support
  • Observability: Structured logging, correlation IDs, optional OpenTelemetry integration

🏗️ Architecture

  • Authentication: Delegated user flow via device code (default) with encrypted per-user token cache
  • Fallback Auth: Optional confidential client mode for service scenarios
  • Transports: STDIO for MCP clients + HTTP/SSE for web integration
  • Resilience: Exponential backoff, circuit breaker, rate limit handling
  • Security: Least-privilege scopes, PII redaction, SSRF protection

📦 Installation

From PyPI (Coming Soon)

pip install fx0-mcp-server

From Source

git clone https://github.com/your-org/fx0-mcp-server.git
cd fx0-mcp-server
pip install -e .

With Optional Dependencies

# For development
pip install -e ".[dev]"

# For OpenTelemetry support
pip install -e ".[telemetry]"

# For keyring integration
pip install -e ".[keyring]"

# For Google Search API support (google.tool.search)
pip install google-api-python-client

# For File Utilities (optional format libraries)
# TXT/CSV require no extras. For others, install as needed:
# DOCX  -> pip install python-docx
# XLSX  -> pip install openpyxl
# PDF   -> pip install reportlab
# PPTX  -> pip install python-pptx

⚙️ Configuration

Environment Variables

Copy to .env and configure:

# Required - Azure App Registration (Delegated Flow)
TENANT_ID=your-tenant-id
CLIENT_ID=your-client-id

# Microsoft Graph Configuration
GRAPH_BASE_URL=https://graph.microsoft.com/v1.0
AUTHORITY=https://login.microsoftonline.com/{tenant}

# Scopes (space-separated, minimal per domain)
SCOPES="Mail.Read Mail.Send Calendars.ReadWrite Tasks.ReadWrite Chat.ReadWrite Files.ReadWrite offline_access"

# Google Search Configuration (Optional - for google.tool.search)
GOOGLE_SEARCH_API_KEY=your-google-search-api-key
GOOGLE_SEARCH_ENGINE_ID=your-custom-search-engine-id

# Transport Configuration
TRANSPORT=stdio  # or "http"
HOST=127.0.0.1   # HTTP only
PORT=8000        # HTTP only
SSE_ENABLED=true # HTTP only

# Optional - Confidential Client Mode
# CLIENT_SECRET=your-client-secret
# AUTH_MODE=confidential

# Logging and Observability
LOG_LEVEL=INFO
LOG_FORMAT=json
# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317

# Token Cache
TOKEN_CACHE=~/.fx0_cache/token_cache.bin
# TOKEN_CACHE=redis://localhost:6379/0
# CACHE_ENCRYPTION_KEY=your-encryption-key

Azure App Registration Setup

For Delegated Flow (Default)
  1. Register app in Azure Portal
  2. Authentication → Add platform → Mobile and desktop applications
  3. Add redirect URI: http://localhost
  4. API permissions → Add permissions → Microsoft GraphDelegated permissions
  5. Add minimal scopes per domain:
    • Mail.Read, Mail.Send (Email)
    • Calendars.ReadWrite (Calendar)
    • Tasks.ReadWrite (To Do)
    • Chat.ReadWrite, ChannelMessage.Send (Teams)
    • Files.ReadWrite (SharePoint/OneDrive)
    • offline_access (Token refresh)
  6. Grant admin consent (if required by organization)

Google Search API Setup (Optional - for google.tool.search)

  1. Go to the Google Cloud Console
  2. Create a new project or select an existing one
  3. Enable the Custom Search API
  4. Create credentials (API key) for the Custom Search API
  5. Set up a Custom Search Engine
  6. Configure your search engine and note the Search Engine ID
  7. Set the environment variables:
    • GOOGLE_SEARCH_API_KEY: Your Google API key
    • GOOGLE_SEARCH_ENGINE_ID: Your Custom Search Engine ID
For Confidential Client (Optional)
  1. Follow steps 1-6 above, but choose Application permissions instead
  2. Certificates & secrets → New client secret → Copy value
  3. Set CLIENT_SECRET and AUTH_MODE=confidential in .env

🚦 Usage

STDIO Transport (MCP Clients)

Windsurf Configuration
{
  "mcpServers": {
    "fx0": {
      "command": "fx0-mcp",
      "env": {
        "TENANT_ID": "your-tenant-id",
        "CLIENT_ID": "your-client-id",
        "SCOPES": "Mail.Read Mail.Send Calendars.ReadWrite Tasks.ReadWrite Chat.ReadWrite Files.ReadWrite offline_access"
      }
    }
  }
}
LM Studio Configuration
{
  "name": "FX0 Server",
  "executable": "fx0-mcp",
  "args": ["--transport", "stdio"],
  "env": {
    "TENANT_ID": "your-tenant-id", 
    "CLIENT_ID": "your-client-id"
  }
}

HTTP/SSE Transport

# Start HTTP server
fx0-mcp --transport http --host 127.0.0.1 --port 8000

# Health check
curl http://localhost:8000/mcp/health

# Initialize MCP session
curl -X POST http://localhost:8000/mcp/initialize \
  -H "Content-Type: application/json" \
  -d '{"protocolVersion": "2024-11-05", "clientInfo": {"name": "test-client", "version": "1.0.0"}}'

# List available tools
curl http://localhost:8000/mcp/tools

# Call a tool
curl -X POST http://localhost:8000/mcp/tools/call \
  -H "Content-Type: application/json" \
  -d '{"name": "m365.mail.list_messages", "arguments": {"folder_id": "inbox", "top": 5}}'

# Connect to Server-Sent Events stream
curl -N -H "Accept: text/event-stream" http://localhost:8000/mcp/sse

# Test with development mode for interactive API docs
fx0-mcp --transport http --dev --no-auth
# Then visit: http://localhost:8000/docs
HTTP API Endpoints
EndpointMethodDescription
/mcp/healthGETServer health check with tool count
/mcp/initializePOSTInitialize MCP protocol session
/mcp/toolsGETList all available tools with schemas
/mcp/tools/callPOSTExecute a specific tool
/mcp/sseGETServer-Sent Events stream for real-time updates
/docsGETInteractive API documentation (dev mode only)
Server-Sent Events

The SSE endpoint provides real-time communication:

// JavaScript example
const eventSource = new EventSource('http://localhost:8000/mcp/sse');

eventSource.onmessage = function(event) {
  const data = JSON.parse(event.data);
  console.log('Received:', data);
  
  if (data.type === 'connected') {
    console.log('Connected with ID:', data.connection_id);
  } else if (data.type === 'heartbeat') {
    console.log('Server heartbeat:', data.server_status);
  }
};

eventSource.onerror = function(error) {
  console.error('SSE error:', error);
};

🔧 Available Tools

Tools follow a hierarchical naming convention: <category>.<domain>.<action>

Tools Description

  • google.tool.search: Search the web for information using Google Custom Search API
  • m365.auth.initiate_login: Start device code sign-in and return instructions
  • m365.auth.poll_login: Wait for device code sign-in to complete
  • m365.auth.status: Check current auth status and identity
  • m365.auth.logout: Clear cached tokens and sign out
  • m365.calendar.create_event: Create a new calendar event
  • m365.calendar.delete_event: Delete a calendar event
  • m365.calendar.find_meeting_times: Find suggested meeting times based on attendee availability
  • m365.calendar.get_event: Get detailed information about a specific calendar event
  • m365.calendar.list_calendars: List all calendars available to the user
  • m365.calendar.list_events: List calendar events within a time range
  • m365.calendar.update_event: Update an existing calendar event
  • m365.mail.create_folder: Create a new email folder
  • m365.mail.delete_message: Delete an email message
  • m365.mail.forward: Forward an email message
  • m365.mail.get_message: Get detailed information about a specific email message
  • m365.mail.list_folders: List email folders
  • m365.mail.list_messages: List email messages from a folder with optional filtering
  • m365.mail.move_message: Move an email message to a different folder
  • m365.mail.reply: Reply to an email message
  • m365.mail.send_email: Send a new email message

Google

Search (fx0.tool.*)

  • google.tool.search(query, num_results?, safe_search?, language?, country?)

MS Graph

Authentication (m365.auth.*)

  1. Initiate login and display to the user:

    • Tool: m365.auth.initiate_login
    • Input (optional): { "scopes": "Mail.Read Mail.Send Calendars.ReadWrite" }
    • Output: verification_uri, user_code, and flow payload
  2. Poll for completion and store token server-side:

    • Tool: m365.auth.poll_login
    • Input: { "flow": <flow from step 1>, "wait": true }
    • Output: status (succeeded|pending|expired) and user identity on success
  3. Check status any time:

    • Tool: m365.auth.status
  4. Logout:

    • Tool: m365.auth.logout

Notes:

  • Device code flow is tenant-aware via TENANT_ID/AUTHORITY.
  • Reserved scopes (openid, profile, offline_access) are filtered automatically for the device flow request.
  • Access tokens are never returned by tools; they are cached and used server-side.

Email (m365.mail.*)

  • m365.mail.list_messages(folder_id?, query?, from_date?, to_date?, top?, skip?)
  • m365.mail.get_message(message_id, include_body?, include_headers?)
  • m365.mail.send_email(to[], subject, body_html|body_text, cc[], bcc[], attachments[])
  • m365.mail.reply(message_id, body, reply_all?) / m365.mail.forward(message_id, to[], body, attachments[])
  • m365.mail.move_message(message_id, destination_folder_id) / m365.mail.delete_message(message_id)
  • m365.mail.create_folder(name, parent_id?) / m365.mail.list_folders(parent_id?)

Calendar (m365.calendar.*)

  • m365.calendar.list_calendars() / m365.calendar.list_events(calendar_id, time_min, time_max, tz, query?)
  • m365.calendar.get_event(event_id) / m365.calendar.create_event(calendar_id, subject, start, end, attendees[], location?, body?)
  • m365.calendar.update_event(event_id, fields) / m365.calendar.delete_event(event_id)
  • m365.calendar.find_meeting_times(attendees[], time_constraints, location?, duration, tz)

To Do (m365.tasks.*) (Future Release)

  • m365.tasks.list_task_lists() / m365.tasks.create_task_list(name) / m365.tasks.delete_task_list(id)
  • m365.tasks.list_tasks(list_id, filter?, order_by?) / m365.tasks.get_task(id)
  • m365.tasks.create_task(list_id, title, due?, importance?, categories[], body?)
  • m365.tasks.update_task(id, fields) / m365.tasks.complete_task(id) / m365.tasks.delete_task(id)

Teams (m365.teams.*) (Future Release)

  • m365.teams.list_teams() / m365.teams.list_channels(team_id) / m365.teams.list_chats()
  • m365.teams.get_channel_messages(team_id, channel_id, top?, cursor?)
  • m365.teams.post_channel_message(team_id, channel_id, body, mentions[])
  • m365.teams.get_chat_messages(chat_id, top?, cursor?) / m365.teams.post_chat_message(chat_id, body, mentions[])
  • m365.teams.create_online_meeting(subject, start, end, attendees[])

SharePoint/OneDrive (m365.files.*) (Future Release)

  • m365.files.list_sites(query?) / m365.files.list_drives(site_id?)
  • m365.files.list_items(drive_id, path?, top?, cursor?)
  • m365.files.get_item(drive_id, item_id) / m365.files.download_item(drive_id, item_id, dest)
  • m365.files.upload_item(drive_id, path, local_file, conflict_behavior=replace|rename)
  • m365.files.create_share_link(drive_id, item_id, type=view|edit, scope=anonymous|organization, expiry?)

Users (m365.users.*) (Future Release)

  • m365.users.get_me() / m365.users.get_user(user_id)
  • m365.users.list_users(query?, top?, skip?)

🧪 Development

Setup

git clone https://github.com/your-org/fx0-mcp-server.git
cd fx0-mcp-server

# Create virtual environment
python -m venv venv
source venv/bin/activate  # Linux/Mac
# or: venv\Scripts\activate  # Windows

# Install development dependencies
pip install -e ".[dev]"

# Setup pre-commit hooks
pre-commit install

Testing

# Unit tests
pytest tests/unit

# Integration tests (requires dev tenant)
pytest tests/integration  

# All tests
pytest

# With coverage
pytest --cov=src/fx0_mcp_server --cov-report=html

Code Quality

# Format code
black src/ tests/

# Lint code  
ruff check src/ tests/

# Type checking
mypy src/

🔒 Security

  • Least Privilege: Minimal scopes per domain, delegated permissions preferred
  • Token Security: Encrypted at-rest caching, never logged, optional keyring integration
  • Transport Security: HTTPS required for production HTTP mode, CORS configuration
  • Input Validation: Pydantic schemas, SSRF protection, path traversal guards
  • Audit: Request correlation IDs, sanitized error responses

📊 Monitoring

  • Health: /mcp/health endpoint with dependency checks
  • Metrics: Optional Prometheus endpoint at /mcp/metrics
  • Logging: Structured JSON logs with correlation IDs
  • Tracing: OpenTelemetry integration for request tracing
  • Alerts: Graph API quota monitoring, authentication failure tracking

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes with tests
  4. Run quality checks (black, ruff, mypy, pytest)
  5. Commit your changes (git commit -m 'Add amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. Open a Pull Request

📝 License

This project is licensed under the MIT License - see the file for details.

🙋 Support

🗺️ Roadmap

  • v0.1.0: STDIO transport, Hierarchical tool taxonomy, Mail + Calendar stub tools (Completed)
  • v0.2.0: Go-Live integration > MS Graph and Google Search Tools, STDIO and HTTP/SSE transport, Docker iamge (Completed)
  • v0.3.0: To Do + Teams tools, advanced search capabilities
  • v0.4.0: SharePoint/OneDrive, file operations
  • v1.0.0: Production hardening, security audit, performance optimization
  • v1.1.0: Webhooks/subscriptions, real-time notifications
  • v1.2.0: Admin tools, compliance helpers, multi-tenant support

Disclaimer: This project is not officially endorsed by Microsoft. Microsoft Graph and related services are trademarks of Microsoft Corporation.

🧰 File Utilities Tools

Six general-purpose tools are available under the fx0.tool.* namespace to export content into files inside the server temp directory (TEMP_DIR, default ./tmp). Each returns a file:// URI and basic metadata:

  • fx0.tool.write_txt (WriteTxtInput → FileCreatedOutput)

    • Inputs: content: str, optional filename, optional subdir
    • Output: { uri, name, mime_type, size_bytes }
  • fx0.tool.write_csv (WriteCsvInput → FileCreatedOutput)

    • Inputs: rows: List[List[Any]] or records: List[Dict], optional headers, optional filename, optional subdir, optional dialect
  • fx0.tool.write_docx (WriteDocxInput → FileCreatedOutput)

    • Requires python-docx
    • Inputs: content: str (newlines become paragraphs), optional filename, optional subdir
  • fx0.tool.write_xlsx (WriteXlsxInput → FileCreatedOutput)

    • Requires openpyxl
    • Inputs: rows or records, optional headers, optional sheet_name, optional filename, optional subdir
  • fx0.tool.write_pdf (WritePdfInput → FileCreatedOutput)

    • Requires reportlab
    • Inputs: content: str, optional filename, optional subdir
  • fx0.tool.write_pptx (WritePptxInput → FileCreatedOutput)

    • Requires python-pptx
    • Inputs: optional title, optional bullets: List[str], optional content: str, optional filename, optional subdir

Notes:

  • When optional libraries are not installed, the tool returns a helpful error message instead of failing.
  • Files are created under TEMP_DIR; you can pass a subdir to organize outputs.