Skip to main content
Glama

ms-project-mcp

Drive Microsoft Project desktop from any MCP-capable agent.

Create the plan, wire the dependencies, level the resources, save baselines, audit the critical path, export the report — all against the real .mpp open in Project.

Python Platform MCP License Tools


Why this exists

Microsoft Project desktop has no public HTTP API. The only way to automate it is COM — a single-threaded, apartment-bound automation surface that is easy to get subtly wrong. This server turns that surface into 51 typed MCP tools with a stable id scheme, field-name-based reads and writes, dry runs, and real error messages, so an agent can do project management work instead of fighting COM.

It talks to the application you have open, not a cloud copy. If you can click it, the agent can usually do it — and you can watch it happen.


Related MCP server: project-mcp

What it can do

Area

Capabilities

Projects

Attach to the running window, list/switch open files, create, open .mpp/.xml/.mpt, save, save-as, close, quit, show/hide

Tasks

Bulk create with WBS nesting, update, delete, indent/outdent, reorder, read/write almost every field, custom fields, paging and filters

Dependencies

FS / SS / FF / SF links with lag or lead, append or replace, parsed successors

Resources

Work, material, and cost resources; rates, max units, calendars, groups; assign with allocation units, reassign, unassign

Calendars

Base calendars, holidays and exception ranges, per-day working shifts, project calendar

Baselines & value

Save/clear baselines 0–10, per-task variance, earned value (BCWS/BCWP/ACWP/SPI/CPI)

Analysis

Critical path, schedule audit, what-if delay with automatic revert, timephased data

Export

CSV, PDF, full JSON snapshot, open existing Project XML

Escape hatches

Run an existing VBA macro, or inject arbitrary VBA (opt-in) with results and errors returned


Architecture

┌──────────────────────────┐
│  Agent (Command Code,    │
│  Claude, Cursor, …)      │
└────────────┬─────────────┘
             │  stdio · JSON-RPC (MCP)
┌────────────▼─────────────┐
│  MCP tools               │  thin, typed, annotated
├──────────────────────────┤
│  MSProjectClient         │  domain logic, field registry
├──────────────────────────┤
│  ComBridge               │  ONE STA thread · serialized queue
│                          │  busy-retry · timeouts
├──────────────────────────┤
│  WINPROJ.EXE             │  attaches, or launches if needed
└──────────────────────────┘

Office COM objects are apartment-threaded. Calling them from several threads produces intermittent RPC_E_WRONG_THREAD and "Call was rejected by callee" errors, so every COM call is marshalled onto one dedicated thread and executed serially. If you click around in Project while an agent works, rejected calls are retried instead of failing.


Requirements

  • Windows with desktop Microsoft Project installed (2016 / 2019 / 2021 / Microsoft 365). Verified end to end against Project 16.0 (Microsoft 365).

  • Python 3.10+ (developed on 3.13).

  • No project file needs to be open at startup.

Project Online / for the Web is a different product with a different API (Graph/CSOM). This server targets the desktop application only.


Install

git clone https://github.com/iamsammy-ak/ms-project-mcp.git
cd ms-project-mcp
python -m venv .venv
.\.venv\Scripts\python -m pip install -e .

Check that it can see Project:

.\.venv\Scripts\python -c "from msproject_mcp.core import MSProjectClient; print(MSProjectClient().status())"

Prefer a global command? pipx install . gives you msproject-mcp on PATH. To ship it to another machine, build a wheel and copy that:

python -m pip wheel --no-deps -w dist .
# elsewhere: pip install msproject_mcp-0.1.0-py3-none-any.whl

Connect your agent

Use the absolute path to your interpreter (or the msproject-mcp shim).

Command Code

cmdc mcp add --scope user --env MSPROJECT_MCP_ALLOW_VBA=1 msproject -- "C:\path\to\ms-project-mcp\.venv\Scripts\python.exe" -m msproject_mcp

Claude Desktop / Claude Code / Cursor / VS Code

{
  "mcpServers": {
    "msproject": {
      "command": "C:\\path\\to\\ms-project-mcp\\.venv\\Scripts\\python.exe",
      "args": ["-m", "msproject_mcp"],
      "env": { "MSPROJECT_MCP_ALLOW_VBA": "1" }
    }
  }
}

Remote agents can use the HTTP transport instead:

python -m msproject_mcp --transport streamable-http --port 8765

Keep it bound to 127.0.0.1 unless you understand the exposure — it drives your desktop applications.


Try it

