---
phase: 01-foundation-types-and-store-contract
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - crates/pmcp-tasks/Cargo.toml
  - crates/pmcp-tasks/src/lib.rs
  - crates/pmcp-tasks/src/types/mod.rs
  - crates/pmcp-tasks/src/types/task.rs
  - crates/pmcp-tasks/src/types/params.rs
  - crates/pmcp-tasks/src/types/capabilities.rs
  - crates/pmcp-tasks/src/types/notification.rs
  - crates/pmcp-tasks/src/types/execution.rs
  - crates/pmcp-tasks/src/error.rs
  - crates/pmcp-tasks/src/constants.rs
  - Cargo.toml
autonomous: true
requirements:
  - TYPE-01
  - TYPE-02
  - TYPE-03
  - TYPE-04
  - TYPE-05
  - TYPE-06
  - TYPE-07
  - TYPE-08
  - TYPE-09
  - TYPE-10

must_haves:
  truths:
    - "pmcp-tasks crate compiles as a workspace member with cargo check --package pmcp-tasks"
    - "TaskStatus enum serializes to snake_case matching spec (working, input_required, completed, failed, cancelled)"
    - "Task wire type serializes ttl as null (not omitted) when None, and omits pollInterval when None"
    - "CreateTaskResult wraps Task in a task field; GetTaskResult and CancelTaskResult are flat Task aliases"
    - "State machine rejects invalid transitions (terminal states reject all, self-transitions rejected)"
    - "All request param types, capability types, notification type, and execution types serialize to match spec schema"
    - "TaskError enum has rich context variants with JSON-RPC error code mapping"
  artifacts:
    - path: "crates/pmcp-tasks/Cargo.toml"
      provides: "Crate manifest with all dependencies"
      contains: "pmcp-tasks"
    - path: "crates/pmcp-tasks/src/lib.rs"
      provides: "Crate root with module declarations and re-exports"
      contains: "pub mod types"
    - path: "crates/pmcp-tasks/src/types/task.rs"
      provides: "Task, TaskStatus, CreateTaskResult, GetTaskResult, CancelTaskResult wire types"
      contains: "pub struct Task"
    - path: "crates/pmcp-tasks/src/types/params.rs"
      provides: "TaskGetParams, TaskResultParams, TaskListParams, TaskCancelParams, TaskParams"
      contains: "pub struct TaskGetParams"
    - path: "crates/pmcp-tasks/src/types/capabilities.rs"
      provides: "ServerTaskCapabilities, ClientTaskCapabilities with full() and tools_only() constructors"
      contains: "pub fn full()"
    - path: "crates/pmcp-tasks/src/types/notification.rs"
      provides: "TaskStatusNotification type"
      contains: "pub struct TaskStatusNotification"
    - path: "crates/pmcp-tasks/src/types/execution.rs"
      provides: "TaskSupport enum, ToolExecution metadata"
      contains: "pub enum TaskSupport"
    - path: "crates/pmcp-tasks/src/error.rs"
      provides: "TaskError enum with rich context and error_code() method"
      contains: "pub enum TaskError"
    - path: "crates/pmcp-tasks/src/constants.rs"
      provides: "Meta key constants and method name constants"
      contains: "RELATED_TASK_META_KEY"
  key_links:
    - from: "crates/pmcp-tasks/src/lib.rs"
      to: "crates/pmcp-tasks/src/types/mod.rs"
      via: "pub mod types"
      pattern: "pub mod types"
    - from: "crates/pmcp-tasks/src/types/task.rs"
      to: "crates/pmcp-tasks/src/error.rs"
      via: "TaskStatus used in TaskError::InvalidTransition"
      pattern: "use crate::error::TaskError"
    - from: "Cargo.toml"
      to: "crates/pmcp-tasks/Cargo.toml"
      via: "workspace members list"
      pattern: "crates/pmcp-tasks"
---

<objective>
Create the pmcp-tasks crate as a workspace member with all spec-compliant wire types, state machine logic, error types, and constants for the MCP 2025-11-25 Tasks specification.

Purpose: This is the foundation everything else builds on. Every other phase depends on these types compiling and serializing correctly. Wire types must match the MCP spec byte-for-byte.

Output: A compiling pmcp-tasks crate with all protocol wire types, TaskStatus state machine, rich error enum, and spec constants. No domain types or store trait yet (Plan 02).
</objective>

<execution_context>
@./.claude/get-shit-done/workflows/execute-plan.md
@./.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/01-foundation-types-and-store-contract/01-CONTEXT.md
@.planning/phases/01-foundation-types-and-store-contract/01-RESEARCH.md
@Cargo.toml
@src/types/mod.rs
@src/types/protocol.rs
@src/types/capabilities.rs
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create pmcp-tasks crate scaffold and core wire types</name>
  <files>
    Cargo.toml (workspace root - add member)
    crates/pmcp-tasks/Cargo.toml
    crates/pmcp-tasks/src/lib.rs
    crates/pmcp-tasks/src/types/mod.rs
    crates/pmcp-tasks/src/types/task.rs
  </files>
  <action>
