When an MCP wrapper hides API fields: set Redmine parent, due date and estimate over REST
Symptom
An agent-facing MCP wrapper for Redmine exposes issue creation and updates, but only a subset of the fields:
subject, description, assigned_to_id, status_id, priority_id, done_ratio
So a task can be created — but it cannot be attached to a parent, given a deadline, or given an estimate. There is no error to debug; the parameters simply do not exist in the tool schema, which is a worse failure mode, because the model happily reports success on a partially created issue.
The underlying API supports all of it
Redmine’s REST API accepts these fields on both create and update. Do the call directly:
# create, with parent, due date and estimate
curl -sS -X POST "https://redmine.example.com/issues.json?key=${API_KEY}" \
-H 'Content-Type: application/json' \
-d '{"issue":{
"project_id": 42,
"subject": "Rotate registry certificates",
"parent_issue_id": 1234,
"due_date": "2026-07-15",
"estimated_hours": 3.5,
"assigned_to_id": 7
}}'
# update an existing issue
curl -sS -X PUT "https://redmine.example.com/issues/1567.json?key=${API_KEY}" \
-H 'Content-Type: application/json' \
-d '{"issue":{"parent_issue_id":1234,"due_date":"2026-07-20","estimated_hours":5}}'
Details that matter in practice:
- The whole payload is wrapped in
{"issue": {...}}. A flat body is accepted with200/204and silently changes nothing — the classic Redmine time sink. - The API key goes in the query string (
?key=…) in this deployment. TheX-Redmine-API-Keyheader is the documented alternative, but if it is rejected in your setup, use?key=; that difference alone can send you chasing a permissions problem. due_dateisYYYY-MM-DD.estimated_hoursis a number, and Redmine accepts fractions.- A successful
PUTreturns204 No Contentwith an empty body. Assert on the status, then re-read the issue if you need proof.
Verify, do not assume
curl -sS "https://redmine.example.com/issues/1567.json?key=${API_KEY}" \
| jq '{id, subject, parent: .issue.parent.id, due: .issue.due_date, est: .issue.estimated_hours}'
Reading the issue back is the only check that catches the flat-payload mistake, because that failure looks exactly like success at the HTTP level.
The general lesson about wrappers
A tool wrapper is a subset of the API by definition, and the subset is usually the fields the author happened to need. Before you build automation on top of one:
- Compare the wrapper’s parameter list against the upstream API reference.
- For anything missing that you actually need, call the API directly rather than working around the gap (creating an issue then “fixing” it manually is not automation).
- File the gap upstream so the next person is not surprised — a wrapper that quietly drops fields is a bug, not a design decision.