With Project open (or not — the agent can start it):

"What's the critical path in the open project?"

"Create a launch plan: Discovery 5d, Design 8d, Build 15d, Test 6d,
 UAT 4d, Launch 1d — link them finish-to-start, indent Build..Test
 under a Delivery phase."

"Alice is 50% available and Bob is out for two weeks. Fix the
 resource overload and tell me what slipped."

"Save baseline 0, then show every task that is more than 3 days late."

"Export the schedule to C:\Temp\launch.csv and a PDF next to it."

"Add our Eid holidays to the Standard calendar for 2026."

Tool reference

Tool

What it does

msproject_status

Is Project running, which files are open, which is active, is VBA enabled

list_open_projects

Every open project with task/resource counts

use_project

Switch active project by index, name, fragment, or path

create_project

New blank project with title and start date

open_project

Open .mpp, .xml, or .mpt

save_project

Save in place

save_project_as

Save as .mpp or .mpt

close_project

Close a project, optionally saving

quit_msproject

Quit the application

set_project_window_visible

Show or hide the Project window

Tool

What it does

get_project

Title, dates, calendar, minutes/day and any requested fields

update_project

Update title, manager, status date, calendar, scheduling options, currency…

get_field_catalog

Every valid field name for tasks, resources, or the project

Tool

What it does

get_tasks

List with name/level/summary/milestone filters and paging

get_task

Read one task with any field set

create_tasks

Append tasks in order; indent builds the WBS; auto-scheduled by default

update_tasks

Bulk field edits with dry_run preview and before/after values

delete_tasks

Delete by id, with preview

indent_task

Promote/demote in the WBS

move_task

