Planview Portfolios Actions MCP Server
This server provides write/action capabilities for Planview Portfolios, unifying REST and SOAP APIs under a single MCP interface. It is designed to complement Planview's read-only Beta MCP server, handling all write/action operations across projects, tasks, financial plans, OKRs, and work hierarchy.
Projects (REST)
Full CRUD: create, read, update, and delete projects
Discover available project attributes and writable fields by category
Navigate the Work Breakdown Structure (WBS) as a nested tree with schedule data
Work Hierarchy (REST)
List work items using filters (e.g., by project ID)
Get a single work/hierarchy node by ID (including portfolio-level nodes)
Update work items with partial field changes
Tasks (SOAP)
Create, read, and delete tasks individually
Batch create and batch delete tasks with per-key success/failure reporting
Financial Plans (SOAP)
Read financial plan structure (accounts, periods) for a project
Upsert (create or update) financial plan lines
Discover plan info with smart fallback to a reference project
Copy a financial plan from a reference project to a target project (dry-run by default)
OKRs (REST)
List objectives with pagination and optional ID filtering
Get key results for a specific objective
Bulk fetch all objectives with their key results in one call
Utility
OAuth ping to verify credentials and connection health
Click on "Deploy 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., "@Planview Portfolios Actions MCP Server Create a project named 'Q4 Marketing'"
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.
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 |
|
|
|
|
|
|
|
|
|
|
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 inmeta.Discover returns labeled keys.
manage_financial_plan action=discoverreturnsaccountsandperiodsas[{key, description}], so you can pick "Aug 2026" without a second call.Upsert accepts both payload shapes. Flat
Lines: [...]or SOAP-styleLines.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
mcpPython 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
└── FinancialPlanServiceScope
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 lowercaseClient 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.
In Planview, go to Menu → Administration → Architecture → Primary Structures
Find Work Structure in the list and click (define levels)
Screenshot the tree — each node shows its name with the structure code in parentheses
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
5964and work node3787. 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:
Press the Windows key, type
cmd, press EnterType these two commands, one at a time:
python --version
pip --versionYou 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
On the GitHub page, click the green Code button → Download ZIP
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 calledportfoliosMCP-maininstead of files likepyproject.toml, move everything up one level sopyproject.tomlsits directly insideC:\portfoliosMCP.
Step 3: Install the server
Open Command Prompt (Windows key → type
cmd→ Enter)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 usepip install -e .— runningpip install -r requirements.txtalone is not enough and the server will fail to start.
Step 4: Get your Python path
While still in Command Prompt, run:
where pythonCopy the line that includes venv\Scripts\python.exe. It should look something like:
C:\portfoliosMCP\venv\Scripts\python.exeYou'll need this in the next step.
Step 5: Configure Claude Desktop
Open Claude Desktop
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
commandpath with your output from Step 4All four
your_...values with your actual Planview credentials from the checklist
Two critical rules for this file:
Double every backslash in the path.
C:\portfoliosMCPmust be written asC:\\portfoliosMCP. If you don't, you'll get a "Bad escaped character" error and Claude Desktop won't start properly.API URL must be lowercase.
https://scdemo508.pvcloud.com/polaris— notSCDEMO508. 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 connectionYou 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 python3Step 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/python3Step 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.jsonIf 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 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 |
"No module named planview_portfolios_mcp" | Package not installed into the venv | Run |
| Tenant ID is wrong or empty | Almost never a stale secret — re-check |
OAuth 400 error | Bad credentials, uppercase API URL, bearer token in CLIENT_SECRET, or (rarer) endpoint not binding the grant | Run |
401 after a new token | Token issued, ping rejected | Almost always |
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 | Nested folder from GitHub zip | Look one folder deeper — move contents up so |
Getting Your Planview Credentials
Log into Planview as admin → Administration → Users → OAuth2 credentials tab
Click Create OAuth2 credentials
Name it (e.g., "MCP Server"), select Portfolios Integration
Copy the Client ID and Client Secret (⚠️ secret is only shown once)
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:
Config — API URL shape, client id/secret present, tenant id present. Detects a bearer JWT pasted into
PLANVIEW_CLIENT_SECRET.Token — tries multipart, then form, then JSON encoding.
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 |
| Requires |
| Requires |
| Partial PATCH. Field IDs are case-sensitive — call |
| Destructive; removes the project and children. |
| Curated writable-field catalog (~120 fields). Optional |
⚠️ Do not invent StructureCode values (Status, Region, RAG, etc.) on create. They are tenant-specific and the catalog's
examplevalues are not safe to send. Omit them and let Planview apply product defaults, then PATCH with codes you've verified.
Check
has_warningsafter 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 |
| Nested WBS tree for a known |
| One work node by |
| Work items under a known project. Prefer |
| PATCH a work node. Returns 405 on some instances — use |
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 |
| Requires |
| Requires |
| Requires |
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 |
| Plan for |
| Accounts and periods with fallback: target → reference project → config. Returns |
| Requires |
| Copies account structure and values from |
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 … 187then jumps to193. Only use keys returned bydiscoverorread. Prefer the labeledperiodsarray 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 RegionThe 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— directekey://2/namespace/id— externalsearch://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. Usemanage_projectfor project-level items.Task updates — not supported. Delete and recreate.
inspect_work action=listwithout 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.0for MCP SDK (stdio transport)httpxfor REST,zeepfor SOAP — seepyproject.toml
License
MIT
Available Tools
24 toolsbatch_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
Scored across 24 tools
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.
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.
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).
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
Related MCP Connectors
Connect your Cormak.app account to manage projects, task boards, AI agents and creative work from your AI assistant. The server exposes up to 83 tools, limited to the OAuth permissions you choose. Organize tasks, read and update project drafts, inspect saved studio results, create product descriptions and images, and request supported photo, video, 3D, voiceover, SEO and advertising operations. Publishing saved projects requires separate owner approval. Reading and ordinary manual task management do not themselves start paid generation. AI operations use credits from your connected Cormak account and require owner confirmation, or an existing bounded spending permission for supported product operations. You can limit and revoke connections in Cormak. The MCP does not provide bank access, payment details or provider API keys. CRM, streaming website generation and business-plan generation are not exposed through this integration. Get started: https://app.cormak.app/mcp Setup guide and examples: https://cormak.app/blog/cormak-mcp-guide Privacy: https://cormak.app/privacy Support: https://cormak.app/contact
Read and write Mission Control state via MCP — projects, tasks, subtasks, templates, status updates.
Manage projects, tasks, time tracking, and team collaboration through natural language.
Manage Superlist tasks and lists in plain language from any MCP-compatible AI agent.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables 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.1416 npmMIT
- AlicenseNot gradedqualityDmaintenanceEnables management of PMO entities such as actions, risks, issues, projects, deliverables, decisions, KPIs, and objectives through natural language from any MCP-compatible agent.58 npmServer Side Public , v 1
- AlicenseAqualityDmaintenanceEnables natural language control of OmniPlan 4 on macOS, allowing creation and manipulation of tasks, resources, dependencies, and project metadata through MCP-compatible agents.221MIT
- AlicenseAqualityDmaintenanceEnables natural language interactions for creating, querying, updating, and deleting job plans in IBM Maximo.4MIT