1. Add `"crates/pmcp-tasks"` to the workspace members list in the root Cargo.toml.

2. Create `crates/pmcp-tasks/Cargo.toml` following the exact template from RESEARCH.md:
   - name: "pmcp-tasks", version "0.1.0", edition "2021", rust-version "1.82.0"
   - Dependencies: pmcp (path = "../.."), serde 1.0 (derive), serde_json 1.0 (preserve_order), async-trait 0.1, thiserror 2.0, uuid 1 (v4, serde), chrono 0.4 (serde), tokio 1 (sync, time), tracing 0.1, parking_lot 0.12
   - Dev-dependencies: tokio 1 (full), serde_json 1.0 (preserve_order), pretty_assertions 1, proptest 1

3. Create `crates/pmcp-tasks/src/lib.rs`:
   - Declare modules: `pub mod types`, `pub mod error`, `pub mod constants`
   - Stub `pub mod domain` and `pub mod store` (empty, for Plan 02)
   - Add crate-level doc comment explaining this is MCP Tasks support (experimental)
   - Re-export key types at crate root matching pmcp's export pattern: `pub use types::*`, `pub use error::TaskError`, `pub use constants::*`

4. Create `crates/pmcp-tasks/src/types/mod.rs`:
   - Declare and re-export all sub-modules: task, params, capabilities, notification, execution
   - `pub use task::*`, `pub use params::*`, etc.

5. Create `crates/pmcp-tasks/src/types/task.rs` with spec-compliant wire types:

   **TaskStatus enum** (TYPE-02):
   - 5 variants: Working, InputRequired, Completed, Failed, Cancelled
   - `#[serde(rename_all = "snake_case")]`
   - Derive: Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize
   - Display impl that outputs the snake_case form

   **State machine methods on TaskStatus** (TYPE-03):
   - `is_terminal(&self) -> bool`: true for Completed, Failed, Cancelled
   - `can_transition_to(&self, next: &TaskStatus) -> bool`: Working -> {InputRequired, Completed, Failed, Cancelled}, InputRequired -> {Working, Completed, Failed, Cancelled}, terminal states -> false. Self-transitions REJECTED (spec does not list them).
   - `validate_transition(&self, next: &TaskStatus) -> Result<(), TaskError>`: calls can_transition_to, returns Ok or InvalidTransition error with rich context

   **Task struct** (TYPE-01):
   - Fields: task_id (String), status (TaskStatus), status_message (Option with skip_serializing_if), created_at (String - ISO 8601), last_updated_at (String - ISO 8601), ttl (Option of u64 - NO skip_serializing_if, serializes as null), poll_interval (Option of u64 with skip_serializing_if)
   - `#[serde(rename_all = "camelCase")]`
   - Derive: Debug, Clone, Serialize, Deserialize
   - Add `#[serde(skip_serializing_if = "Option::is_none")]` to _meta field
   - Add `_meta: Option<serde_json::Map<String, Value>>` field with skip_serializing_if for GetTaskResult responses (per RESEARCH.md open question 1 recommendation)
   - Use `#[allow(clippy::pub_underscore_fields)]` on `_meta`

   **CreateTaskResult struct** (TYPE-01):
   - Fields: task (Task), _meta (Option of Map with skip_serializing_if)
   - `#[serde(rename_all = "camelCase")]`

   **GetTaskResult type alias** = Task (flat, no wrapper)
   **CancelTaskResult type alias** = Task (flat, no wrapper)

   **Related-task metadata helper** (TYPE-04):
   - Function `related_task_meta(task_id: &str) -> serde_json::Map<String, Value>` that produces `{"io.modelcontextprotocol/related-task": {"taskId": "..."}}` map entry
   - This is used to populate `_meta` on tasks/result responses
  </action>
  <verify>
    Run `cargo check --package pmcp-tasks` from workspace root. Must compile with zero errors.
    Run `cargo clippy --package pmcp-tasks -- -D warnings` for zero clippy warnings.
  </verify>
  <done>
    pmcp-tasks crate exists as workspace member. Task, TaskStatus, CreateTaskResult, GetTaskResult, CancelTaskResult wire types compile. TaskStatus state machine methods (is_terminal, can_transition_to, validate_transition) are implemented. Related-task metadata helper function exists.
  </done>
</task>

<task type="auto">
  <name>Task 2: Create remaining wire types, error enum, and constants</name>
  <files>
    crates/pmcp-tasks/src/types/params.rs
    crates/pmcp-tasks/src/types/capabilities.rs
    crates/pmcp-tasks/src/types/notification.rs
    crates/pmcp-tasks/src/types/execution.rs
    crates/pmcp-tasks/src/error.rs
    crates/pmcp-tasks/src/constants.rs
  </files>
  <action>
1. Create `crates/pmcp-tasks/src/types/params.rs` (TYPE-06):
   - **TaskParams** (for tools/call task field): task_id (Option - omit for new task creation), ttl (Option of u64), poll_interval (Option of u64). All with skip_serializing_if. Rename camelCase.
   - **TaskGetParams**: task_id (String). Rename camelCase.
   - **TaskResultParams**: task_id (String). Rename camelCase.
   - **TaskListParams**: cursor (Option of String with skip_serializing_if). Rename camelCase.
   - **TaskCancelParams**: task_id (String). Rename camelCase.
   - All derive Debug, Clone, Serialize, Deserialize.