Reorder a row (uses Project's cut/paste)

Tool

What it does

set_task_links

FS/SS/FF/SF links with lag/lead; replace or append

get_task_links

Parsed predecessors and successors

Tool

What it does

get_resources

Resource pool with filters and paging

create_resources

Add work/material/cost resources

update_resources

Change rates, units, calendar, grouping, custom fields

delete_resources

Remove resources and their assignments

assign_resources

Assign with units; creates missing resources on request

unassign_resources

Remove assignments

get_assignments

Work, actual/remaining work, cost, dates, % complete

Tool

What it does

get_calendars

Base calendars and the project calendar

create_calendar

New calendar copied from an existing one

add_calendar_exception

Holiday or exception date range

list_calendar_exceptions

Exceptions with their index

delete_calendar_exception

Remove an exception

set_working_hours

Per-day shifts, or mark a day non-working

Tool

What it does

calculate_project

Recalculate and return the new finish date

level_resources

Run Project's automatic leveling

save_baseline / clear_baseline

Baseline 0–10

get_baseline_variance

Finish slip, duration/work/cost variance per task

get_earned_value

BCWS/BCWP/ACWP/SV/CV/SPI/CPI and totals

get_critical_path

Critical tasks in chronological order with slack

check_schedule

Overdue, negative slack, hard constraints, missing resources/links

simulate_delay

What-if delay, then revert automatically

get_timephased_data

Per-day/week/month work, cost, actual, remaining, baseline

Tool

What it does

export_csv

UTF-8 CSV of the task table

export_pdf

PDF through Project's own exporter

snapshot_project

JSON snapshot of tasks, resources, and metadata

import_project_xml

Open a Project XML file

run_macro

Run an existing VBA macro

run_vba

Inject and run VBA (opt-in)


Fields instead of a tool per property

Rather than hundreds of narrow tools, task/resource/project tools accept a fields list keyed by stable names. get_field_catalog returns the whole set.

get_tasks(fields=["name", "duration", "start", "finish", "text1", "number1"])
update_tasks([{"unique_id": 12, "set": {"duration": "8d", "percent_complete": 40}}])

Conventions:

  • Ids accept UniqueID (stable across edits) or the visible row ID. UniqueID is matched first; every row returns both.

  • Durations accept "5d", "12h", "480m", or raw minutes. Reads return both duration: "5d" and duration_minutes: 2400.

  • Percentages are 0–100; a non-integer 0.0–1.0 value is a fraction.

  • New tasks are auto-scheduled (manual: false) so links and durations recalculate. Pass "manual": true to pin a task's dates.

  • Every bulk mutation supports dry_run.

Custom fields are first-class: text1–text30, number1–number20, flag1–flag20, date1–date10, cost1–cost10, duration1–duration10.


VBA escape hatch

run_vba closes the last gap: dialog-only operations and expressions no COM property exposes. It is off by default.

  1. Start the server with MSPROJECT_MCP_ALLOW_VBA=1.

  2. In Project: File → Options → Trust Center → Trust Center Settings → Macro Settings → Trust access to the VBA project object model.

The code is injected into a temporary module, executed, and the module is removed. Assign your result to MspMcpEval:

Dim t As Task
Set t = ActiveProject.Tasks(3)
MspMcpEval = t.Name & " / " & t.Finish

Runtime errors come back as readable tool errors (VBA error: 11: Division by zero).


Configuration

Variable

Default

Purpose

MSPROJECT_MCP_VISIBLE

1

Show the Project window while working

MSPROJECT_MCP_ALLOW_VBA

0

Enable run_vba

MSPROJECT_MCP_TIMEOUT

180

Seconds before a single COM call is abandoned

MSPROJECT_MCP_PROJECT

—

Project to use when several are open

MSPROJECT_MCP_MAX_ROWS

1000

Row cap for list tools

MSPROJECT_MCP_TRANSPORT

stdio

stdio, streamable-http, or sse

MSPROJECT_MCP_LOG_LEVEL

WARNING

Logs go to stderr only


What working with Project 16 actually taught us

These are baked into the code, and they are the difference between a demo and a tool that works:

Behaviour

Consequence

Tasks.Add creates manually scheduled tasks

Links and durations would never propagate; new tasks default to auto-scheduled

Task.Indent() does not exist

WBS moves use OutlineLevel, falling back to OutlineIndent

Project.Assignments is not exposed

Assignments are created with task.Assignments.Add(TaskID, ResourceID, Units) positionally

PDF export lives on Application.DocumentExport(Filename, 0)

Project.ExportAsFixedFormat rejects every argument combination

Saving to XML through COM opens a modal dialog

XML export is intentionally not exposed; CSV/PDF/snapshot are

Rates come back localized ("₹ 85.00/hr")

Numeric fields parse currency strings and also return the raw text

Dates arrive UTC-tagged but represent local wall-clock time

Converting to local (not stripping the tz) keeps dates correct

Application.Run cannot return a Function value

VBA results are written to a temp file and read back


Limitations

  • One automation client per Project instance. A modal dialog in Project blocks calls until dismissed; they surface as a clear timeout.

  • No undo. Project's COM undo is one level deep and unreliable. Use dry_run and snapshot_project before destructive batches.

  • save_project_as rebinds the open project, exactly like Project's own Save As. Only .mpp and .mpt are supported.

  • move_task uses cut/paste, so it touches the Windows clipboard.

  • Dialog-only features (recurring-task insert, report designer) have no COM entry point; run_vba covers most of them.

  • Timephased queries over multi-year ranges are slow — keep ranges modest.

  • Windows only, and desktop Project only.


Testing

.\.venv\Scripts\python tests\smoke_live.py   # 47 steps against real Project
.\.venv\Scripts\python tests\mcp_e2e.py      # real server over stdio MCP

smoke_live.py launches Project, creates a throwaway schedule, exercises every tool group, and closes only what it created. mcp_e2e.py spawns the server over stdio and drives it through an MCP client, including the error path.

Both require Microsoft Project and are therefore not run in CI.


Contributing

Issues and PRs are welcome. Please keep the invariants:

  • All COM access goes through ComBridge — never call COM from another thread.

  • New fields go in fields.py; new behaviour goes in core.py; tools stay thin.

  • Add a live test step for anything that touches Project.


License

MIT © 2026 iamsammy-ak

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables MCP-aware agents to interact with the classic Outlook desktop client for mail, calendar, contacts, tasks, and Out-of-Office settings via the COM API, without Azure or OAuth.
    201 PyPI
    36
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server that enables LLMs to read and analyze Microsoft Project schedules, including critical path, resources, and advanced construction planning layers (AWP and LPS) for work packages and Lean planning.
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables MCP-compatible AI clients to query, analyze, and mutate Oracle Primavera P6 schedules from XER files or live P6 EPPM, with tools for critical path, DCMA compliance, earned value, resources, baselines, and exports.
    31 PyPI
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables Claude and other AI agents to build, edit, and analyze Microsoft Project desktop schedules on Windows through natural language, covering tasks, WBS, dependencies, resources, calendars, baselines, critical path, resource leveling, and earned value, with live changes in Project and single-step undo. It also exports results to PDF or CSV and reads project health and time-phased data for S-curves and histograms.
    MIT