Skip to content

Add idempotency keys to prevent duplicate queued prints after a lost response #112

Description

@artiehinz

Problem

submit_job persists a queue entry and returns a generated job ID, but it has no caller-supplied idempotency key. If the queue accepts the job and the MCP response is lost, repeating the same tool call creates a second queued job. The scheduler can then execute the same physical print twice.

This is separate from start_print: its mandatory preflight normally sees that a printer is no longer idle and blocks a blind replay. The queue boundary is still ambiguous because the first job can be durable while its ID never reaches the caller.

Proposed Solution

Add an optional opaque idempotency_key to the MCP submit_job tool and core PrintQueue.submit path:

  • same key plus the same canonical submission (file_name, printer_name, priority, caller scope) returns the original job_id
  • same key with different submission parameters fails closed with a structured conflict
  • persistent queues retain the key across restart with an additive SQLite migration and a uniqueness constraint
  • in-memory queues enforce the same behavior for their process lifetime
  • successful responses indicate whether the job was newly queued or safely replayed

Regression coverage should include sequential duplicate calls, concurrent calls with one key, response-lost simulation, restart recovery, and a conflicting-payload reuse. The guarantee should be described as queue-submission idempotency, not exactly-once physical printing.

I can contribute the focused implementation and tests after agreement on the key scope and persistence shape.

Alternatives Considered

Deduplicating only by file name would block intentional repeat prints. Checking current printer state helps direct starts but cannot distinguish two deliberately queued copies. A caller-supplied operation key preserves that intent.

Context

  • Printer/adapter: adapter-independent queue layer
  • Affects: [x] MCP tools / [ ] CLI / [x] Core library / [ ] Other

This is the ambiguous-success case described by the vendor-neutral Reliability Sidecar Contract v1. A deterministic before/after fixture is also being proposed for MCPJam so clients can test this boundary independently.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions