Skip to main content
Glama

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

pip install fastmcp python-dotenv aiohttp uvicorn

Or using a virtual environment:

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:

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 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):

cd mcp python main.py

HTTP Streaming Mode:

cd mcp python main.py --http

With custom host/port:

# 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.)

{ "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:

  1. Install ngrok on Windows:

    winget install ngrok.ngrok
  2. Start your MCP server:

    cd mcp python main.py --http
  3. Create a tunnel (in a separate terminal):

    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

  2. Start your MCP server:

    cd mcp python main.py --http
  3. Create a tunnel (in a separate terminal):

    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:

šŸ”„ 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:

# 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! šŸš€

-
security - not tested
F
license - not found
-
quality - not tested

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Charley-Forey-AI/ProjectSight-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server