Skip to content

postApi collapses "never sent" and "sent, response lost" into the same failure — retries can duplicate a create #163

Description

@aurumflux20

Hi — I read through the create/update tools in mcp-server/src/tools while looking at how MCP servers handle retries on writes, and I think there's a real gap worth a second pair of eyes, not a bug report with a fix attached (I don't know your backend well enough to propose one).

What's provable from the client code:

postApi (lib/request.ts) has no idempotency key anywhere — not in the request body, not as a header. And when the fetch throws (a dropped connection, a timeout), it's caught and returns null exactly the same way a clean 4xx/5xx does:

} catch (error: any) {
  logger.error("Error making Currents POST request:", error.toString());
  return null;
}

Every caller of postApi then reports that as an unqualified failure — create-action.ts:

if (!data) {
  return { content: [{ type: "text" as const, text: "Failed to create action" }] };
}

Same pattern in create-jira-issue.ts and the other create/update tools.

The gap: those two outcomes are not the same thing. "The request never reached you" is safe to retry. "The request reached you, created the thing, and the response was lost on the way back" is not — retrying that creates a second action, or a second Jira issue. From the client's side, both look identical: postApi returns null, the tool says "Failed," and an agent (or a human) doing the reasonable thing and retrying has no way to know which case it's in.

What I can't tell from here, and won't guess: whether /actions or /projects/:id/jira/issues on your side deduplicates a repeat POST — by name + matcher, by some other natural key, or not at all. If they do, this is a non-issue in practice and I've misread the risk. If they don't, a lost response after a real create is the failure mode that produces a duplicate action or a duplicate Jira issue with no error ever surfaced to the caller.

Happy to be told this is already handled server-side — genuinely asking, not asserting. If it isn't, I'd guess the smallest fix is either (a) an idempotency key the caller can set and you dedupe on, or (b) distinguishing "send failed" from "response lost" in postApi so retries are only offered where they're actually safe.

For transparency: I maintain small open-source exactly-once tooling (once, effectfence), so this is a problem I think about outside of this repo too. No pitch attached — flagging it either way.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions