Skip to main content
Glama
Construction Project Intelligence MCP Server

Construction Project Intelligence MCP Server

by ConSpen
README.md
# Construction Project Intelligence MCP Server

[![CI](https://github.com/ConSpen/construction-mcp-server/actions/workflows/ci.yml/badge.svg)](https://github.com/ConSpen/construction-mcp-server/actions/workflows/ci.yml)
![Python](https://img.shields.io/badge/python-3.11%2B-blue) ![MCP](https://img.shields.io/badge/MCP-server-black) ![License](https://img.shields.io/badge/license-MIT-green)

An [MCP](https://modelcontextprotocol.io) server that gives AI assistants (Claude Desktop, Claude Code, any MCP client) **read-only access to live construction project data and the project document library at the same time**. Users can ask questions like:

> *"Which issues are putting the North Campus handover at risk, and what do the latest reports say about them?"*

The assistant then pulls the issues from the project platform, scores the schedule risk, finds the inspection reports and minutes that reference those issues, and cites its sources.

> **Note:** This is a sanitised reference implementation. The platform API, the projects and the documents are **fictional** and run locally. The architecture is modelled on common integration patterns between construction project platforms and enterprise document stores.

## Why this exists

On large CAPEX projects, the truth about a project is split across systems:

- **Issues, inspections and schedules** live in a project platform.
- **Context** (minutes, inspection reports, specifications) lives in a document library.

Project controls teams spend hours stitching the two together every week. An MCP server puts that join behind a small set of well-defined tools. Any AI assistant can use them safely, and the business logic stays testable and reusable.

## Architecture

```mermaid
flowchart LR
    U[User in Claude Desktop / Claude Code] --> A[AI assistant]
    A -- MCP stdio / HTTP --> S[MCP server<br/>thin tool layer]
    S --> PI[ProjectIntelligence<br/>service layer]
    PI --> PC[PlatformClient<br/>REST, auth, pagination, retries]
    PI --> DS[DocumentStore<br/>metadata + TF-IDF search]
    PC --> API[(Project platform API<br/>mock: FastAPI)]
    DS --> DOCS[(Document library<br/>mock: Markdown + YAML;<br/>prod: Microsoft Graph)]
```

| Layer | Responsibility | File |
|---|---|---|
| MCP tools | Tool contracts, input limits, error-to-message mapping, prompt template | `src/construction_mcp/server.py` |
| Service | Joins platform and documents, filtering, risk scoring | `src/construction_mcp/service.py` |
| Platform connector | Async REST, bearer auth, pagination, retry with backoff on 429/5xx | `src/construction_mcp/platform_client.py` |
| Document connector | `DocumentStore` protocol, local implementation with metadata and ranked search | `src/construction_mcp/document_store.py` |
| Mock platform | Deterministic fictional data, auth, pagination, optional rate limiting | `mock_platform/app.py` |

## Tools

All tools are annotated `readOnlyHint: true`. The assistant can investigate, but it cannot change source data.

| Tool | What it does |
|---|---|
| `list_projects` | Projects with phase, location, planned completion |
| `search_issues` | Filter by status, priority, discipline, overdue; sorted by priority then due date |
| `get_issue` | One issue, days overdue, and the documents that reference it (falls back to search) |
| `search_documents` | Ranked full-text search over minutes, reports and specs, scoped by project |
| `read_document` | Full text and metadata of one document |
| `project_risk_brief` | Risk score, discipline hotspots, worst overdue issues with evidence, **and the scoring method** |

Prompt: `weekly_site_brief(project_id)` is a template for a one-page steering-meeting brief.

### Example: `project_risk_brief("P-200")` (real output, abridged)

```json
{
  "open_issues": 18, "overdue_issues": 16, "critical_or_high_overdue": 5,
  "risk_score": 58, "risk_level": "elevated",
  "hotspots": [
    {"discipline": "HVAC", "open_issues": 6, "overdue_issues": 6, "risk_points": 68},
    {"discipline": "Electrical", "open_issues": 5, "overdue_issues": 5, "risk_points": 49}
  ],
  "top_overdue": [
    {"issue": {"id": "ISS-2006", "title": "AHU commissioning sheet incomplete at Level 2 lab wing", "priority": "critical"},
     "days_overdue": 69, "linked_documents": [{"id": "DOC-201", "title": "Commissioning status report, October"}]}
  ],
  "method": "Each open issue scores its priority weight (low 1, medium 2, high 4, critical 8). Overdue issues are doubled ..."
}
```

## Design decisions

- **Thin MCP layer, fat service layer.** Business logic is plain Python, tested without MCP. The same service can drive a scheduled report or a dashboard.
- **Explainable scoring.** The risk score is a simple, documented formula returned with every brief, so a project manager can challenge it. Priority dominates and lateness only amplifies it, so a stale low-priority snag never outranks a fresh critical one. This is covered by a unit test.
- **Read-only by default.** Write actions on site data (closing issues, changing dates) should go through a human approval step, not an LLM tool call.
- **Errors are answers.** Platform failures return a clear message to the assistant (e.g. "check CPI_PLATFORM_TOKEN") instead of crashing the session.
- **Swappable connectors.** `DocumentStore` is a protocol. The local implementation can be replaced with Microsoft Graph (SharePoint) or a vector index without touching the tools.
- **Bounded outputs.** Tool limits are capped, so a single call cannot flood the model context.

## Quickstart

```bash
git clone https://github.com/ConSpen/construction-mcp-server
cd construction-mcp-server
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

# 1. start the mock project platform
uvicorn mock_platform.app:app --port 8765

# 2. run the tests (no server needed, uses an in-process transport)
pytest

# 3. try the server interactively
CPI_AS_OF=2026-11-02 mcp dev src/construction_mcp/server.py
```

### Use it from Claude Desktop

Add this to `claude_desktop_config.json`, using absolute paths:

```json
{
  "mcpServers": {
    "construction-intelligence": {
      "command": "/path/to/construction-mcp-server/.venv/bin/construction-mcp",
      "env": {
        "CPI_PLATFORM_URL": "http://127.0.0.1:8765",
        "CPI_PLATFORM_TOKEN": "demo-token",
        "CPI_DOCUMENTS_PATH": "/path/to/construction-mcp-server/sample_docs",
        "CPI_AS_OF": "2026-11-02"
      }
    }
  }
}
```

Then ask: *"Give me a weekly site brief for P-100."*

## Configuration

| Variable | Default | Purpose |
|---|---|---|
| `CPI_PLATFORM_URL` | `http://127.0.0.1:8765` | Platform API base URL |
| `CPI_PLATFORM_TOKEN` | `demo-token` | Bearer token |
| `CPI_DOCUMENTS_PATH` | `sample_docs` | Document library root |
| `CPI_AS_OF` | today | Pin "today" for demos and reproducible results |
| `CPI_MAX_RETRIES` | `3` | Retries on 429 / 5xx |
| `CPI_MOCK_FLAKY_EVERY` | off | Mock only: return 429 on every Nth call to exercise retries |

## Testing

There are 18 tests across three levels:

- **Connector:** pagination, filters, auth errors, retry and give-up behaviour
- **Service:** search ranking, project scoping, issue-document linking, risk model invariants
- **MCP:** tool registration, read-only annotations, tool round trips, error handling

CI runs ruff and pytest on every push.

## Taking this to production

- Replace `LocalDocumentStore` with a Microsoft Graph connector (delegated auth, so users only see documents they already have access to).
- Add caching with short TTLs for list endpoints, plus structured logging of tool calls for audit.
- Run over streamable HTTP behind the organisation's identity provider (OAuth), not with a shared token.
- Add an evaluation set of real questions with expected tool calls, and track answer quality over time.

## License

MIT. Built with Claude Code.

TDQS

A3.5/5.0

Scored across 6 tools

Disambiguation4/5

Most tools have clearly distinct resource/action boundaries: document search vs. document read, issue search vs. issue retrieval, and project listing vs. risk briefing. The only mild overlap is that project_risk_brief also surfaces overdue issues, which search_issues can also find, but the aggregate scoring and hotspot focus keep it distinct.

Naming Consistency4/5

Five of six tools follow a clear verb_noun pattern: search_documents, read_document, list_projects, search_issues, get_issue. project_risk_brief is a noun phrase that breaks the pattern, but the overall convention remains predictable.

Tool Count5/5

Six tools is well-scoped for a construction project intelligence server focused on projects, documents, issues, and risk. Each tool covers a distinct operation without obvious padding.

Completeness4/5

The surface covers the core read-oriented intelligence workflows: project listing, document search/read, issue search/retrieval, and a project risk brief. Minor gaps remain, such as no dedicated get_project detail operation or issue creation/update, though these may be outside the server's read-only scope.

Maintenance

ActivityMaintained
ResponsivenessNo recent community issues