# ProjectSight MCP Server
A scalable, maintainable MCP (Model Context Protocol) server that provides access to the ProjectSight API for managing portfolios, projects, and various record types like ActionItems, RFIs, Submittals, and more.
**This server has been refactored into a modular, teachable structure** that demonstrates best practices for building MCP servers. It's designed to be both production-ready and an excellent learning resource.
## π― Features
- **OAuth2 Authentication**: Automatic token management with caching and refresh
- **Modular Architecture**: Clean separation of concerns, easy to extend
- **Project Operations**: Access project information
- **Record Management**: CRUD operations for:
- ActionItems
- RFIs (Request for Information)
- Submittals
- **Workflow States**: Get workflow states for record types
- **Debug Tools**: Test connection, debug tokens, test different scopes
## π Project Structure
```
projectsight-mcp/
βββ mcp/ # Main MCP server code
β βββ main.py # Entry point - run this to start the server
β βββ config.py # Configuration management
β βββ auth.py # OAuth2 authentication
β βββ client.py # API client for HTTP requests
β βββ utils.py # Utility functions
β βββ tools/ # MCP tools organized by domain
β βββ __init__.py # Tool registration
β βββ debug.py # Debug/diagnostic tools
β βββ projects.py # Project operations
β βββ action_items.py # Action item CRUD
β βββ rfis.py # RFI operations
β βββ submittals.py # Submittal operations
β βββ workflow.py # Workflow state tools
βββ README.md # This file
βββ .env # Environment variables (create this)
```
## π Quick Start
### 1. Install Dependencies
```bash
pip install fastmcp python-dotenv aiohttp uvicorn
```
Or using a virtual environment:
```bash
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install fastmcp python-dotenv aiohttp uvicorn
```
### 2. Configure Credentials
Create a `.env` file in the `mcp/` directory (or project root) with your ProjectSight API credentials:
```env
APPLICATION_NAME=your_app_name
CLIENT_ID=your_client_id
CLIENT_SECRET=your_client_secret
PORTFOLIO_ID=your_portfolio_id_or_guid
PROJECTSIGHT_API_URL=https://cloud.api.trimble.com/projectsight/us1/1.0
PROJECTSIGHT_SCOPE=ProjectSight_-_US1
```
**Getting Credentials:**
1. Sign in to [API Cloud](https://console.trimble.com) with your Trimble account
2. On the Discover API page, select **ProjectSight** or **ProjectSight-EU**
3. Select **Subscriptions** and subscribe your application
4. Select **Get Key** and copy:
- Application Name
- Consumer Key (CLIENT_ID)
- Consumer Secret (CLIENT_SECRET)
For more information, email `ProjectSightAPISupport@trimble.com` to request API access.
**Note on Scope**: The `PROJECTSIGHT_SCOPE` should match your API subscription region:
- `ProjectSight_-_US1` for US region
- `ProjectSight_-_EU1` for EU region
- `ProjectSight_-_US2` for Azure US region
### 3. Run the Server
**STDIO Mode (default for MCP clients):**
```bash
cd mcp
python main.py
```
**HTTP Streaming Mode:**
```bash
cd mcp
python main.py --http
```
**With custom host/port:**
```bash
# Windows PowerShell
$env:MCP_HOST="0.0.0.0"
$env:MCP_PORT="8000"
python main.py --http
# Linux/Mac
export MCP_HOST=0.0.0.0
export MCP_PORT=8000
python main.py --http
```
## π§ MCP Client Configuration
### STDIO Mode (Cursor, Claude Desktop, etc.)
```json
{
"mcpServers": {
"projectsight": {
"command": "python",
"args": ["C:\\Users\\cforey\\Desktop\\projectsight-mcp\\mcp\\main.py"],
"env": {
"APPLICATION_NAME": "your_app_name",
"CLIENT_ID": "your_client_id",
"CLIENT_SECRET": "your_client_secret",
"PORTFOLIO_ID": "your-portfolio-guid-or-account-id"
}
}
}
}
```
**Note**: You can also use a `.env` file instead of setting environment variables in the config. The script will automatically load variables from a `.env` file in the `mcp/` directory.
### HTTP Mode
For MCP clients that support HTTP transport, configure the connection URL:
```
http://localhost:8000/mcp
```
## π Available Tools
### Connection & Debug Tools
- **test_connection()**: Test the connection to ProjectSight API and verify authentication
- **debug_token()**: Debug token acquisition and validation (without exposing the actual token)
- **test_different_scopes()**: Test different scope combinations to find what works
### Project Operations
- **get_projects(portfolio_guid)**: Get Projects for a specific Portfolio. If `portfolio_guid` is not provided, uses `PORTFOLIO_ID` from `.env`.
### ActionItem Operations
- **get_action_item(portfolio_guid, project_id, action_item_id)**: Get an individual ActionItem
- **list_action_items(portfolio_guid, project_id)**: List ActionItems for a project. If `portfolio_guid` is not provided, uses `PORTFOLIO_ID` from `.env`.
- **create_or_update_action_item(portfolio_guid, project_id, action_item_data)**: Create or update an ActionItem
- **delete_action_item(portfolio_guid, project_id, action_item_id)**: Delete an ActionItem
### RFI Operations
- **get_rfi(portfolio_guid, project_id, rfi_id)**: Get an individual RFI
- **list_rfis(portfolio_guid, project_id, project_name)**: List RFIs for a project. Supports finding project by name if `project_id` is not provided. If `portfolio_guid` is not provided, uses `PORTFOLIO_ID` from `.env`.
- **create_or_update_rfi(...)**: Create or update an RFI with flexible parameters:
- Required: `subject`, `question` (or `details`), and either `project_id` or `project_name`
- Optional: `date_due`, `importance`, `details`, `author_contact_id`, etc.
- Automatically handles date normalization, project name lookup, workflow state detection, and RFI number generation
- **delete_rfi(portfolio_guid, project_id, rfi_id)**: Delete an RFI
### Submittal Operations
- **get_submittal(portfolio_guid, project_id, submittal_id)**: Get an individual Submittal
- **list_submittals(portfolio_guid, project_id)**: List Submittals for a project. If `portfolio_guid` is not provided, uses `PORTFOLIO_ID` from `.env`.
### Workflow Operations
- **get_workflow_states(record_type, portfolio_guid, project_id)**: Get workflow states for a record type (e.g., "RFI", "ActionItem", "Submittal"). If `portfolio_guid` is not provided, uses `PORTFOLIO_ID` from `.env`.
## ποΈ Architecture & Best Practices
This MCP server has been refactored into a **scalable, maintainable structure** that demonstrates best practices:
- **Modular Design**: Each component in its own file with clear responsibilities
- **Separation of Concerns**: Configuration, authentication, API client, and tools are separated
- **Organized Tools**: Tools grouped by domain for easy discovery and extension
- **Comprehensive Documentation**: Architecture guide and extension examples included
### π Learning Resource
This structure is designed to be a **teaching example** - use it as a reference when building your own MCP servers! The code demonstrates:
- β
Single Responsibility Principle
- β
Separation of Concerns
- β
Dependency Injection
- β
DRY (Don't Repeat Yourself)
- β
Clear Naming Conventions
- β
Comprehensive Documentation
- β
Type Hints
- β
Error Handling Patterns
## π Authentication
The server uses OAuth2 client credentials flow with Trimble Identity:
- Tokens are automatically cached and refreshed
- Token cache is stored at `~/.cache/projectsight/token_cache.json`
- Tokens are refreshed automatically when expired
## π Important Notes
- **Portfolio ID**: The `PORTFOLIO_ID` environment variable can be either:
- An integer AccountID (e.g., `1`, `2`, `3`)
- A Portfolio GUID in UUID format (e.g., `b1123573-2dac-4499-81f4-7ec44b89152c`)
- **Project Lookup**: Many tools support finding projects by name (case-insensitive, partial match) if you don't know the project ID.
- **RFI Creation**: The `create_or_update_rfi` tool is highly flexible and will:
- Auto-detect workflow states from existing RFIs
- Auto-generate RFI numbers
- Normalize dates from various formats ("today", "tomorrow", "2026-01-26", etc.)
- Map importance text ("high", "normal", "low") to importance IDs
- Use existing RFI settings as defaults when appropriate
## π Exposing via Public URL (Tunneling)
To make your local server accessible via a public URL, use a tunneling service:
### Option 1: ngrok (Recommended)
1. **Install ngrok on Windows**:
```powershell
winget install ngrok.ngrok
```
2. **Start your MCP server**:
```bash
cd mcp
python main.py --http
```
3. **Create a tunnel** (in a separate terminal):
```bash
ngrok http 8000
```
4. **Use the public URL**: ngrok will provide a public URL like `https://abc123.ngrok-free.dev`.
The MCP endpoint is at `/mcp`:
```
https://abc123.ngrok-free.dev/mcp
```
### Option 2: Cloudflare Tunnel (cloudflared)
1. **Install cloudflared**: Download from [developers.cloudflare.com](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/)
2. **Start your MCP server**:
```bash
cd mcp
python main.py --http
```
3. **Create a tunnel** (in a separate terminal):
```bash
cloudflared tunnel --url http://localhost:8000
```
4. **Use the public URL**: Cloudflare will provide a public URL like `https://random-subdomain.trycloudflare.com`. Your MCP endpoint will be:
```
https://random-subdomain.trycloudflare.com/mcp
```
## π Security Note
When exposing your server publicly, consider:
- Adding authentication/API keys if your MCP server handles sensitive data
- Using HTTPS (all tunneling services above provide HTTPS)
- Limiting access to specific IPs if possible
- Monitoring usage and rate limiting
## π Rate Limits
Based on updated guidance from Trimble Cloud, you don't need to pass the x-API-key to the trimblepaas.com endpoints. If you do pass it, there is a limit of 50 requests per second.
## π API Documentation
For detailed API documentation, refer to:
- [ProjectSight API Documentation](https://raw.githubusercontent.com/trimble-oss/projectsight-api-samples/main/ProjectSight-v1.json)
## π Migration from Old Structure
If you were using the old `projectsight.py` file directly:
1. **Update command**: Use `python main.py` instead of `python projectsight.py`
2. **Configuration**: Same `.env` file format (place in `mcp/` directory)
3. **MCP Client Config**: Update path to point to `mcp/main.py`
4. **Tools**: All tools work the same, just organized better
The old `projectsight.py` file is still available for reference but the new modular structure is recommended.
## π οΈ Extending the Server
See `mcp/EXAMPLE_EXTENSION.md` for detailed examples. Quick template:
```python
# mcp/tools/my_new_tool.py
from fastmcp import FastMCP
from client import ProjectSightClient
_client: ProjectSightClient = None
def register(mcp: FastMCP):
@mcp.tool()
async def my_tool(param: str) -> Dict:
"""Tool description."""
result = await _client.get(f"/endpoint/{param}")
return {"result": result}
def set_client(client: ProjectSightClient):
global _client
_client = client
```
Then register it in `mcp/tools/__init__.py`.
## π License
This project is provided as-is for use with the ProjectSight API.
---
**Built with best practices in mind** - Use this structure as a reference for building your own MCP servers! π