1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# yaml-language-server: $schema=../../generated/schema-manifest.schema.json
# ─────────────────────────────────────────────────────────────────────────────
# Recipe schema — fully commented example of a custom memstead schema.
#
# How to use:
# 1. Copy this directory into the workspace-level schemas dir
# (schemas_dir in .memstead/workspace.toml, conventionally
# <workspace>/schemas/recipe/)
# 2. Restart the MCP server so the new schema is picked up
# 3. In any mem's .memstead/config.json, set "schema": "recipe@0.1.0"
# 4. Create entities with entity_type "recipe" or "ingredient"
#
# A schema is a package. It bundles every type definition, the full
# relationship vocabulary, and the LLM-facing documentation an agent needs
# to build a graph correctly. This file (schema.yaml) is the manifest.
# Each type gets its own file under types/.
# ─────────────────────────────────────────────────────────────────────────────
# Schema identity — both fields are pinned exactly by consuming mems.
# Never "latest", never a range. Bump version when any shape or semantics
# changes; external mems keep working against the old version.
name: recipe
version: 0.1.0
# description — required. Agents consuming the schema see this first.
# State the domain and what kinds of entities live here.
description: |
Cooking recipes plus the ingredients they reference. Scoped to home-
cooking workflows — not industrial food production, not nutrition
databases.
# when_to_use — required. Teaches the agent WHEN to pick this schema
# versus authoring a different one. Contrast against neighbours.
when_to_use: |
Use for personal recipe collections, cookbook authoring, or meal-
planning knowledge graphs. Pick a different schema when you need
nutrition analysis (per-100g macros, allergens), supplier pricing,
or restaurant-scale production planning — those domains need
vocabulary this schema deliberately does not carry.
# system_message — encouraged. Injected into MCP tool contexts whenever
# an agent works with this schema. Tell the agent the invariants of the
# domain and what "good" looks like.
system_message: |
You are working in a cooking-recipe knowledge graph. Two entity types:
recipe (instructions + ingredient list) and ingredient (pantry item
plus substitution rules).
Always link a recipe to its ingredients via CONTAINS, never via
REFERENCES. When a recipe variant exists, use DERIVED_FROM so the
lineage is explicit. Use SUBSTITUTES_FOR on ingredient pairs only —
not on recipes.
# types — list of type names. Every name must match a file in types/
# (recipe.yaml, ingredient.yaml) and the name field inside that file.
# The loader cross-checks all three.
types:
- recipe
- ingredient
# relationships — the vocabulary of edges any entity in this schema may
# use. An edge using a relationship not listed here is:
# strict mode → rejected at create/update/relate with a suggested match
# open mode → accepted, assigned _default weight, returned as warning
# Pick strict for stable domains, open while you're still discovering
# the vocabulary. Recipe is strict — the vocabulary is small and known.
relationships:
mode: strict
definitions:
- name: CONTAINS
description: |
A recipe contains an ingredient. The ingredient is listed in the
recipe's ingredient table.
when_to_use: |
Always for recipe→ingredient. Not for recipe→recipe composition
(use DERIVED_FROM for variants, REFERENCES for "serve with").
default_weight: 3.0
- name: DERIVED_FROM
description: |
A recipe or ingredient is a variant of, or inspired by, another.
when_to_use: |
Use for recipe variants (vegan bolognese derived from classic
bolognese) or ingredient substitutes that historically replaced
another. Distinct from SUBSTITUTES_FOR which declares
substitutability, not lineage.
default_weight: 2.0
- name: SUBSTITUTES_FOR
description: |
This ingredient can replace the target ingredient in recipes,
possibly with caveats described in the source's body.
when_to_use: |
Only ingredient→ingredient. Read from the caller's side —
"soy_sauce SUBSTITUTES_FOR tamari" means soy sauce can stand
in for tamari.
default_weight: 1.5
- name: PART_OF
description: |
Structural containment — typically sub-recipes that are part of
a larger recipe (pie_crust PART_OF apple_pie).
when_to_use: |
Use when the child would be meaningless or lost without the
parent. If the child could stand alone as a recipe someone
would cook independently, prefer REFERENCES instead.
default_weight: 3.0
- name: REFERENCES
description: |
Soft reference — "serve with", "pairs well with", "see also".
when_to_use: |
Auto-emitted from inline wiki-links; rarely authored by hand.
Use over PART_OF when the target stands alone.
default_weight: 0.5
- name: _default
description: |
Fallback weight for any relationship not otherwise declared.
Required by the engine; do not remove.
default_weight: 1.0
# community — Louvain algorithm parameters for cluster detection on the
# graph. Defaults are sensible for most schemas.
# resolution: higher = more, smaller clusters.
# seed: deterministic partitioning across runs.
community:
resolution: 1.0
seed: 42