Skip to main content
Glama
Planview Portfolios Actions MCP Server

Planview Portfolios Actions MCP Server

by norman2112

Planview Portfolios MCP Server (v2)

An MCP server that connects Planview Portfolios (enterprise strategic portfolio management) to Claude Desktop or any MCP-compatible AI client. Five action-based tools covering projects, tasks, financial plans, and work hierarchy — bridging both REST and legacy SOAP APIs under a single MCP interface.

Why This Exists

Planview Portfolios is an enterprise SPM platform used by large organizations to manage project portfolios, resource capacity, and strategic funding. Its API surface is split across REST (projects, work items) and SOAP (tasks, financial plans) with different auth models and data formats.

This server unifies both API layers behind MCP so Claude can create projects, build financial plans, manage tasks, and navigate work hierarchies through conversation.

This is the second MCP server I built for Planview products (the first was AgilePlace MCP Server). Same pattern, completely different APIs — AgilePlace is a modern REST API, Portfolios mixes REST with SOAP services that require different serialization, auth, and error handling.

Related MCP server: Selis MCP Server

What Changed in v2

v1 exposed 24 tools. v2 exposes 5.

Benchmark evidence is consistent that tool count degrades LLM tool-selection accuracy, and v1's surface had a lot of near-duplicates (get_project / create_project / update_project / delete_project → one manage_project with an action parameter). v2 collapses each domain into a single action-dispatched tool.

v1

v2

get_project, create_project, update_project, delete_project, get_project_attributes, list_field_reference

manage_project (create / get / update / delete / fields)

list_work, get_work, update_work, get_work_attributes, get_project_wbs

inspect_work (wbs / get / list / update)

create_task, read_task, delete_task, batch_create_tasks, batch_delete_tasks

manage_tasks (create / read / delete, all batch-capable)

read_financial_plan, upsert_financial_plan, discover_financial_plan_info, load_financial_plan_from_reference

manage_financial_plan (read / discover / upsert / copy)

oauth_ping

test_connection (structured checks, not a bare 401)

Other v2 changes:

  • OKR tools removed. Out of scope for v2; not shipped.

  • Scope is stated inline. Every tool description opens with a [LOCAL — ...] summary of what it does and does not cover, so Claude doesn't attempt discovery this server can't perform.

  • Warnings promoted. Create responses surface Planview API warnings at the top level (warnings, has_warnings, warning_hint) instead of burying them in meta.

  • Discover returns labeled keys. manage_financial_plan action=discover returns accounts and periods as [{key, description}], so you can pick "Aug 2026" without a second call.

  • Upsert accepts both payload shapes. Flat Lines: [...] or SOAP-style Lines.FinancialPlanLineDto[...] — nested envelopes are normalized on input.

What It Does

  • Projects — full CRUD, curated writable-field catalog, live attribute lookup

  • Work hierarchy — WBS tree navigation, work-node read/update

  • Tasks (SOAP) — create, read, delete via Planview's TaskService

  • Financial plans (SOAP) — read, discover structure, upsert, copy from a reference project

  • Connection diagnostics — structured config/token/ping checks

Tech Stack

  • Runtime: Python 3.10+

  • Protocol: MCP over stdio (official mcp Python SDK)

  • APIs: Planview REST (OAuth2 client credentials) + SOAP (zeep) — TaskService, FinancialPlanService

  • Validation: Pydantic for config and input models

  • HTTP: httpx (REST), zeep (SOAP)

Architecture

Claude Desktop / MCP Client
        ↓ stdio
  Local MCP Server (Python)
    ├── Planview REST API (OAuth2 client credentials)
    │     ├── Projects
    │     └── Work Items
    └── Planview SOAP API (zeep + OAuth2)
          ├── TaskService
          └── FinancialPlanService

Scope

This server is built for acting on things you can already identify — creating projects, writing financial plans, managing tasks, updating work nodes.

It deliberately does not do discovery. There is no "list all portfolios," no project search, no strategy-tree browsing. Every tool takes an id you already have. That keeps the surface small and the behavior predictable.

In practice you'll get ids from the Planview UI, from a previous call's response, or from another tool. If you use a read-oriented Planview MCP alongside this one, that pairing works fine — but nothing here depends on it.

Before You Start — Checklist

