Content Relations in MCP: six new tools

The relation graph tools are now wired in smeldr.dev/mcp. If your app calls app.Relations(store), six tools become available automatically — no new server option needed.

The relation graph tools are now wired in smeldr.dev/mcp. If your app calls app.Relations(store), six tools become available automatically — no new server option needed.

The six tools

ToolRoleWhat it does
assert_relationAuthorCreate an asserted edge. Not idempotent — each call produces a unique edge. Use get_relations first to check for duplicates.
propose_relationAuthorCreate an inferred edge for human/agent review. Stays edge_class="inferred" until promoted.
get_relationsAuthorQuery edges by source, target, or both. Filter by kind and edge_class.
preview_impactEditorSee what would receive AfterRelationCascade before archiving or deleting an item. Read-only.
upsert_relation_kindAdminRegister or update a relation kind. Idempotent on type_name.
list_relation_kindsAuthorInspect the kind registry.

Wiring

smeldr.CreateRelationTables(db)
store, _ := smeldr.NewRelationStore(db)

app := smeldr.New(cfg).Relations(store)
mcpSrv := mcp.New(app) // all six relation tools wired automatically

No mcp.WithRelations() option. The tools appear only when app.RelationStore() != nil.

Why assert_relation is not idempotent

MCPAssertRelation calls insertEdge, which generates a new UUID for every call. The ON CONFLICT clause in the DB only guards the edge's own ID — two calls with identical endpoints create two edge records. This is by design: the relation graph supports multiple parallel edges between the same pair (different confidence scores, different created_by_job values). Call get_relations first if you want to avoid duplicates.

Output format

All tools return snake_case field names (source_type, edge_class, relation_kind, etc.) even though the underlying RelationEdge struct uses db: tags. The MCP layer applies local conversion helpers to keep the output consistent with every other Smeldr tool.