smeldr.dev/mcp v1.24.0 ships three new tools that expose the T23 custom state flow infrastructure to MCP clients.
What shipped
transition_item(type_name, slug, to_state)— moves a dynamic content item to a new state. The transition is validated against the registered flow; if the transition is not permitted, the tool returns -32001 with the specific rejection reason. Requires Editor role.
get_valid_transitions(type_name, slug)— returns the item's current state and the list of states it can legally transition to. Uses the type's registered custom flow, falling back to the default flow (draft → scheduled/published/archived) when no custom flow is registered. Requires Author role.
list_items_by_state(type_name, state)— returns all items of a dynamic content type in the given state. Useful for building inboxes, dashboards, or queues from the MCP interface. Requires Author role.
All three tools are gated on the app's database being configured (App.Config().DB != nil). No new server option is required.
How to use
// Register a custom flow for your type at startup:
app.RegisterFlow(smeldr.StateFlow{
Name: "review-flow",
TypeName: "proposal",
States: []smeldr.State{
{Name: "draft", IsInitial: true},
{Name: "in-review"},
{Name: "approved", IsTerminal: true},
{Name: "rejected", IsTerminal: true},
},
Transitions: []smeldr.Transition{
{From: "draft", To: "in-review"},
{From: "in-review", To: "approved"},
{From: "in-review", To: "rejected"},
// RequiredRole is stored in smeldr_transitions but not yet enforced —
// per-transition role gating is planned for a later T23 step.
},
})
Then from the MCP interface:
// Check what's possible next:
get_valid_transitions("proposal", "my-proposal-slug")
// → {current_state: "draft", valid_transitions: ["in-review"]}
// Move it forward:
transition_item("proposal", "my-proposal-slug", "in-review")
// → {slug: "my-proposal-slug", status: "in-review"}
// Query the review queue:
list_items_by_state("proposal", "in-review")
// → {type_name: "proposal", state: "in-review", items: [...], count: N}
The fix that comes with it
errorFor in the MCP server now correctly maps smeldr.ErrConflict to JSON-RPC -32001 with the specific error message. Previously, a transition conflict from set_content_status would have produced a generic -32603 "internal error". This is now fixed as a side effect of the state tools landing.