Gather these before you touch anything. You will be stuck without them.

  • API URL — Your Planview instance URL + /polaris (e.g., https://scdemo5xx.pvcloud.com/polaris) — must be lowercase

  • Client ID — From Administration → Users → OAuth2 credentials

  • Client Secret — Shown once at OAuth credential creation. If you didn't copy it, create a new one.

  • Global Tenant ID — Not obvious in the UI. Ask your Planview admin.

  • Parent structure codes — See Work Hierarchy Setup below. You cannot create a project without one.

⚠️ Do not skip this step. You will get through the entire setup and hit a wall at the end if any of these are missing or wrong.


Work Hierarchy Setup (Required for Project Creation)

manage_project action=create requires data.parent.structureCode — the work-hierarchy ($Plan) folder one level above Primary Planning Level. This server cannot discover it, so you need to grab it once from the Planview UI.

Do this before your first create and you won't have to think about it again.

  1. In Planview, go to Menu → Administration → Architecture → Primary Structures

  2. Find Work Structure in the list and click (define levels)

  3. Screenshot the tree — each node shows its name with the structure code in parentheses

  4. Paste it to Claude: "Commit these structure codes to memory for [tenant name]"

After that, project creation just works — Claude has the codes and picks the right parent.

Requires admin access. If you don't have it, ask whoever administers your tenant for a screenshot of that page — it's a one-time ask.

Two things to know:

  • Codes are per-tenant. Nothing carries between environments. Label the tenant when you commit them.

  • Resolve by code, never by name. Duplicate folder names are common (one demo tenant has three departments named "Marketing" and two nodes named "Archived Area"). The code is the only unambiguous handle.

The layer you want is where projects hang directly — typically Department, one below Division:

PlanRoot (Enterprise)
└── Active Enterprise Area
    └── Information Technology      ← Division
        ├── Mobility                ← Department  ✅ use this code
        ├── System Development      ← Department  ✅
        └── Business Applications   ← Department  ✅
            └── [your project]

Note: portfolio entity IDs are not work-hierarchy node IDs. The same business unit can be portfolio 5964 and work node 3787. They are not interchangeable.

Also distinct from alternate structures (Region, Line of Business, etc.), which live under Administration → Attributes and Column Sets → Alternate Structures. Both use structure codes; they are different trees.


Setup — Windows

Step 1: Install Python

If you've never installed Python before, that's fine. Go to python.org/downloads and download the latest version.

When the installer opens, you'll see a checkbox at the bottom that says "Add Python to PATH". Check that box. This is the most important part of the install.

After it finishes, close any open Command Prompt windows and open a fresh one:

  1. Press the Windows key, type cmd, press Enter

  2. Type these two commands, one at a time:

python --version
pip --version

You should see version numbers for both. If you see "not recognized," go back and reinstall Python with the PATH checkbox checked.

Step 2: Download this repository

  1. On the GitHub page, click the green Code button → Download ZIP

  2. Extract the zip to C:\portfoliosMCP

⚠️ Use a simple path like C:\portfoliosMCP. Do NOT put this in OneDrive, your Desktop, or any folder with spaces in the name. It will cause problems later.

⚠️ Check for a folder-inside-a-folder. After unzipping, open C:\portfoliosMCP. If you see another folder called portfoliosMCP-main instead of files like pyproject.toml, move everything up one level so pyproject.toml sits directly inside C:\portfoliosMCP.

Step 3: Install the server

  1. Open Command Prompt (Windows key → type cmd → Enter)

  2. Run these commands one at a time:

cd C:\portfoliosMCP
python -m venv venv
venv\Scripts\activate
pip install -e .

Wait for each command to finish before running the next one. The last command will download dependencies and may take a minute or two.

ℹ️ What does this do? It creates an isolated Python environment (venv) and installs the server into it. You must use pip install -e . — running pip install -r requirements.txt alone is not enough and the server will fail to start.

Step 4: Get your Python path

While still in Command Prompt, run:

where python

Copy the line that includes venv\Scripts\python.exe. It should look something like:

C:\portfoliosMCP\venv\Scripts\python.exe

You'll need this in the next step.

Step 5: Configure Claude Desktop

  1. Open Claude Desktop

  2. Go to Settings → Developer → Edit Config

Or: press Win+R, type %APPDATA%\Claude, press Enter, and open claude_desktop_config.json in Notepad.

If the file doesn't exist, create a new text file with that exact name.

Step 6: Paste this into the config file

{
  "mcpServers": {
    "portfoliosMCP_v2": {
      "command": "C:\\portfoliosMCP\\venv\\Scripts\\python.exe",
      "args": ["-m", "planview_portfolios_mcp"],
      "env": {
        "PLANVIEW_API_URL": "https://your-instance.pvcloud.com/polaris",
        "PLANVIEW_CLIENT_ID": "your_client_id",
        "PLANVIEW_CLIENT_SECRET": "your_client_secret",
        "PLANVIEW_TENANT_ID": "your_tenant_id",
        "USE_OAUTH": "true"
      }
    }
  }
}

Replace:

  • The command path with your output from Step 4

  • All four your_... values with your actual Planview credentials from the checklist

Two critical rules for this file:

  1. Double every backslash in the path. C:\portfoliosMCP must be written as C:\\portfoliosMCP. If you don't, you'll get a "Bad escaped character" error and Claude Desktop won't start properly.

  2. API URL must be lowercase. https://scdemo508.pvcloud.com/polaris — not SCDEMO508. Uppercase can cause authentication failures.

Step 7: Restart Claude Desktop

Close Claude Desktop completely — use File → Exit or right-click the icon in the system tray and quit. Just clicking the X may not fully close it. Then reopen it.

Step 8: Test it

In Claude Desktop, type:

Use test_connection to check my Planview connection

You should get a structured result with config, token, and ping checks. If any check fails, the response tells you which one — see the troubleshooting table below.


Setup — macOS

Step 1: Install Python

brew install python3

Step 2: Clone and install

git clone https://github.com/norman2112/portfoliosMCP.git
cd portfoliosMCP
python3 -m venv venv
source venv/bin/activate
pip install -e .

Step 3: Get your Python path

which python3
# Example output: /Users/yourname/portfoliosMCP/venv/bin/python3

Step 4: Open the Claude Desktop config file

# Press Cmd+Shift+G in Finder and paste this path:
~/Library/Application Support/Claude/claude_desktop_config.json

# Or from terminal:
open ~/Library/Application\ Support/Claude/claude_desktop_config.json

If the file doesn't exist, create it.

Step 5: Paste the config

{
  "mcpServers": {
    "portfoliosMCP_v2": {
      "command": "/Users/yourname/portfoliosMCP/venv/bin/python3",
      "args": ["-m", "planview_portfolios_mcp"],
      "env": {
        "PLANVIEW_API_URL": "https://your-instance.pvcloud.com/polaris",
        "PLANVIEW_CLIENT_ID": "your_client_id",
        "PLANVIEW_CLIENT_SECRET": "your_client_secret",
        "PLANVIEW_TENANT_ID": "your_tenant_id",
        "USE_OAUTH": "true"
      }
    }
  }
}

Replace the Python path with your output from Step 3. Fill in all four credential values.

Step 6: Quit Claude Desktop (Cmd+Q) and reopen it.

Step 7: Test it

Ask Claude: "Use test_connection to check my Planview connection"


Troubleshooting

What you see

What's wrong

How to fix it

python or pip is "not recognized"

Python isn't installed or isn't on PATH

Reinstall Python from python.org — check "Add Python to PATH"

"Bad escaped character in JSON"

Single backslashes in the config file

Change every \ to \\ in the command path

"No module named planview_portfolios_mcp"

Package not installed into the venv

Run pip install -e . from the repo folder (not pip install -r requirements.txt)

test_connection: token OK, ping 401

Tenant ID is wrong or empty

Almost never a stale secret — re-check PLANVIEW_TENANT_ID first

OAuth 400 error

Bad credentials, uppercase API URL, bearer token in CLIENT_SECRET, or (rarer) endpoint not binding the grant

Run test_connection and read diagnosis.verdict / next_step. credentials_rejected → fix id/secret. request_not_bound → support ticket (not a password problem). Compare config sha256_8 fingerprints for stale values

401 after a new token

Token issued, ping rejected

Almost always PLANVIEW_TENANT_ID. Run test_connection — if the token check is OK and ping fails, fix the tenant ID

401 Unauthorized

Wrong Client ID, Secret, or Tenant ID

Re-verify all credentials. Watch for extra spaces when pasting

Tools don't show up in Claude

Claude Desktop didn't fully restart

Quit via File → Exit (not just X), then reopen

Tools vanish after an edit

Server crashed on startup

Check your terminal for a stack trace, then toggle the connector off/on

JSON syntax error on startup

Malformed config file

Copy your config into jsonlint.com to find the error

Folder has no pyproject.toml

Nested folder from GitHub zip

Look one folder deeper — move contents up so pyproject.toml is at your root path


Getting Your Planview Credentials

  1. Log into Planview as admin → Administration → Users → OAuth2 credentials tab

  2. Click Create OAuth2 credentials

  3. Name it (e.g., "MCP Server"), select Portfolios Integration

  4. Copy the Client ID and Client Secret (⚠️ secret is only shown once)

  5. Find your Tenant ID in the admin panel or ask your Planview admin


Tools

Five tools, action-dispatched. Every description opens with a [LOCAL — ...] summary stating what the tool covers and what it can't do.

test_connection

No parameters. Runs three checks and always returns a structured result rather than throwing:

  1. Config — API URL shape, client id/secret present, tenant id present. Detects a bearer JWT pasted into PLANVIEW_CLIENT_SECRET.

  2. Token — tries multipart, then form, then JSON encoding.

  3. Ping — secured ping with that token and X-Tenant-Id.

Token succeeds but ping returns 401 → tenant ID is wrong or empty. Not a stale secret.

manage_project

Action

Notes

create

Requires data.description and data.parent.structureCode. Dates default to today and +6 months. create_default_tasks=true seeds five sample tasks via SOAP.

get

Requires project_id. Response includes parent.structureCode for reuse on later creates.

update

Partial PATCH. Field IDs are case-sensitive — call action=fields first if unsure.

delete

Destructive; removes the project and children.

fields

Curated writable-field catalog (~120 fields). Optional category filter. include_live_catalog=true fetches the live attribute list.

⚠️ Do not invent StructureCode values (Status, Region, RAG, etc.) on create. They are tenant-specific and the catalog's example values are not safe to send. Omit them and let Planview apply product defaults, then PATCH with codes you've verified.

Check has_warnings after every create. See Warnings below.

Not supported: listing the work tree, browsing $Strategy, or discovering a parent code. Bring the id.

inspect_work

Action

Notes

wbs (default)

Nested WBS tree for a known project_id. Optional max_depth.

get

One work node by work_id.

list

Work items under a known project. Prefer project_id (the filter is built for you); raw filter is a fallback, e.g. project.Id .eq 1906.

update

PATCH a work node. Returns 405 on some instances — use manage_project for project-level fields.

This is the work hierarchy ($Plan), not strategy ($Strategy). It cannot enumerate the Plan tree or list parents without an id.

manage_tasks

Action

Notes

create

Requires tasks (list; length 1 is fine). Each needs Description. FatherKey optional if project_id is set. An ekey:// is minted when Key is missing so retries don't duplicate.

read

Requires task_key or task_keys. Null fields in the response do not mean the create failed.

delete

Requires task_key or task_keys. Cascades to children. Per-key results.

Task updates are not supported. SOAP Update doesn't serialize reliably with zeep. Delete and recreate, or use the UI.

SOAP Create is not atomic — the response carries per-task success/failure. Retry only the failures.

manage_financial_plan

Action

Notes

read

Plan for project_id (or entity_key) + version_key (default Actual/Forecast key://14/1). include_entries=false by default to keep the payload small.

discover

Accounts and periods with fallback: target → reference project → config. Returns accounts / periods as [{key, description}] plus bare key lists. Source is tagged. Use this when upsert says "No editable lines."

upsert

Requires plan_data with Lines. Creates the plan if it doesn't exist.

copy

Copies account structure and values from reference_project_id onto target_project_id. Dry-run unless confirm=true. Always preview first.

Preferred upsert shape:

{
  "EntityKey": "key://2/$Plan/17696",
  "VersionKey": "key://14/1",
  "Lines": [{
    "AccountKey": "key://2/$Account/3653",
    "Unit": "Currency",
    "Entries": [
      { "PeriodKey": "key://16/183", "Value": 50000 },
      { "PeriodKey": "key://16/184", "Value": 50000 }
    ]
  }]
}

SOAP-style envelopes (Lines.FinancialPlanLineDto, Entries.EntryDto) are also accepted and normalized — so a read response can be fed back in after adding entries.

⚠️ Never build PeriodKeys by incrementing. Period ids skip across fiscal-year boundaries — one tenant runs …181, 182, 183 … 187 then jumps to 193. Only use keys returned by discover or read. Prefer the labeled periods array so you can see which month you're writing to.


Behaviors Worth Knowing

Warnings are non-fatal but real

Create responses promote Planview API warnings to top-level warnings / has_warnings / warning_hint. The project does exist — do not retry the create.

The common one is InvalidDefaultValues + InvalidStructureCode, meaning a tenant-configured attribute default points at a code Planview won't accept. The field is silently left unset. Example seen in the wild:

1020 InvalidStructureCode: (2263) is not a valid choice for Region

The attribute default is stored as a code|label pair captured when it was set (2263|North America). The label is a snapshot, not a live lookup — so a correct-looking label tells you nothing about whether the code still resolves. Fix it in Administration → Attributes and Column Sets → Alternate Structures → [attribute] → Edit Attribute, either by reactivating the code (Show Deactivated Elements) or repointing the Default list.

If nothing in your workflow reads that field, ignoring it is a legitimate choice.

SOAP echoes are incomplete

upsert routinely returns Lines: [] on success. This is normal, not a failure. Always verify with action=read. Same for tasks — null fields in a read response don't mean the write failed.

Read and upsert are shaped differently

read passes SOAP's response through as-is, so collections arrive wrapped in typed envelopes (Lines.FinancialPlanLineDto[]). That wrapper is an artifact of XML→JSON conversion — XML has no array type, so a converter keys the list by its child element name. upsert normalizes both shapes on input, so the round trip works either way.

Key URI formats

  • key://2/$Plan/12345 — direct

  • ekey://2/namespace/id — external

  • search://2/$Plan?description=Name — search

Field names in SOAP payloads are PascalCase (FatherKey, not father_key).

Known Limitations

  • No discovery. No portfolio lists, project search, or strategy browsing. Every tool needs an id you already have.

  • Parent structure codes — must come from the Planview UI. See Work Hierarchy Setup.

  • inspect_work action=update — 405 on some instances. Use manage_project for project-level items.

  • Task updates — not supported. Delete and recreate.

  • inspect_work action=list without a filter — some instances require one.

Development

python -m venv venv && source venv/bin/activate
pip install -e ".[dev]"
cp .env.example .env  # Add your credentials

# Run
python -m planview_portfolios_mcp

# Test & lint
pytest
black src/ && ruff check src/ && mypy src/

Requirements

  • Python 3.10+

  • Planview Portfolios instance with OAuth API access

  • mcp>=1.0.0 for MCP SDK (stdio transport)

  • httpx for REST, zeep for SOAP — see pyproject.toml

License

MIT

Available Tools

24 tools
batch_create_tasksA

[LOCAL — bulk write operation via SOAP. Beta MCP cannot create tasks.]

Batch create multiple tasks in a single SOAP call.

Much faster than calling create_task() multiple times. Creates all tasks in a single SOAP request, significantly reducing latency for bulk creation.

Args: tasks: List of task creation dictionaries. Each dict must contain: - Description: Task description (required) - FatherKey: Parent work entity key URI (required) Optional fields: Key, ScheduleStartDate, ScheduleFinishDate, Duration, etc. (same as create_task) options: Optional WorkOptionsDto dictionary (applies to all tasks)

Returns: Dict with per-task results (SOAP may partially succeed): - success: True only if all tasks succeeded - created: List of per-task entries in the same order as tasks: - description: task description (if available) - key: created task key (for succeeded tasks) or null (for failed tasks) - status: "success" | "failed" - error: present only for failed tasks - summary: {total, succeeded, failed} - warnings: optional list of warning messages

Raises: PlanviewValidationError: If task data is invalid PlanviewAuthError: If authentication fails PlanviewError: For other errors

Example: tasks = [ { "Description": "Task 1", "FatherKey": "key://2/$Plan/12345", "ScheduleStartDate": "2024-01-01T08:00:00", "ScheduleFinishDate": "2024-01-15T17:00:00" }, { "Description": "Task 2", "FatherKey": "key://2/$Plan/12345", "ScheduleStartDate": "2024-01-16T08:00:00", "ScheduleFinishDate": "2024-01-30T17:00:00" } ] result = await batch_create_tasks(tasks)

Notes: - All tasks are created in a single SOAP call, making this much faster than individual create_task() calls - If some tasks fail, this tool still returns the successful ones so callers can avoid retrying the already-created tasks (which would create duplicates) - Response fields may be null - this is normal SOAP API behavior - Use read_task() to verify individual tasks if needed - Recommended to use external Key (ekey://) to prevent duplicates

Inputs: tasks (required), options.

batch_delete_tasksA

[LOCAL — bulk write operation via SOAP. Beta MCP cannot delete tasks.]

Delete multiple tasks in bulk using the SOAP TaskService.

Planview SOAP operations are not guaranteed atomic. This tool therefore returns per-key success/failure information so callers can safely retry only the failed keys (without re-deleting ones that already succeeded).

Inputs: task_keys (required).

create_projectA

[LOCAL — write operation. Beta MCP is read-only and cannot create projects.]

Create a new project.

Creates a project using the Planview Portfolios API. The payload should match the CreateProjectDtoPublic schema from the Swagger documentation.

Projects MUST have defined start and finish dates. If dates are not provided, default dates will be set: start date = today, finish date = 6 months from today.

Args: data: Project creation payload. Minimum required fields: - description: Project name/description (required) - parent: Object with structureCode (required) Optional fields: - scheduleStart: Start date (ISO 8601 format: YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS) - scheduleFinish: Finish date (ISO 8601 format: YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS) If not provided, defaults to today and 6 months from today respectively. attributes: Optional list of attributes to return in response create_default_tasks: If True, automatically creates 5 default sample tasks (Project Setup, Requirements Gathering, Design, Development, Testing)

Returns: Created project data from API response. The response may include warnings (e.g., "InvalidStructureCode", "InvalidDefaultValues") which are non-fatal. Projects are created successfully even with these warnings - they indicate configuration issues but don't prevent project creation.

Example: { "description": "Jon's MCP Project", "parent": {"structureCode": "14170"} }

With explicit dates:
{
    "description": "My Project",
    "parent": {"structureCode": "14170"},
    "scheduleStart": "2024-01-01",
    "scheduleFinish": "2024-06-30"
}

Notes: - See your instance's Swagger docs at {PLANVIEW_API_URL}/swagger/index.html for full schema details and additional optional fields like shortName, attributes, etc. - Warnings are non-fatal: Warnings like "InvalidStructureCode" or "InvalidDefaultValues" indicate Planview configuration issues (e.g., default region code not configured) but don't prevent successful project creation. Check response for warning details.

Note: On create, you MUST provide 'description' (project name) and 'parent' (structureCode of parent work item). Optional: scheduleStart, scheduleFinish (default to today and +6 months). For available writable fields, call list_field_reference() to browse by category: core_identity, dates, progress, status_assessments, investment_scoring, strategic_classification, wsjf_safe, risk, business_case_text, lifecycle_roles, financial_metrics, agileplace_integration, swot

Inputs: data (required), attributes, create_default_tasks.

create_taskA

[LOCAL — write operation via SOAP. Beta MCP cannot create tasks.]

Create a new task using SOAP TaskService.

Creates a task (planning entity below PPL) in Planview Portfolios using the SOAP API.

Args: task_data: Task data dictionary with TaskDto2 fields. Required fields: - Description: Task description (required) - FatherKey: Parent work entity key URI (required) Optional fields: - Key: External key URI (recommended to prevent duplicates) - ScheduleStartDate: Schedule start date (ISO 8601) - ScheduleFinishDate: Schedule finish date (ISO 8601) - Duration: Duration in minutes - CalendarKey: Calendar key URI - EnterProgress: Enable manual progress entry (bool) - IsMilestone: Is this a milestone (bool) - IsTicketable: Can create tickets (bool) - IsDeliverable: Is deliverable (bool) - PercentComplete: Percent complete (0-100) - WorkId: Work ID string - WorkStatusKey: Work status key URI - LifecycleAdminUserKey: Lifecycle admin user key URI - Notes: Task notes - Place: Task place/order options: Optional WorkOptionsDto dictionary: - CopyMissingValuesFromPlanview: Copy missing values from existing record (bool) - RollupActuals: Roll up actuals to parent (bool) - ClearStagingTableAfterRun: Clear staging table after run (bool, default: True)

Returns: Dict with: - success: True if operation succeeded - data: Task DTO (may have null fields - this is normal SOAP API behavior) - warnings: List of non-fatal warnings

Note: The SOAP API may return null for many fields (e.g., ScheduleStartDate, Duration)
even though the task was created successfully with those values. This is expected behavior.
Use read_task() to verify the task was created with the correct data.

Raises: PlanviewValidationError: If task data is invalid PlanviewAuthError: If authentication fails PlanviewError: For other errors

Examples: Minimal (required fields only): {"Description": "My Task", "FatherKey": "key://2/$Plan/12345"}

With external key (recommended to prevent duplicates):
    {
        "Description": "My Task",
        "FatherKey": "key://2/$Plan/12345",
        "Key": "ekey://2/namespace/task-1"
    }

With schedule dates:
    {
        "Description": "My Task",
        "FatherKey": "key://2/$Plan/12345",
        "ScheduleStartDate": "2024-01-01T08:00:00",
        "ScheduleFinishDate": "2024-01-15T17:00:00"
    }

Notes: - Field names must use PascalCase (e.g., FatherKey, not father_key) - Date format: ISO 8601 (YYYY-MM-DDTHH:MM:SS or YYYY-MM-DD) - Fields are automatically sorted alphabetically (Planview requirement) - None values are automatically filtered - Use external key (ekey://) to prevent duplicate creation

Known SOAP API Behaviors: - Response fields may be null: The SOAP API doesn't always populate all fields in the response DTO, even though the task was created successfully with those values. This is normal. The task IS created correctly in Planview - use read_task() to verify. - Warnings are non-fatal: Warnings indicate configuration issues but don't prevent creation. Check the warnings array in the response for details.

Inputs: task_data (required), options.

delete_projectA

[LOCAL — write operation. Beta MCP is read-only and cannot delete projects. WARNING: destructive operation, deletes project and all child data.]

Delete a project by ID.

Deletes a project from Planview Portfolios using the REST API. WARNING: This is destructive and will delete the project and all its child tasks, financial plans, and other associated data.

Args: project_id: The structureCode/ID of the project to delete.

Returns: Dict with deletion status.

Raises: PlanviewNotFoundError: If the project doesn't exist. PlanviewAuthError: If authentication fails. PlanviewError: For other errors.

Inputs: project_id (required).

delete_taskA

[LOCAL — write operation via SOAP. Beta MCP cannot delete tasks.]

Delete a task using SOAP TaskService.

Deletes a task from Planview Portfolios using the SOAP API. Note: Deleting a task will also delete all its child tasks.

Args: task_key: Task key URI in key://, search://, or ekey:// format

Returns: Dict with deletion status

Raises: PlanviewValidationError: If task_key is invalid PlanviewNotFoundError: If task is not found PlanviewAuthError: If authentication fails PlanviewError: For other errors

Inputs: task_key (required).

discover_financial_plan_infoA

[LOCAL — financial plan discovery with smart fallback. No Beta MCP equivalent exists.]

Discover financial plan information with smart fallback.

Attempts to read the financial plan for the target project. If that fails (e.g., project is too new), falls back to reading a reference project's financial plan to discover available accounts and periods.

Optimized to check config data first (instant), and skip slow target reads for new projects when skip_target_read=True. Use include_entries=False (default for this tool) to avoid large EntryDto arrays and reduce payload size.

Args: entity_key: Target project entity key (e.g., "key://2/$Plan/17291") version_key: Financial plan version key (default: "key://14/1" for Actual/Forecast) reference_entity_key: Optional reference project entity key for fallback. Defaults to None - if not provided and target read fails, returns config data. skip_target_read: If True, skip reading target project's plan (much faster for new projects). Defaults to False for backward compatibility. include_entries: If False, strip EntryDto arrays from each line (default False for smaller response). summary: If True, return only account_keys and period_keys (minimal response). fields: If set, return only these top-level data fields.

Returns: Dict with financial plan data including accounts and periods, or None if unavailable. May return config-based data structure for fast path.

Example: # Fast path for new projects - skip target read, use config or reference plan_info = await discover_financial_plan_info( entity_key="key://2/$Plan/17291", reference_entity_key="key://2/$Plan/3818", skip_target_read=True # Skip slow read for new project )

# Standard path - try target first, then reference
plan_info = await discover_financial_plan_info(
    entity_key="key://2/$Plan/17291",
    reference_entity_key="key://2/$Plan/3818"
)

if plan_info:
    # Extract accounts and periods
    lines = plan_info.get("data", {}).get("Lines", {}).get("FinancialPlanLineDto", [])

Inputs: entity_key (required), fields, summary, version_key, +3 more.

get_key_results_for_objectiveA

[LOCAL — OKR key results for a single objective. No Beta MCP equivalent exists.]

Get all key results for a specific objective.

Args: objective_id: The ID of the objective

Returns: Dict with key_results array

Example: { "key_results": [ { "id": 28304, "name": "Increase NPS Score", "objective_id": 17841, ... } ] }

Inputs: objective_id (required).

get_projectA

[LOCAL — single project read by ID. For listing/searching projects across a portfolio, use Beta MCP's listProjectsByPortfolioId or searchProjectByName instead.]

Get a single project by id.

Inputs: project_id (required), attributes.

get_project_attributesA

[LOCAL — raw attribute list. For natural-language attribute search, use Beta MCP's searchAttributes instead.]

List available project attributes.

Inputs: no named parameters.

get_project_wbsA

[LOCAL — nested WBS tree with schedule data. For a flat hierarchy view, Beta MCP's getWorkHierarchy is an alternative.]

Get a project's WBS as a nested, lean tree.

Calls list_work with project.Id .eq {project_id} and rebuilds the parent/child structure into a sorted tree.

Node shape (lean): structureCode, description, depth, place, isMilestone, hasChildren, scheduleStart, scheduleFinish, status, constraintDate, constraintType, and children.

Inputs: project_id (required), max_depth, include_milestones.

get_workB

[LOCAL — read any single work hierarchy node by ID (including portfolio-level nodes). For listing projects within a portfolio, use Beta MCP's listProjectsByPortfolioId.]

Get a single work item by id.

Inputs: work_id (required), attributes.

get_work_attributesA

[LOCAL — raw work attribute list. For natural-language attribute search, use Beta MCP's searchAttributes(entity='work').]

Get available work attributes.

Inputs: no named parameters.

list_all_objectives_with_key_resultsA

[LOCAL — OKR objectives with key results. No Beta MCP equivalent exists.]

List all objectives with their key results.

This is a convenience function that fetches all objectives and optionally includes their key results in the response.

Args: limit: Maximum number of objectives per page (default: 500, max: 500) include_key_results: If True, fetch key results for each objective (default: True)

Returns: Dict with objectives and their key results: { "total_records": 1100, "objectives": [ { "id": 17841, "name": "Increase customer satisfaction", "key_results": [...], # Only if include_key_results=True ... } ] }

Inputs: limit, include_key_results.

list_field_referenceA

[LOCAL — field discovery for write operations. For read-side attribute discovery, use Beta MCP's searchAttributes instead.]

List available writable project fields organized by category.

Use this tool to discover which field IDs to pass to update_project or create_project.

Args: category: Optional category filter. If not provided, returns all categories. Valid categories: core_identity, dates, progress, status_assessments, investment_scoring, strategic_classification, wsjf_safe, risk, business_case_text, lifecycle_roles, financial_metrics, agileplace_integration, swot

Inputs: category.

list_objectivesA

[LOCAL — OKR objectives list. No Beta MCP equivalent exists for OKRs.]

List all objectives from the OKRs API.

Args: ids: Optional comma-separated list of objective IDs to filter by limit: Number of results to return (default: 10, max: 500) offset: Offset for pagination (default: 0)

Returns: Dict with objectives list and total_records count

Example: { "fetch_objectives": { "total_records": 1100, "objectives": [...] } }

Inputs: ids, limit, offset.

list_workA

[LOCAL — query work items with filter (e.g., project.Id .eq X). Limited filtering support. For portfolio-scoped project lists, use Beta MCP's listProjectsByPortfolioId instead.]

List work items using a filter string (e.g., project.Id .eq 1906).

If fields is provided, the response is trimmed per work item to reduce payload size.

Inputs: filter (required), fields, attributes.

load_financial_plan_from_referenceA

[LOCAL — copy financial plan from reference project. No Beta MCP equivalent exists.]

Load a financial plan onto a project by copying the account structure and values from a reference project. Defaults to dry-run mode (confirm=False) which shows a preview without writing anything. Set confirm=True to execute.

This is a heavy operation — always preview first unless you're sure.

Inputs: target_project_id (required), reference_project_id (required), confirm, version_key, +1 more.

oauth_pingA

[LOCAL — auth health check for this server's connection.]

Call secured ping to verify credentials.

Inputs: no named parameters.

read_financial_planA

[LOCAL — SOAP financial plan read. No Beta MCP equivalent exists for financial plans.]

Read a financial plan for a project using SOAP FinancialPlanService.

This tool reads the financial plan structure including all account lines, entries, and periods. Use this to discover available accounts before adding new lines with upsert_financial_plan.

Args: entity_key: Project entity key (e.g., "key://2/$Plan/17288") version_key: Financial plan version key (e.g., "key://14/1" for Actual/Forecast) include_entries: If True, include EntryDto arrays for each line. Defaults to False. summary: If True, return only account_keys and period_keys (minimal response). fields: If set, return only these top-level data fields (e.g. ["EntityKey", "VersionKey", "Lines"]).

Returns: Dict with financial plan data including: - EntityKey: Project entity key - VersionKey: Version key - Lines: Array of FinancialPlanLineDto objects with account details (unless summary=True) - ModelDescription: Financial model name - VersionDescription: Version name

Raises: PlanviewValidationError: If entity_key or version_key is invalid PlanviewNotFoundError: If financial plan is not found PlanviewAuthError: If authentication fails PlanviewError: For other errors

Example: # Read financial plan for project 17288, Actual/Forecast version result = await read_financial_plan( entity_key="key://2/$Plan/17288", version_key="key://14/1" )

# Extract available accounts
lines = result.get("data", {}).get("Lines", {}).get("FinancialPlanLineDto", [])
accounts = {}
for line in lines:
    account_key = line.get("AccountKey")
    if account_key:
        accounts[account_key] = {
            "description": line.get("AccountDescription"),
            "parent": line.get("AccountParentDescription"),
            "unit": line.get("Unit"),
        }

Inputs: entity_key (required), version_key (required), fields, summary, +1 more.

read_taskA

[LOCAL — SOAP task read by key. For reading tasks with custom attributes by project or task ID, Beta MCP's getTasksByProjectIds or getTasksByTaskIds may be richer.]

Read a task by key using SOAP TaskService.

Reads a task from Planview Portfolios using the SOAP API.

Args: task_key: Task key URI in key://, search://, or ekey:// format

Returns: Dict with task data (full TaskDto2)

Raises: PlanviewValidationError: If task_key is invalid PlanviewNotFoundError: If task is not found PlanviewAuthError: If authentication fails PlanviewError: For other errors

Example: task_key: "key://2/$Plan/12345" or: "ekey://2/namespace/task-1" or: "search://2/$Plan?description=Task Name"

Inputs: task_key (required).

update_projectA

[LOCAL — write operation. Beta MCP is read-only and cannot update projects.]

Update an existing project (partial payload).

Send only the fields you want to change. Field IDs are case-sensitive. Planview business rules may override some values (e.g., dates get calendar-aligned).

IMPORTANT CONSTRAINTS:

  • Duration is calculated from start/finish — don't send it directly

  • Lifecycle-controlled Work Status cannot be overridden via API

  • StructureCode fields: send {"structureCode": "CODE"} or {"structureCode": "CODE", "description": "LABEL"}

  • Fields marked PPL-only only work at Primary Planning Level (projects), not sub-tasks

For available writable fields, call list_field_reference() to browse by category: core_identity, dates, progress, status_assessments, investment_scoring, strategic_classification, wsjf_safe, risk, business_case_text, lifecycle_roles, financial_metrics, agileplace_integration, swot

Inputs: updates (required), project_id (required), attributes.

update_workA

[LOCAL — write operation. Beta MCP is read-only and cannot update work items.]

Update an existing work item (partial payload).

Useful for updating phase/task fields like ExecType (execution type) on work items via PATCH /public-api/v1/work/{id}.

Note: Some instances reject PATCH on /work/{id} with HTTP 405. In that case, this tool returns a clear limitation message.

Inputs: updates (required), work_id (required), attributes.

upsert_financial_planA

[LOCAL — SOAP financial plan write. No Beta MCP equivalent exists.]

Upsert (create or update) a financial plan using SOAP FinancialPlanService.

Creates or updates a financial plan in Planview Portfolios using the SOAP API. This is a single-line update tool optimized for simple use cases.

Args: plan_data: Financial plan data dictionary. Required fields: - Key: Financial plan key URI (e.g., "ekey://12/MyPlan") OR - EntityKey: Entity key URI (e.g., "key://2/$Plan/17286") AND - VersionKey: Version key URI (e.g., "key://14/57") - Lines: List of FinancialPlanLineDto dictionaries Each FinancialPlanLineDto requires: - AccountKey: Account key URI (e.g., "key://2/$Account/13607") - Unit: Unit type ("Currency", "Units", "Unit Cost", "Unit Price", "FTE", "Hours") - Entries: List of EntryDto dictionaries - CurrencyKey: Currency key URI (defaults to "key://1/USD" if not provided) - Attributes: Optional list of LineAttributeDto dictionaries Each EntryDto requires: - PeriodKey: Period key URI (e.g., "key://16/197") - Value: Numeric value

Returns: Dict with: - success: True if operation succeeded - data: Financial plan DTO (may have empty Lines array - this is normal SOAP API behavior) - warnings: List of non-fatal warnings (e.g., "InvalidStructureCode", "InvalidDefaultValues")

Note: The SOAP API may return empty Lines array in the response even though data was persisted.
This is expected behavior - use read_financial_plan() to verify the data was saved.

Raises: PlanviewValidationError: If plan data is invalid PlanviewAuthError: If authentication fails PlanviewError: For other errors

Examples: Minimal (single line, single period): { "EntityKey": "key://2/$Plan/17286", "VersionKey": "key://14/57", "Lines": [{ "AccountKey": "key://2/$Account/13607", "Unit": "Currency", "CurrencyKey": "key://1/USD", "Entries": [{ "PeriodKey": "key://16/197", "Value": 10000 }] }] }

Using existing plan Key:
    {
        "Key": "ekey://12/MyPlan",
        "Lines": [{
            "AccountKey": "key://2/$Account/13607",
            "Unit": "Currency",
            "Entries": [{
                "PeriodKey": "key://16/197",
                "Value": 10000
            }]
        }]
    }

Notes: - Field names must use PascalCase (e.g., AccountKey, not account_key) - Only changed or added lines must be sent - AccountKey and PeriodKey must match Planview configuration - Unit types: "Currency", "Units", "Unit Cost", "Unit Price", "FTE", "Hours" - For new projects, the financial plan may not exist yet - upsert will create it

Common Errors and Solutions: - "No editable lines were provided": The account/period keys don't match the model. Solution: Use discover_financial_plan_info() or read_financial_plan() to find valid keys. - "Account not found in model": The specified account doesn't exist for this version. Solution: Use discover_financial_plan_info() with a reference project to discover valid accounts. - "Unable to find the requested Financial Plan": Plan doesn't exist (common for new projects). Solution: Use upsert_financial_plan() directly - it creates the plan if needed.

Known SOAP API Behaviors: - Response may show empty Lines array: The SOAP API doesn't always echo back the full payload. This is normal - the data IS persisted. Use read_financial_plan() to verify. - Warnings are non-fatal: Warnings like "InvalidStructureCode" or "InvalidDefaultValues" indicate configuration issues but don't prevent successful creation. Check the warnings array in the response for details.

Inputs: plan_data (required).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.7/5.0

    Scored across 24 tools

    Disambiguation3/5

    Tools are mostly distinct, but there is overlap between get_work and read_task (both retrieve work items/tasks) and between list_objectives and list_all_objectives_with_key_results. The descriptions provide some guidance, but an agent may struggle to choose the right tool for reading a specific task or objective.

    Naming Consistency3/5

    The naming follows a verb_noun pattern, but verbs are inconsistent: get, read, list, discover, load, upsert. For example, get_project uses 'get' while read_task uses 'read', and list_objectives is a 'list' while list_all_objectives_with_key_results is also a list but with a qualifier. oauth_ping does not follow the pattern.

    Tool Count4/5

    With 24 tools, the server covers multiple domains (projects, tasks, financial plans, OKRs) without being excessive. The count is reasonable for the complexity of Planview Portfolios, though a few tools could be merged (e.g., list_objectives into list_all_objectives_with_key_results).

    Completeness3/5

    The tool set covers core CRUD for projects and tasks (though task update is not explicit, only update_work), reading for OKRs, and financial plan operations. However, there are gaps: no create/update/delete for objectives, no delete for financial plans, and no search or portfolio-level operations. References to Beta MCP alternatives suggest intentional gaps.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      D
      maintenance
      Enables work item management in DevOps Plan systems, allowing users to create, retrieve, filter, and delete work items, as well as manage applications, projects, components, and work item types through natural language.
      14
      16 npm
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables management of PMO entities such as actions, risks, issues, projects, deliverables, decisions, KPIs, and objectives through natural language from any MCP-compatible agent.
      58 npm
      Server Side Public , v 1
    • A
      license
      A
      quality
      D
      maintenance
      Enables natural language control of OmniPlan 4 on macOS, allowing creation and manipulation of tasks, resources, dependencies, and project metadata through MCP-compatible agents.
      22
      1
      MIT
    • A
      license
      A
      quality
      D
      maintenance
      Enables natural language interactions for creating, querying, updating, and deleting job plans in IBM Maximo.
      4
      MIT