Provides access to the ProjectSight API for managing portfolios, projects, and construction records like ActionItems, RFIs, and Submittals, including support for workflow states and project lookups.
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., "@ProjectSight MCP Serverlist the open action items for project 101"
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.
ProjectSight MCP Server
A scalable, maintainable MCP (Model Context Protocol) server that provides access to the ProjectSight API via 425 tools across 48 domainsβincluding portfolios, projects, action items, RFIs, submittals, contracts, budgets, applications for payment, change orders, daily reports, drawings, files, meetings, 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
425 tools across 48 domains: Full ProjectSight API coverageβActionItems, RFIs, Submittals, Projects, Portfolios, Contracts, Budgets, Applications for Payment, Change Orders (potential/prime/sub), Daily Reports, Field Work Directives, Drawings, Drawing Sets, Files/Folders, Meetings, Notices, Safety Notices, Punch Lists, Issues, Checklists, Forecasts, Job Costs, Purchase Orders, Invoicing, ERP read-only, Users, Roles, Companies, Contacts, and more.
Intelligent gateway: By default the server exposes a single
projectsighttool with intent matching and 35+ named capabilities (multi-tool workflows) defined inmcp/yaml/capabilities.yaml. SetMCP_GATEWAY_ONLY=0to expose all 425 tools.Server policy: Deletes never run by default; optional approval for create/update via
mcp/yaml/policy.yamland env overrides (MCP_DELETE_POLICY,MCP_REQUIRE_APPROVAL_FOR_MUTATIONS).OAuth2 Authentication: Automatic token management with caching and refresh; client credentials (STDIO) or On-Behalf token exchange (e.g. Agent Studio).
Modular Architecture: Clean separation of concerns, registry-driven gateway, easy to extend.
Debug Tools: Test connection, debug tokens, get context requirements, test different scopes.
π Project Structure
Tool modules register with the MCP instance and with HANDLER_REGISTRY so the gateway can execute them by name.
π Quick Start
1. Install Dependencies
Or using a virtual environment:
(Pyyaml is used by the registry and policy modules.)
2. Configure Credentials
Create a .env file in the mcp/ directory (or project root) with your ProjectSight API credentials:
Note on PORTFOLIO_ID: PORTFOLIO_ID is optional. If unset, the server calls the ProjectSight API (GET /accounts and GET /accounts/{accountId}/portfolios) at startup and uses the first (or only) portfolio found. You can also discover accounts and portfolios with get_accounts and get_portfolios_for_account, then set PORTFOLIO_ID in .env to a Portfolio GUID (UUID) or pass portfolio_guid on tool calls. The API base URL is unchanged (PROJECTSIGHT_API_URL); all requests, including account/portfolio discovery, use it.
Getting Credentials:
Sign in to API Cloud with your Trimble account
On the Discover API page, select ProjectSight or ProjectSight-EU
Select Subscriptions and subscribe your application
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_-_US1for US regionProjectSight_-_EU1for EU regionProjectSight_-_US2for Azure US region
3. Run the Server
Run from the mcp/ directory so that .env and imports resolve correctly (or set PYTHONPATH and ensure .env is loaded from the right place).
STDIO Mode (default for MCP clients):
HTTP Streaming Mode:
With custom host/port:
Gateway-only mode (default):
The server defaults to exposing only the intelligent projectsight gateway tool so the agent is not overwhelmed by hundreds of tools. No configuration needed. To expose all tools instead (e.g. for debugging), set MCP_GATEWAY_ONLY=0 before starting. See "Intelligent Gateway Tool" below.
Port already in use (10048): If you see "port already in use" or error 10048 when running python main.py --http, use another port: set MCP_PORT=8001 (PowerShell: $env:MCP_PORT="8001"), then start the server again and use http://localhost:8001/mcp in your client. To free port 8000 on Windows: run netstat -ano | findstr :8000, note the PID (last column), then taskkill /PID <pid> /F.
π§ MCP Client Configuration
STDIO Mode (Cursor, Claude Desktop, etc.)
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. PORTFOLIO_ID can be omitted; the server will discover a default portfolio at startup. MCP_GATEWAY_ONLY=1 is the default (single-tool mode); including it in env makes the behavior explicit for Cursor and other clients.
HTTP Mode
For MCP clients that support HTTP transport, configure the connection URL:
On behalf of actor token (Agent Studio)
When the MCP is used from Trimble Agent Studio (or any client that sends the signed-in user's Trimble ID token), you can configure the MCP with "On behalf of actor token" instead of static credentials. The client sends the user's TID token as Authorization: Bearer <token> on each request. The MCP then exchanges that token for a ProjectSight-scoped token via Trimble Identity's On Behalf / Token Exchange grant and uses it for API calls, so requests run as that user.
Token format: The Bearer token must be a JWT (e.g. Trimble ID token). The MCP detects JWT shape and sends the correct subject_token_type for the exchange. If you see an error like subject_token type 'urn:ietf:params:oauth:token-type:access_token' not supported, ensure the client sends a JWT in Authorization: Bearer, not an opaque access token.
Setup in Agent Studio:
Authentication: Select On behalf of actor token.
URL: Your MCP endpoint (e.g.
https://your-mcp-host/mcp). The MCP must be reachable over HTTPS when used from Studio.Scopes (informational): Required scopes for the user's token typically include:
openidProjectSight scope for your region:
ProjectSight_-_US1(US),ProjectSight_-_EU1(EU), orProjectSight_-_US2(Azure US).
Confirm exact scope names with Trimble Identity and API Endpoints for your environment (stage vs prod).
Server-side: The MCP still needs CLIENT_ID and CLIENT_SECRET in .env (or environment) to perform the On Behalf token exchange. Set APPLICATION_NAME, PROJECTSIGHT_SCOPE, PORTFOLIO_ID, and PROJECTSIGHT_API_URL as defaults; they can be overridden per request via headers (see below).
Optional per-request headers (when a gateway or client sends them):
X-Portfolio-Idβ Portfolio GUID for the request (overridesPORTFOLIO_IDfor that request).X-API-Base-URLβ ProjectSight API base URL (e.g. for a different region).X-ProjectSight-Scopeβ Scope used for token exchange (e.g.ProjectSight_-_US1).X-Application-Nameβ Application name for token/scope.
If these headers are not sent, the MCP uses .env defaults and tool parameters (e.g. portfolio_guid) as today.
Fallback: When no Authorization: Bearer token is present (e.g. STDIO or "None" auth in Studio), the MCP uses client credentials from .env as before.
Troubleshooting On Behalf exchange:
Error | Cause | What to do |
| The Bearer token was not issued by (or cannot be verified by) the same Trimble Identity environment the MCP is using. | Ensure TRIMBLE_TOKEN_URL in |
| The JWT was issued for a different application; Trimble Identity will not exchange it for a token for this MCP's CLIENT_ID. | Ensure the MCP's CLIENT_ID (in |
| IdP expects a JWT. | The MCP now sends JWT type when the token looks like a JWT. Ensure the client sends a JWT in |
Automatic fallback when On Behalf fails: If On Behalf token exchange fails (e.g. signature verification or intended audience), the MCP automatically falls back to client credentials so API requests can still be made. Set PORTFOLIO_ID in .env if discovery does not find a portfolio or you need a specific portfolio. Requests then run with the application identity (not the signed-in user).
Workaround when On Behalf is not configured: Omit the Bearer token (use client credentials only), set PORTFOLIO_ID in .env, and the MCP will use the cached client_credentials token for discovery and all tools.
π§ Intelligent Gateway Tool (Single-Tool Mode)
By default (or when MCP_GATEWAY_ONLY=1), the server exposes a single tool projectsight so the agent is not overwhelmed by hundreds of tools. Set MCP_GATEWAY_ONLY=0 to expose all tools.
Named capabilities: The gateway can run named capabilities (e.g. project_overview, contract_and_budget_summary, quality_and_issues) that execute a fixed sequence of tools with shared context. The list is in mcp/yaml/capabilities.yaml.
Parameters:
user_request (required): Natural language request (e.g. "List submittals for Downtown project", "Get projects", "Test connection").
context (optional): Dict with known context from prior turns:
portfolio_guid,project_id,project_name,contract_id, etc.prefer_discovery (optional): If
true, returns only a plan (which tools would run) without executing.
Responses:
action: "need_more_info" β Ask the user
questions_for_user, then call again withcontextupdated (e.g. user says "Downtown" β sendcontext: { "project_name": "Downtown" }).action: "plan" β When
prefer_discovery=true, returns the steps that would run (no execution).action: "result" β The result from the executed tool(s).
action: "policy_blocked" β A delete was requested; this server does not run delete commands (delete actions require triple check).
action: "approval_required" β A create/update was requested and the server is configured to require approval; call again with the same request and
contextplusapproved: trueorconfirm_mutation: trueto execute.action: "error" β Error and suggestion.
Server policy (no deletes; optional approval for create/update):
Deletes: The server does not execute delete commands. Delete requests return
action: "policy_blocked"with a message that delete actions require triple check; use the ProjectSight UI or API for deletions.Create/Update: You can require approval before running create/update tools by setting
MCP_REQUIRE_APPROVAL_FOR_MUTATIONS=1. Then the first call returnsaction: "approval_required"with a plan; the agent or user can confirm by calling again with the same context plusapproved: trueorconfirm_mutation: true. Policy is configured inmcp/yaml/policy.yamland overridden byMCP_DELETE_POLICYandMCP_REQUIRE_APPROVAL_FOR_MUTATIONSin.env.
Recommended workflow for the agent:
User sends a message.
Agent calls
projectsight(user_request=user_message, context={}).If response is need_more_info β agent asks the user the returned questions, then calls
projectsight(user_request=..., context={ ... user answers ... }).If response is plan (with
prefer_discovery=true) β agent can confirm with user, then call again withoutprefer_discoveryto execute.If response is result β agent presents the result to the user.
If response is error β agent shows the error and suggestion.
Context continuity (multi-turn conversations): The gateway is stateless: each call receives only the user_request and context for that call. Successful responses include resolved_context (e.g. project_id, project_name, portfolio_guid, and when relevant record IDs such as rfi_id). Clients should retain this so that follow-up requests (e.g. "change the due date to Feb 19" after listing RFIs) work without the user re-specifying project or RFI. When the user refers to a specific item from a prior result (e.g. "that one", "RFI 005"), the client should add the corresponding ID (e.g. rfi_id: 5) to the context it sends.
No multi-tool sequencing is required by the agent; the gateway resolves project/portfolio context and runs the right internal tools server-side. Tool metadata is in mcp/yaml/tool_registry.yaml; handlers are registered at startup in HANDLER_REGISTRY for execution.
π Available Tools
The server provides 425 tools across 48 domains. Full tool list and metadata: mcp/yaml/tool_registry.yaml. Named multi-tool capabilities (e.g. project_overview, contract_and_budget_summary, quality_and_issues): mcp/yaml/capabilities.yaml. When using the gateway, call get_mcp_context_requirements() for required context and tools grouped by domain.
Domains (48): action_items, application_for_payment, budget, budget_code_structure, budget_group, budget_snapshot, change_order_request, checklist, company, contact, contract, contract_invoice, daily_report, debug, drawing, drawing_set, erp_read_only, field_work_directive, file, folder, forecast, general_invoice, issue, job_costs, lookup_list, meeting, notice, photo, po_catalog, portfolio, potential_co, prime_contract_co, projects, punch_list, purchase_order, records, report_generator, rfis, role, safety_notice, sub_contract_co, submittal_package, submittals, transmittal, user, workflow, workflow_states.
Common tools (when not using gateway-only mode): get_projects, list_action_items, list_rfis, list_submittals, test_connection, get_mcp_context_requirements. Use the corresponding list/get tools to discover IDs (e.g. list_contracts, list_budgets) when a tool asks for project_id, contract_id, etc. Portfolio-level tools use portfolio_guid from the parameter or PORTFOLIO_ID from .env (must be a Portfolio GUID/UUID).
ποΈ Architecture & Best Practices
This MCP server uses a scalable, maintainable structure with a registry-driven gateway.
Architecture and data flow
Startup:
main.pyloads Config, creates Auth and ProjectSightClient, and callsregister_tools(mcp, client). Intools/__init__.py, ifMCP_GATEWAY_ONLY=1(default), internal tool modules register with a no-op MCP so they don't appear as tools to the client, but they still register handlers inHANDLER_REGISTRY. The gateway always registers the singleprojectsighttool on the real MCP.Gateway flow: The user calls
projectsight(user_request, context?, prefer_discovery?)β keyword-based intent matching againstmcp/yaml/tool_registry.yamlandmcp/yaml/capabilities.yamlβ resolveportfolio_guid/project_id(and project name) viautils.resolve_projectand Config/request context β policy check (delete blocked; mutation approval if required) β execute handler(s) fromHANDLER_REGISTRYβ returnneed_more_info|plan|result|policy_blocked|approval_required|error.Request context (HTTP):
request_context.pyholds per-request actor token and overrides (X-Portfolio-Id,X-API-Base-URL,X-ProjectSight-Scope,X-Application-Name). Auth and Config use these when present (e.g. Agent Studio).Policy:
policy.pyreadsmcp/yaml/policy.yaml; env varsMCP_DELETE_POLICYandMCP_REQUIRE_APPROVAL_FOR_MUTATIONSoverride. Deletes are never run by default; create/update can requirecontext.approvedorcontext.confirm_mutation.
Modular Design: Each component in its own file with clear responsibilities.
Separation of Concerns: Configuration, authentication, API client, registry, policy, and tools are separated.
Organized Tools: Tools grouped by domain; gateway uses registry and capabilities for intent matching.
π Documentation
TOOL_STANDARD.md: Docstring and registry standards for tools; body/Dict expectations.
TOOL_REGISTRY_MAINTENANCE.md: How to maintain or regenerate
mcp/yaml/tool_registry.yaml(e.g. runpython scripts/build_registry.pyfrommcp/).
Testing
Gateway intent-matching tests live in mcp/tests/. Run them from the mcp directory:
Tests cover registry loading, intent matching for common phrases (e.g. "list submittals", "get projects"), and capability-hint summary.
π 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.jsonTokens are refreshed automatically when expired
π Important Notes
Portfolio ID: Optional in .env. When unset, the server discovers a default portfolio at startup via the accounts/portfolios API. Use get_accounts and get_portfolios_for_account to list accounts and portfolios, then set
PORTFOLIO_IDto a Portfolio GUID (UUID) or passportfolio_guidto tools. IfPORTFOLIO_IDis an integer (Account ID), you must passportfolio_guidper request or setPORTFOLIO_IDto a UUID.Server behavior (env):
MCP_GATEWAY_ONLY(default1= onlyprojectsighttool;0= expose all 425 tools).MCP_DELETE_POLICY(defaultnever_run; deletes are never executed).MCP_REQUIRE_APPROVAL_FOR_MUTATIONS(set to1to requirecontext.approvedorcontext.confirm_mutationbefore create/update tools run). Optional:MCP_REQUEST_TIMEOUT_SECONDS,MCP_CLIENT_RETRY_COUNT.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_rfitool 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)
Install ngrok on Windows:
winget install ngrok.ngrokStart your MCP server:
cd mcp python main.py --httpCreate a tunnel (in a separate terminal):
ngrok http 8000Use 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)
Install cloudflared: Download from developers.cloudflare.com
Start your MCP server:
cd mcp python main.py --httpCreate a tunnel (in a separate terminal):
cloudflared tunnel --url http://localhost:8000Use 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:
Update command: Use
python main.pyinstead ofpython projectsight.pyConfiguration: Same
.envfile format (place inmcp/directory)MCP Client Config: Update path to point to
mcp/main.pyTools: 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
New tools live under
mcp/tools/<domain>.py. Each tool module definesregister(mcp, handler_registry)and registers both the FastMCP tool and an async handler:handler_registry[name] = my_async_handler. The gateway executes tools by name viaget_handler(name)from the registry.Gateway intent matching: Add (or regenerate) the tool's entry in
mcp/yaml/tool_registry.yaml(name, description, domain, required_context, optional_context, keywords; optionally examples, follow_ups). Runpython scripts/build_registry.pyfrom themcp/directory to regenerate the registry from docstrings; see TOOL_REGISTRY_MAINTENANCE.md.Wiring: Add the new module to the imports and
register(...)calls inmcp/tools/__init__.py. Optional: if the tool is part of a multi-step workflow, add or extend an entry inmcp/yaml/capabilities.yaml.
Example pattern:
π 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! π