2. Create `crates/pmcp-tasks/src/types/capabilities.rs` (TYPE-05):
   - **ServerTaskCapabilities**: list (Option of EmptyObject), cancel (Option of EmptyObject), requests (Option of ServerTaskRequests). All with skip_serializing_if. Rename camelCase.
   - **ServerTaskRequests**: tools (Option of ToolsTaskRequests) with skip_serializing_if. Rename camelCase.
   - **ToolsTaskRequests**: call (Option of EmptyObject) with skip_serializing_if. Rename camelCase.
   - **EmptyObject**: empty struct that serializes to `{}`. Derive Serialize, Deserialize.
   - **ClientTaskCapabilities**: supported (bool). Rename camelCase. (Spec: client declares it supports tasks.)
   - Convenience constructors:
     - `ServerTaskCapabilities::full()` -> list + cancel + tools.call all enabled
     - `ServerTaskCapabilities::tools_only()` -> only tools.call, no list/cancel

3. Create `crates/pmcp-tasks/src/types/notification.rs` (TYPE-07):
   - **TaskStatusNotification**: Struct for `notifications/tasks/status` params.
   - Fields: task_id (String), status (TaskStatus), status_message (Option with skip), created_at (String), last_updated_at (String), ttl (Option of u64 - required nullable, NO skip), poll_interval (Option of u64 with skip). Rename camelCase.

4. Create `crates/pmcp-tasks/src/types/execution.rs` (TYPE-08):
   - **TaskSupport enum**: Forbidden, Optional, Required. `#[serde(rename_all = "lowercase")]`. Default impl returns Forbidden.
   - **ToolExecution struct**: task_support (TaskSupport with default). Rename camelCase. This goes into ToolInfo's execution field.

5. Create `crates/pmcp-tasks/src/error.rs` (TYPE-09):
   - **TaskError enum** with thiserror::Error derive:
     - `InvalidTransition { task_id: String, from: TaskStatus, to: TaskStatus, suggested_action: Option<String> }`
     - `NotFound { task_id: String }`
     - `Expired { task_id: String, expired_at: Option<String> }`
     - `NotReady { task_id: String, current_status: TaskStatus }`
     - `OwnerMismatch { task_id: String }`
     - `ResourceExhausted { suggested_action: Option<String> }`
     - `VariableSizeExceeded { limit_bytes: usize, actual_bytes: usize }`
     - `StoreError(String)`
   - `error_code(&self) -> i32` method mapping to JSON-RPC codes per spec: -32602 for InvalidTransition/NotFound/Expired/NotReady/OwnerMismatch/VariableSizeExceeded, -32603 for ResourceExhausted/StoreError
   - Display messages include relevant context fields (task_id, status, etc.)

6. Create `crates/pmcp-tasks/src/constants.rs` (TYPE-10, TYPE-04):
   - `pub const RELATED_TASK_META_KEY: &str = "io.modelcontextprotocol/related-task";`
   - `pub const MODEL_IMMEDIATE_RESPONSE_META_KEY: &str = "io.modelcontextprotocol/model-immediate-response";`
   - Task method name constants: `pub const METHOD_TASKS_GET: &str = "tasks/get";`, `METHOD_TASKS_RESULT`, `METHOD_TASKS_LIST`, `METHOD_TASKS_CANCEL`, `METHOD_TASKS_STATUS_NOTIFICATION: &str = "notifications/tasks/status";`
  </action>
  <verify>
    Run `cargo check --package pmcp-tasks` -- zero errors.
    Run `cargo clippy --package pmcp-tasks -- -D warnings` -- zero warnings.
    Run `cargo doc --package pmcp-tasks --no-deps` -- documentation builds without warnings.
  </verify>
  <done>
    All wire types (params, capabilities, notification, execution) compile with correct serde attributes. TaskError enum exists with all variants, rich context, and error_code() method. Constants for meta keys and method names are defined. Full crate compiles and passes clippy.
  </done>
</task>

</tasks>

<verification>
1. `cargo check --package pmcp-tasks` compiles successfully
2. `cargo clippy --package pmcp-tasks -- -D warnings` passes with zero warnings
3. `cargo doc --package pmcp-tasks --no-deps` builds documentation
4. All public types have doc comments
5. serde attributes match spec requirements: camelCase rename, skip_serializing_if on optional fields, NO skip on ttl
</verification>

<success_criteria>
- pmcp-tasks is a workspace member that compiles
- All 10 TYPE requirements have corresponding types with correct serde serialization attributes
- TaskStatus state machine validates transitions correctly
- TaskError provides rich context for all error scenarios
- Zero clippy warnings, zero compilation errors
</success_criteria>

<output>
After completion, create `.planning/phases/01-foundation-types-and-store-contract/01-01-SUMMARY.md`
</output>
