P4 Plan MCP Server

![Support](https://img.shields.io/badge/Support-Community-yellow.svg) ![License](https://img.shields.io/badge/License-MIT-blue.svg)

MCP (Model Context Protocol) server for P4 Plan, enabling AI assistants like Claude and VS Code Copilot to interact with P4 Plan project management data.

## Architecture This service acts as a **stateless** protocol adapter between MCP clients (AI assistants) and the P4 Plan GraphQL API, using **stdio transport** (stdin/stdout). ``` ┌─────────────────┐ stdio (stdin/stdout) ┌─────────────────┐ GraphQL ┌─────────────────┐ │ AI Client │ ────────────────────────▶ │ P4 Plan MCP │ ──────────────▶ │ P4 Plan GraphQL │ │(Claude, Copilot)│ Spawns as child process │ Server │ Port 4000 │ API │ │ │ P4PLAN_API_AUTH_TOKEN │ (stateless) │ Bearer token │ │ └─────────────────┘ env var └─────────────────┘ forwarded └─────────────────┘ ``` The client spawns the MCP server as a child process. Authentication is provided via the `P4PLAN_API_AUTH_TOKEN` environment variable, which the server validates at startup and forwards to the GraphQL API on every tool call. ## Prerequisites | Requirement | Version | Notes | |-------------------------|--------------------------|----------------------------------------------------------------------------------------| | **Node.js** | \>= 20 (24+ recommended) | The server targets ES2023. Check with `node -v`. | | **npm** | \>= 9 | Comes with Node.js. Check with `npm -v`. | | **P4 Plan API** | \>= 2026.1.002 | Required for all tool operations. Earlier versions are not supported. | > **Tip:** Use [nvm](https://github.com/nvm-sh/nvm) to manage Node.js versions, or skip the Node.js requirement entirely by using [Docker](#docker). ## Quick Start (npx) The fastest way to get started — **no installation required**. Just configure your MCP client to use `npx`: ```bash npx -y @perforce/p4plan-mcp ``` `npx` automatically downloads and runs the latest version of the server. Your MCP client (VS Code, Claude Desktop, etc.) handles this for you — just add the config below and start chatting. **VS Code** — add to `.vscode/mcp.json`: ```json { "servers": { "p4-plan": { "type": "stdio", "command": "npx", "args": ["-y", "@perforce/p4plan-mcp"], "env": { "P4PLAN_API_AUTH_TOKEN": "YOUR_JWT_TOKEN", "P4PLAN_API_URL": "http://localhost:4000" } } } } ``` **Claude Desktop** — add to your config: ```json { "mcpServers": { "p4-plan": { "command": "npx", "args": ["-y", "@perforce/p4plan-mcp"], "env": { "P4PLAN_API_AUTH_TOKEN": "YOUR_JWT_TOKEN", "P4PLAN_API_URL": "http://localhost:4000" } } } } ``` > **Note:** The `-y` flag auto-confirms the npm install prompt so the server starts without user interaction. See [Client Configuration](#client-configuration) for more options including Docker, secure token prompts, and local builds. ## MCP Registry This server is published to the official [MCP Registry](https://registry.modelcontextprotocol.io) as **`io.github.perforce/p4plan-mcp`**. Discover it via the registry API: ```bash curl 'https://registry.modelcontextprotocol.io/v0/servers?search=io.github.perforce/p4plan-mcp' ``` MCP clients that support registry discovery can install the server by name without needing to know the npm package identifier. ## Installation
Build from source For development or when you want to run from a local clone: ```bash npm ci npm run build # For using npx locally npm link ```
Run from Docker Run the MCP server via Docker instead of installing Node.js locally. The MCP client (VS Code, Claude Desktop) spawns the container as a child process — same as `npx`, just using `docker` as the command. The image is published to Docker Hub at [`perforce/p4plan-mcp`](https://hub.docker.com/r/perforce/p4plan-mcp), built multi-arch (`linux/amd64` + `linux/arm64`) on every release, with SLSA provenance and SBOM attestations. **VS Code** (`.vscode/mcp.json`): ```json { "servers": { "p4-plan": { "type": "stdio", "command": "docker", "args": [ "run", "-i", "--rm", "-e", "P4PLAN_API_AUTH_TOKEN=YOUR_JWT_TOKEN", "-e", "P4PLAN_API_URL=http://host.docker.internal:4000", "perforce/p4plan-mcp:latest" ] } } } ``` **Claude Desktop:** ```json { "mcpServers": { "p4-plan": { "command": "docker", "args": [ "run", "-i", "--rm", "-e", "P4PLAN_API_AUTH_TOKEN=YOUR_JWT_TOKEN", "-e", "P4PLAN_API_URL=http://host.docker.internal:4000", "perforce/p4plan-mcp:latest" ] } } } ``` > **Note:** Use `host.docker.internal` (macOS/Windows) or `172.17.0.1` (Linux) to reach the P4 Plan GraphQL API running on the host machine. > **Pin a specific version** by replacing `:latest` with `:2026.2.0` (or whichever tag) for reproducible deployments. **Build locally** (for development against unreleased changes): ```bash docker build -t p4plan-mcp:dev . # then swap "perforce/p4plan-mcp:latest" for "p4plan-mcp:dev" in the configs above ```
## Configuration Copy the example config and configure: ```bash cp config-example.env .env ``` Edit `.env` with your settings: ```dotenv # JWT token for authenticating with P4 Plan GraphQL API P4PLAN_API_AUTH_TOKEN=your-jwt-token # P4 Plan GraphQL API URL P4PLAN_API_URL=http://localhost:4000 # Logging level LOG_LEVEL=debug # Search results limit (default: 400) # SEARCH_LIMIT=400 # Allow self-signed TLS certificates (for HTTPS APIs with untrusted certs) # P4PLAN_ALLOW_SELF_SIGNED_CERTS=true ``` The server communicates via stdin/stdout. It is not meant to be run interactively — MCP clients (VS Code, Claude Desktop) spawn it as a child process automatically. ## Authentication The MCP server requires a JWT token provided via the `P4PLAN_API_AUTH_TOKEN` environment variable. The token is validated at startup and forwarded to the P4 Plan GraphQL API on every tool call. No sessions or state are maintained. ### Obtaining a JWT Token Get a JWT token from the P4 Plan GraphQL API using `curl`. You can authenticate with either your **password** or a **Personal Access Token (PAT)**:
Using your password ```bash curl -s -X POST http://localhost:4000/graphql \ -H "Content-Type: application/json" \ -d '{ "query": "mutation Login($loginUserInput: LoginUserInput!) { login(loginUserInput: $loginUserInput) { access_token } }", "variables": { "loginUserInput": { "username": "YOUR_USERNAME", "password": "YOUR_PASSWORD" } } }' ```
Using a Personal Access Token (recommended) A PAT can be used in place of your password in the same login mutation. This avoids exposing your actual password: ```bash curl -s -X POST http://localhost:4000/graphql \ -H "Content-Type: application/json" \ -d '{ "query": "mutation Login($loginUserInput: LoginUserInput!) { login(loginUserInput: $loginUserInput) { access_token } }", "variables": { "loginUserInput": { "username": "YOUR_USERNAME", "password": "YOUR_PERSONAL_ACCESS_TOKEN" } } }' ```
Both methods return the same response: ```json { "data": { "login": { "access_token": "eyJhbGciOiJIUzI1NiIs..." } } } ``` Copy the `access_token` value and use it in your MCP client configuration.
Getting a Personal Access Token 1. Log in to P4 Plan 2. Go to **User Settings** → **Personal Access Tokens** 3. Click **Generate New Token** 4. Set an appropriate expiration date 5. Copy the token — use it in the login mutation above to obtain a JWT
> **Note:** JWT tokens expire. When your token expires, the server will fail to start with an authentication error. Generate a new JWT using the same `curl` command above. ## Available Tools (28 total) ### Projects
list_projects - List all active projects the user is a member of - **Use cases**: Discover project IDs needed by other tools
get_project - Get project configuration including archivedStatus, backlog ID, and QA ID - **Parameters**: `projectId` - **Use cases**: Retrieve section IDs (backlog, QA, planning) for other tools
### Tasks
get_my_tasks - Get tasks assigned to current user (todoList) - **Parameters**: `showCompleted`, `showOnlyNextFourWeeks`, `showHidden`, `showPipelineTasksThatCannotStart` - **Use cases**: View personal work queue across all projects
get_tasks - Get detailed information for one or more items by ID (max 20) - **Parameters**: `taskIds` (array of strings, max 20) - **Use cases**: Full item details, link inspection, batch retrieval of multiple items
search_tasks - Search for items in a project section using P4 Plan Find queries - **Parameters**: `findQuery`, `projectId` - Uses P4 Plan Find query syntax for all searches. Call `read_skill` with `skillName="search-queries"` first to get exact column names, operators, and value formats. For simple name search use `Itemname:Text("text")`. Supports filtering by status, assignee, severity, item type, dates, boolean conditions, and combinations with AND/OR/NOT. - Each project has three sections (Backlog, QA, Planning) with different IDs. - **Use cases**: Item discovery, filtering, reporting
create_item - Create any item type - **Types**: `backlog_task`, `bug`, `scheduled_task`, `sprint`, `release`, `sprint_task` - **Parameters**: `type`, `name`, `projectId`, `parentItemId`, `previousItemId`, and type-specific fields - **Use cases**: Task creation, sprint creation, bug filing
update_item - Update any item (auto-detects type) - **Types**: BacklogTask, Bug, ScheduledTask, Sprint, Release - **Parameters**: `itemId`, plus any updatable fields (name, status, assignedTo, points, etc.) - **Use cases**: Status updates, assignments, estimation, sprint configuration
### Sprint & Release Management
commit_to_sprint - Commit a backlog task or bug to a sprint - **Parameters**: `taskId`, `sprintId` - **Use cases**: Sprint planning, backlog commitment
uncommit_from_sprint - Remove a task from a sprint (return to backlog) - **Parameters**: `taskId` - **Use cases**: Sprint scope adjustment
### Custom Fields & Workflows
get_custom_columns - Get custom column definitions available in a project - **Parameters**: `projectId` - **Use cases**: Discover custom fields before reading/writing values
get_custom_fields - Get custom field values set on a task - **Parameters**: `taskId`, `onlySet` - **Use cases**: Read project-specific metadata
set_custom_field - Set a custom field value on a task - **Parameters**: `taskId`, `columnId`, `value` - **Use cases**: Update project-specific metadata
get_workflows - Get workflow definitions and status IDs for a project - **Parameters**: `projectId` - **Use cases**: Discover workflow statuses for status transitions
### Task Actions
complete_task - Mark a task as completed - **Parameters**: `taskId` - **Use cases**: Quick status update convenience method
start_task - Mark a task as in progress - **Parameters**: `taskId` - **Use cases**: Quick status update convenience method
### Comments & Attachments
get_comments - Get all comments on a task - **Parameters**: `taskId` - **Use cases**: Read discussion history
post_comment - Post a new comment on a task - **Parameters**: `taskId`, `text` - **Use cases**: Add discussion, acceptance criteria, notes
update_comment - Edit an existing comment - **Parameters**: `taskId`, `commentId`, `text` - **Use cases**: Correct or update existing comments
delete_comment - Delete a comment from a task - **Parameters**: `taskId`, `commentId` - **Use cases**: Remove outdated or incorrect comments
get_attachments - Get all attachments on a task - **Parameters**: `taskId` - **Use cases**: List attached files, discover paths for download
download_attachment - Download and return attachment file content - **Parameters**: `taskId`, `path` - Text files returned inline, images as base64 - **Use cases**: Read attached documents, view screenshots
delete_attachment - Delete an attachment from a task - **Parameters**: `taskId`, `path` - **Use cases**: Remove outdated attachments
set_cover_image - Set or unset the cover image for a task - **Parameters**: `taskId`, `imagePath` - **Use cases**: Set visual identity for cards/items
### Links
link_items - Create internal or external links - **Parameters**: `fromItemId`, `toItemId` or `url`, `relation` (blocks, duplicates, relatedTo) - **Use cases**: Dependency tracking, cross-references, external URLs
unlink_items - Remove an internal or external link - **Parameters**: `fromItemId`, `toItemId` or `url` - **Use cases**: Clean up outdated dependencies
### Users
get_current_user - Get current user information - **Use cases**: Identity verification, user context
list_project_users - List users in a project - **Parameters**: `projectId` - **Use cases**: Find user IDs for assignments, sprint member management
### Skills
read_skill - Read a P4 Plan skill document at runtime - **Parameters**: `skillName` - Returns the full Markdown content of the requested skill document. The AI agent **must** call this with `skillName="search-queries"` before composing any `findQuery` for `search_tasks`. - Available skills: `project-navigation`, `search-queries`, `task-management`, `planning`, `backlog-refinement`, `bug-tracking`, `custom-fields`, `gantt-scheduling`, `workflows` - **Use cases**: Learn correct query syntax, discover tool usage patterns, understand domain concepts
## Client Configuration
VS Code (Copilot) — via npx (recommended) Create `.vscode/mcp.json` in your workspace: ```json { "servers": { "p4-plan": { "type": "stdio", "command": "npx", "args": ["-y", "@perforce/p4plan-mcp"], "env": { "P4PLAN_API_AUTH_TOKEN": "YOUR_JWT_TOKEN", "P4PLAN_API_URL": "http://localhost:4000" } } } } ``` > **Note:** The `-y` flag auto-confirms the npm install prompt so the server starts without user interaction.
VS Code (Copilot) — via node (local build) If running from a local clone instead of npm: ```json { "servers": { "p4-plan": { "type": "stdio", "command": "node", "args": ["/path/to/MCP/dist/main.js"], "env": { "P4PLAN_API_AUTH_TOKEN": "YOUR_JWT_TOKEN", "P4PLAN_API_URL": "http://localhost:4000" } } } } ```
VS Code — with secure token prompt For added security, you can use VS Code input prompts to avoid storing tokens in files: ```json { "inputs": [ { "type": "promptString", "id": "p4-plan-jwt", "description": "P4 Plan JWT Token", "password": true } ], "servers": { "p4-plan": { "type": "stdio", "command": "npx", "args": ["-y", "@perforce/p4plan-mcp"], "env": { "P4PLAN_API_AUTH_TOKEN": "${input:p4-plan-jwt}", "P4PLAN_API_URL": "http://localhost:4000" } } } } ```
Claude Desktop Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows): ```json { "mcpServers": { "p4-plan": { "command": "npx", "args": ["-y", "@perforce/p4plan-mcp"], "env": { "P4PLAN_API_AUTH_TOKEN": "YOUR_JWT_TOKEN", "P4PLAN_API_URL": "http://localhost:4000" } } } } ``` Replace `YOUR_JWT_TOKEN` with a token obtained from the login mutation (see [Obtaining a JWT Token](#obtaining-a-jwt-token)).
Claude Code (CLI / VS Code extension) Create `.mcp.json` in your project root: ```json { "mcpServers": { "p4-plan": { "command": "npx", "args": ["-y", "@perforce/p4plan-mcp"], "env": { "P4PLAN_API_AUTH_TOKEN": "YOUR_JWT_TOKEN", "P4PLAN_API_URL": "http://localhost:4000" } } } } ``` For a local build, replace `"command"` and `"args"` with: ```json { "mcpServers": { "p4-plan": { "command": "node", "args": ["/path/to/MCP/dist/main.js"], "env": { "P4PLAN_API_AUTH_TOKEN": "YOUR_JWT_TOKEN", "P4PLAN_API_URL": "http://localhost:4000" } } } } ``` Or add it via the CLI: ```bash claude mcp add p4-plan \ -e P4PLAN_API_AUTH_TOKEN=YOUR_JWT_TOKEN \ -e P4PLAN_API_URL=http://localhost:4000 \ -- npx -y @perforce/p4plan-mcp ``` > **Note:** The server name must come **before** the `-e` flags, otherwise the variadic `-e` parser consumes the name as an env value. **Verify it's running:** type `/mcp` inside Claude Code to check server status. > **Tip:** Place `.mcp.json` in your project root to share the config with your team (tokens excluded). For personal config, add the server to `~/.claude.json` instead.
### Verifying the Connection 1. Ensure the P4 Plan GraphQL API is running 2. Open VS Code with the workspace containing `.vscode/mcp.json` 3. Look for "MCP SERVERS" in the Extensions sidebar — you should see "p4-plan" listed 4. Start a new Copilot chat and ask "What tasks are assigned to me?" ### Environment Variables - `P4PLAN_API_AUTH_TOKEN` - JWT token for authenticating with the P4 Plan GraphQL API - `P4PLAN_API_URL` - P4 Plan GraphQL API URL (default: `http://localhost:4000`) - `P4PLAN_ALLOW_SELF_SIGNED_CERTS` - Set to `true` to accept self-signed or untrusted TLS certificates when connecting to the API over HTTPS (default: `false`) - `LOG_LEVEL` - Logging level: `debug`, `info`, `warn`, `error` (default: `debug`) - `SEARCH_LIMIT` - Maximum number of results returned by `search_tasks` (default: `400`) ## Skills & Resources The server includes **skill files** — domain-specific guides that help AI agents construct correct tool calls. Skills are accessible in two ways: - **`read_skill` tool** — any MCP client can call `read_skill` with a `skillName` to fetch skill content at runtime. This is the primary access method and works with all clients. - **MCP resources** — skills are also registered as MCP resources (e.g., `skill://p4-plan/search-queries`) for clients that support native resource reading. | Skill | Purpose | |--------------------|-------------------------------------------------------------| | project-navigation | Finding projects, items, and getting started | | search-queries | P4 Plan Find query syntax (column names, values, operators) | | task-management | Task CRUD, status, assignments, comments, attachments | | planning | Sprints, releases, commitment, allocations | | backlog-refinement | Backlog items, estimation, prioritization | | bug-tracking | Bugs, severity, QA section | | custom-fields | Custom columns, project-specific metadata | | gantt-scheduling | Scheduled tasks, timeline, dependencies | | workflows | Workflows, pipelines, status state machines | See [`skills/README.md`](skills/README.md) for details on using skills with different AI clients. ## Development
Local Testing with npm link To test the `npx` experience locally without publishing to npm: ```bash # Build and create a global symlink npm run build npm link # Now test exactly as an end user would P4PLAN_API_AUTH_TOKEN=your-jwt-token npx @perforce/p4plan-mcp # Clean up when done npm unlink -g @perforce/p4plan-mcp ``` The symlink persists across rebuilds — just run `npm run build` after code changes.
Adding New Tools 1. Create or edit a tools file in `src/tools/` 2. Define the tool with: - `name`: Unique tool identifier - `description`: What the tool does (shown to AI) - `inputSchema`: JSON Schema for parameters - `handler`: Function that executes the tool 3. Register in `ToolsModule`
Testing ```bash # Unit tests npm run test # E2E tests (MCP protocol compliance via @modelcontextprotocol/sdk) npm run test:e2e ```
### Logging The server uses Winston with two transports: - **Console (stderr):** Only warnings and errors are written to stderr. In VS Code, these appear in the Output panel under the "p4-plan" dropdown. Only `warn` and `error` level messages are shown to keep the output clean. - **File:** Full debug logs are written to `logs/P4PlanMCP_.log`. Use these for detailed troubleshooting. > **Note:** All console output goes to stderr (never stdout) because stdout is the MCP protocol channel. VS Code labels all stderr output as `[warning]` — this is expected behavior and does not indicate a problem. ## Protocol This server uses the **MCP stdio transport** — communication happens over stdin/stdout using JSON-RPC 2.0 messages. The client spawns the server as a child process. - **Transport:** stdio (stdin/stdout) - **Protocol:** JSON-RPC 2.0 - **Authentication:** `P4PLAN_API_AUTH_TOKEN` environment variable (validated at startup) - **SDK:** `@modelcontextprotocol/sdk` with `StdioServerTransport` ## Troubleshooting
Server fails to start 1. **Check P4PLAN_API_AUTH_TOKEN is set:** The server requires a valid JWT token in the `P4PLAN_API_AUTH_TOKEN` environment variable. If missing, it exits immediately with an error. 2. **Check token is valid:** If the token is expired or invalid, the server exits with "Authentication failed". Generate a new JWT using the login mutation. 3. **Check GraphQL API is reachable:** The P4 Plan GraphQL API must be accessible at the configured `P4PLAN_API_URL` (default: `http://localhost:4000`).
Server not detected in VS Code 1. **Verify mcp.json syntax:** Ensure your `.vscode/mcp.json` is valid JSON. Check for trailing commas. 2. **Check the command is available:** If using `npx`, ensure Node.js is in VS Code's PATH. If `npx` is not found, use the absolute path to `node` instead (see [local build config](#vs-code-copilot--via-node-local-build)). 3. **Reload VS Code window:** Press `Cmd+Shift+P` → "Developer: Reload Window" 4. **Start a new chat:** MCP servers are connected when a new chat session starts.
Tools don't work 1. **Check GraphQL server is running:** The P4 Plan GraphQL API must be accessible at the configured URL. 2. **Check server logs:** In VS Code, check the Output panel → select "p4-plan" from the dropdown to see warnings/errors. For full debug logs, check the `logs/` directory. 3. **Verify the JWT hasn't expired:** Generate a new JWT if needed and update your mcp.json config.
## License This project is licensed under the MIT License. See [LICENSE](LICENSE.txt) for details.