Skip to content

strategy — full tool reference

BM Canvas write path: business units, canvas blocks, relationships, assumptions, sources, snapshots, competitors, personas.

Endpointhttps://strategy.mcp.devfellowship.com/mcp
Packagepackages/dfl-mcp-strategy
Tools24
ToolDescription
list_business_unitsList strategy.business_units rows. By default excludes archived units — pass include_archived: true to see everything. business_unit_id is a self-referential parent link (a BU can belong to a parent BU, e.g. a product line under a company).
get_business_unitFetch one strategy.business_units row by id.
create_business_unitCreate a strategy.business_units row — the root entity every BM Canvas block (value propositions, customer segments, channels, etc.) hangs off of via business_unit_id. golden_circle_why/how/what are free-form jsonb (Simon Sinek framing).
update_business_unitPartially update a strategy.business_units row by id. Only the fields provided are changed.
archive_business_unitSet strategy.business_units.is_archived on a row (default true — archive). Pass archived: false to unarchive. This is a soft flag, not a delete — child block rows (value propositions, segments, etc.) are left untouched.
set_canvas_blockUpsert a row into one of the 9 Business Model Canvas block tables for a business_unit: value_proposition, customer_segment, customer_relationship, channel, revenue_stream, key_partner, key_activity, key_resource, expense_category. Pass “id” to update an existing row (scoped to business_unit_id), or omit it to insert a new row. “fields” is passed through to the underlying table as-is — column names must match the table (e.g. value_proposition needs “name”; customer_relationship needs “name” + “type”; key_partner accepts free-text “name” + “description”; key_resource (“resources” table) accepts free-text “name” + “description” — both added 2026-07-15 via dfl-schema #683 to fix the “Unnamed Partner” display bug and let resources be modeled without a fixed entity/resource_category catalog FK). All 9 blocks are agent-writable end to end as of 2026-07-15 (RLS INSERT/UPDATE policies land on every block table).
add_business_unit_relationshipCreate a typed edge in strategy.business_unit_relationships between two business units (from_bu → to_bu), e.g. “the Fellowship BU funds the Studio BU” or “Itera commercializes Revera’s methodology”. mechanics is free-form jsonb describing how the relationship actually works (revenue split, staffing %, etc).
add_assumptionCreate a strategy.assumptions row for a business_unit — a testable belief underpinning the strategy (desirability/viability/feasibility), its status, evidence gathered so far, and the risk if it turns out to be wrong.
add_sourceCreate a strategy.sources row — a provenance record for where a strategy artifact came from (a Fireflies call, a plans.devfellowship.com plan, a Company Brain node, or a manual note). Use with link_artifact_source to attach it to the artifact it backs.
link_artifact_sourceCreate a strategy.artifact_sources row linking any strategy artifact row (by table name + id, e.g. artifact_table: “assumptions”) to a strategy.sources row created via add_source. This is how provenance (“this assumption came from the 2026-07-10 strategic call”) gets recorded without a dedicated source_id column on every block table.
create_canvas_snapshotAssemble a business_unit’s full Business Model Canvas (business_unit row + all 9 block tables + active relationships/assumptions) into a strategy.canvas_snapshots row, status=draft. snapshot_version auto-increments per business_unit. IMPORTANT: agents can only ever create draft snapshots — strategy.canvas_snapshots UPDATE (ratifying draft → ratified) is DB-gated to iam.is_global_admin() and there is deliberately no ratify tool here. Ratification happens in the BM Canvas app by a human global admin.
add_competitorCreate a strategy.competitors row for a business_unit. Competitors point at a strategy.entities row (the actual company name/website/logo lives on entities, not on competitors itself, so entities can be shared across the competitor/key_partner graph). Pass entity_id to link an existing entity, or entity_name (+ optional entity_description/ entity_website) to create a new one inline.
add_personaCreate a strategy.personas row under a customer_segment — a named buyer/user persona (occupation, buying role in the deal, ICP priority tier).
list_personasList strategy.personas rows. Pass exactly one filter: customer_segment_id (direct — personas belonging to one segment) or business_unit_id (joins through customer_segments to return every persona across all of that BU’s segments).
update_personaPartially update a strategy.personas row by id. Only the fields provided are changed.
delete_personaHard-delete a strategy.personas row by id. strategy.personas has no soft-delete/archive flag today, so this is a permanent row delete — use for cleaning up misparented/duplicate personas (e.g. seeded under the wrong customer_segment).
list_customer_segmentsList strategy.customer_segments rows for a business_unit_id.
update_customer_segmentPartially update a strategy.customer_segments row by id. Only the fields provided are changed.
delete_customer_segmentHard-delete a strategy.customer_segments row by id. CAUTION: as of 2026-07-15, strategy.customer_segments has INSERT/UPDATE/SELECT RLS policies for authenticated members but NO DELETE policy — this call will likely fail with a Postgres RLS error (0 rows deleted, or 42501) until a dfl-schema migration adds one. If it fails for that reason, do not silently swallow it — surface it and flag a dfl-schema follow-up. Also note personas FK-reference customer_segment_id with no documented ON DELETE behavior — delete/re-parent child personas first.
list_writing_patternsList strategy.writing_patterns rows for a business_unit. Each row is one “voice slot” — a reusable description of how a given surface should sound (e.g. slot “book”, “youtube”). The free-form pattern jsonb holds the actual voice definition. Optionally filter by slot to fetch a single voice. Read this BEFORE writing a new slot so the new row follows the same pattern shape as the existing ones.
upsert_writing_patternCreate or update one strategy.writing_patterns row (a “voice slot”) for a business_unit. Resolution order: pass id to update that exact row; else pass slot and the tool updates the existing row with that slot in the business_unit, or inserts a new one if none exists. pattern is free-form jsonb — call list_writing_patterns first and mirror the shape the other slots already use, so a consumer reading every slot does not break. On update, pattern REPLACES the stored object (no deep merge) — send the whole thing.
list_keywordsList strategy.keywords rows (the SEO keyword corpus) of one business unit, ordered by text. count is the exact number of keywords the BU holds, independent of limit — compare it before and after a test run to prove the run left no rows behind. Reads run under the caller’s user-JWT (RLS applies).
delete_keywordsHard-delete strategy.keywords rows by id, scoped to one business unit. Their keyword_metrics and keyword_relations rows cascade; child keywords keep their row and lose parent_keyword_id (SET NULL). The generic repair and test-cleanup path for the SEO corpus (e2e specs delete what they create). An id that is absent, belongs to another BU, or is hidden by RLS is not deleted and is listed in not_deleted_ids.
upsert_keywordsCreate strategy.keywords rows (the SEO corpus) in one business unit, with an optional keyword_metrics snapshot and an optional parent (parent_keyword_id, the mind-map tree). A keyword is identified by (business_unit_id, slug); the slug is the campaigns app slugifier of text. A keyword that already exists is reused and NOT changed, unless update_existing is true — then its category / cluster fields are overwritten and a new metrics snapshot is added. Group keywords into a cluster by giving them the same cluster_name + cluster_color. Writes run under the caller user-JWT (RLS applies). Not a transaction: a failure after the first insert leaves the earlier rows; re-run the same batch to finish, it is idempotent.

List Business Units

List strategy.business_units rows. By default excludes archived units — pass include_archived: true to see everything. business_unit_id is a self-referential parent link (a BU can belong to a parent BU, e.g. a product line under a company).

ParameterTypeRequiredDescription
include_archivedbooleannoInclude is_archived=true rows Default: false.
parent_business_unit_idstringnoFilter to children of this business_unit_id (the parent-link column, also named business_unit_id on the row)
limitnumbernoDefault: 50.

Get Business Unit

Fetch one strategy.business_units row by id.

ParameterTypeRequiredDescription
idstringyesstrategy.business_units.id

Create Business Unit

Create a strategy.business_units row — the root entity every BM Canvas block (value propositions, customer segments, channels, etc.) hangs off of via business_unit_id. golden_circle_why/how/what are free-form jsonb (Simon Sinek framing).

ParameterTypeRequiredDescription
namestringyesBusiness unit name
descriptionstringno—
sectorstringno—
tagsstringno—
colorstringnoHex or CSS color used by the canvas UI
logo_urlstringno—
business_unit_idstringnoParent business_unit_id, if this BU is nested under another (e.g. a product line under a company)
golden_circle_whyobjectno—
golden_circle_howobjectno—
golden_circle_whatobjectno—

Update Business Unit

Partially update a strategy.business_units row by id. Only the fields provided are changed.

ParameterTypeRequiredDescription
idstringyesstrategy.business_units.id
namestringno—
descriptionstringno—
sectorstringno—
tagsstringno—
colorstringno—
logo_urlstringno—
business_unit_idstringnoParent business_unit_id
golden_circle_whyobjectno—
golden_circle_howobjectno—
golden_circle_whatobjectno—

Archive Business Unit

Set strategy.business_units.is_archived on a row (default true — archive). Pass archived: false to unarchive. This is a soft flag, not a delete — child block rows (value propositions, segments, etc.) are left untouched.

ParameterTypeRequiredDescription
idstringyesstrategy.business_units.id
archivedbooleannoDefault: true.

Set Canvas Block

Upsert a row into one of the 9 Business Model Canvas block tables for a business_unit: value_proposition, customer_segment, customer_relationship, channel, revenue_stream, key_partner, key_activity, key_resource, expense_category. Pass “id” to update an existing row (scoped to business_unit_id), or omit it to insert a new row. “fields” is passed through to the underlying table as-is — column names must match the table (e.g. value_proposition needs “name”; customer_relationship needs “name” + “type”; key_partner accepts free-text “name” + “description”; key_resource (“resources” table) accepts free-text “name” + “description” — both added 2026-07-15 via dfl-schema #683 to fix the “Unnamed Partner” display bug and let resources be modeled without a fixed entity/resource_category catalog FK). All 9 blocks are agent-writable end to end as of 2026-07-15 (RLS INSERT/UPDATE policies land on every block table).

ParameterTypeRequiredDescription
blockenumyesOne of: value_proposition, customer_segment, customer_relationship, channel, revenue_stream, key_partner, key_activity, key_resource, expense_category.
business_unit_idstringyesstrategy.business_units.id this block row belongs to
idstringnoRow id to update. Omit to insert a new row.
fieldsobjectnoColumn name → value for the target table Default: {}.

Add Business Unit Relationship

Create a typed edge in strategy.business_unit_relationships between two business units (from_bu → to_bu), e.g. “the Fellowship BU funds the Studio BU” or “Itera commercializes Revera’s methodology”. mechanics is free-form jsonb describing how the relationship actually works (revenue split, staffing %, etc).

ParameterTypeRequiredDescription
from_bustringyesstrategy.business_units.id — the source of the relationship
to_bustringyesstrategy.business_units.id — the target of the relationship (must differ from from_bu)
relationship_typeenumyesOne of: funds, supplies_talent, commercializes, provides_methodology, shares_brand, incubates.
mechanicsobjectnoDefault: {}.
descriptionstringno—
is_activebooleannoDefault: true.
started_atstringnoISO date the relationship started

Add Assumption

Create a strategy.assumptions row for a business_unit — a testable belief underpinning the strategy (desirability/viability/feasibility), its status, evidence gathered so far, and the risk if it turns out to be wrong.

ParameterTypeRequiredDescription
business_unit_idstringyesstrategy.business_units.id
statementstringyesThe assumption, stated as a testable claim
categoryenumyesOne of: desirability, viability, feasibility.
statusenumnoOne of: untested, testing, validated, invalidated. Default: "untested".
evidenceobject[]noDefault: [].
risk_if_wrongstringno—

Add Source

Create a strategy.sources row — a provenance record for where a strategy artifact came from (a Fireflies call, a plans.devfellowship.com plan, a Company Brain node, or a manual note). Use with link_artifact_source to attach it to the artifact it backs.

ParameterTypeRequiredDescription
source_typeenumyesOne of: fireflies_call, plans_app, company_brain, manual.
external_refstringnoExternal id/URL for the source (call id, plan slug, brain node id, …)
occurred_atstringnoISO timestamp the source event occurred
summarystringno—

Link Artifact Source

Create a strategy.artifact_sources row linking any strategy artifact row (by table name + id, e.g. artifact_table: “assumptions”) to a strategy.sources row created via add_source. This is how provenance (“this assumption came from the 2026-07-10 strategic call”) gets recorded without a dedicated source_id column on every block table.

ParameterTypeRequiredDescription
artifact_tablestringyesThe strategy.* table the artifact lives in, e.g. “assumptions”, “value_propositions”
artifact_idstringyesThe artifact row id in artifact_table
source_idstringyesstrategy.sources.id (from add_source)
extraction_notestringnoHow/why this source backs this artifact

Create Canvas Snapshot

Assemble a business_unit’s full Business Model Canvas (business_unit row + all 9 block tables + active relationships/assumptions) into a strategy.canvas_snapshots row, status=draft. snapshot_version auto-increments per business_unit. IMPORTANT: agents can only ever create draft snapshots — strategy.canvas_snapshots UPDATE (ratifying draft → ratified) is DB-gated to iam.is_global_admin() and there is deliberately no ratify tool here. Ratification happens in the BM Canvas app by a human global admin.

ParameterTypeRequiredDescription
business_unit_idstringyesstrategy.business_units.id to snapshot
triggerenumnoOne of: manual, weekly_synthesis, pre_deck_export, post_strategic_call. Default: "manual".
diff_summary_mdstringnoOptional human-readable summary of what changed since the last snapshot

Add Competitor

Create a strategy.competitors row for a business_unit. Competitors point at a strategy.entities row (the actual company name/website/logo lives on entities, not on competitors itself, so entities can be shared across the competitor/key_partner graph). Pass entity_id to link an existing entity, or entity_name (+ optional entity_description/ entity_website) to create a new one inline.

ParameterTypeRequiredDescription
business_unit_idstringyesstrategy.business_units.id this competitor is tracked against
entity_idstringnoExisting strategy.entities.id — omit if creating a new entity inline
entity_namestringnoName for a new entity (required if entity_id is omitted)
entity_descriptionstringno—
entity_websitestringno—
market_sharenumberno—
threat_levelenumnoOne of: high, medium, low.
visiblebooleannoDefault: true.

Add Persona

Create a strategy.personas row under a customer_segment — a named buyer/user persona (occupation, buying role in the deal, ICP priority tier).

ParameterTypeRequiredDescription
customer_segment_idstringyesstrategy.customer_segments.id this persona belongs to
namestringyes—
occupationstringno—
descriptionstringno—
imagestringnoImage URL
sort_ordernumbernoDefault: 0.
buying_roleenumnoOne of: economic_buyer, champion, user, influencer, gatekeeper, blocker.
icp_priorityenumnoOne of: primary, secondary, tertiary.

List Personas

List strategy.personas rows. Pass exactly one filter: customer_segment_id (direct — personas belonging to one segment) or business_unit_id (joins through customer_segments to return every persona across all of that BU’s segments).

ParameterTypeRequiredDescription
customer_segment_idstringnostrategy.customer_segments.id — direct filter
business_unit_idstringnostrategy.business_units.id — filters personas across every segment of this BU (joins via customer_segments)
limitnumbernoDefault: 50.

Update Persona

Partially update a strategy.personas row by id. Only the fields provided are changed.

ParameterTypeRequiredDescription
idstringyesstrategy.personas.id
customer_segment_idstringnoRe-parent the persona to a different customer_segment_id
namestringno—
occupationstringno—
descriptionstringno—
imagestringnoImage URL
sort_ordernumberno—
buying_roleenumnoOne of: economic_buyer, champion, user, influencer, gatekeeper, blocker.
icp_priorityenumnoOne of: primary, secondary, tertiary.

Delete Persona

Hard-delete a strategy.personas row by id. strategy.personas has no soft-delete/archive flag today, so this is a permanent row delete — use for cleaning up misparented/duplicate personas (e.g. seeded under the wrong customer_segment).

ParameterTypeRequiredDescription
idstringyesstrategy.personas.id

List Customer Segments

List strategy.customer_segments rows for a business_unit_id.

ParameterTypeRequiredDescription
business_unit_idstringyesstrategy.business_units.id this segment belongs to
limitnumbernoDefault: 50.

Update Customer Segment

Partially update a strategy.customer_segments row by id. Only the fields provided are changed.

ParameterTypeRequiredDescription
idstringyesstrategy.customer_segments.id
business_unit_idstringnoRe-parent the segment to a different business_unit_id
namestringno—
descriptionstringno—
demographicsstringno—
needsobjectno—
pain_pointsobjectno—
age_rangestringnoOne of strategy.age_range_enum
persona_namestringnoLegacy inline-persona field (superseded by strategy.personas rows — prefer add_persona/update_persona)
persona_descriptionstringno—
persona_occupationstringno—
persona_imagestringno—

Delete Customer Segment

Hard-delete a strategy.customer_segments row by id. CAUTION: as of 2026-07-15, strategy.customer_segments has INSERT/UPDATE/SELECT RLS policies for authenticated members but NO DELETE policy — this call will likely fail with a Postgres RLS error (0 rows deleted, or 42501) until a dfl-schema migration adds one. If it fails for that reason, do not silently swallow it — surface it and flag a dfl-schema follow-up. Also note personas FK-reference customer_segment_id with no documented ON DELETE behavior — delete/re-parent child personas first.

ParameterTypeRequiredDescription
idstringyesstrategy.customer_segments.id

List Writing Patterns

List strategy.writing_patterns rows for a business_unit. Each row is one “voice slot” — a reusable description of how a given surface should sound (e.g. slot “book”, “youtube”). The free-form pattern jsonb holds the actual voice definition. Optionally filter by slot to fetch a single voice. Read this BEFORE writing a new slot so the new row follows the same pattern shape as the existing ones.

ParameterTypeRequiredDescription
business_unit_idstringyesstrategy.business_units.id whose writing patterns to list
slotstringnoFilter to a single slot (e.g. “book”, “youtube”, “pedagogy_fala”)
limitnumbernoDefault: 50.

Upsert Writing Pattern

Create or update one strategy.writing_patterns row (a “voice slot”) for a business_unit. Resolution order: pass id to update that exact row; else pass slot and the tool updates the existing row with that slot in the business_unit, or inserts a new one if none exists. pattern is free-form jsonb — call list_writing_patterns first and mirror the shape the other slots already use, so a consumer reading every slot does not break. On update, pattern REPLACES the stored object (no deep merge) — send the whole thing.

ParameterTypeRequiredDescription
business_unit_idstringyesstrategy.business_units.id this writing pattern belongs to
idstringnoRow id to update. Omit to resolve by slot (update-or-insert).
slotstringnoStable machine key for the voice surface (e.g. “book”, “youtube”). Used to resolve update-vs-insert when id is not given.
namestringnoHuman-readable label shown in the BM Canvas UI
patternobjectnoFree-form voice definition (jsonb). Existing DFL rows use: description, toneAxes [{id,left,right,value}], vocabularyDo[], vocabularyAvoid[], examplePairs [{id,onBrand,offBrand}], notes. Call list_writing_patterns first and keep those keys so existing consumers keep working; add extra keys only when the slot genuinely needs them.
versionnumbernoVersion counter for this slot
sort_ordernumberno—

List SEO Keywords

List strategy.keywords rows (the SEO keyword corpus) of one business unit, ordered by text. count is the exact number of keywords the BU holds, independent of limit — compare it before and after a test run to prove the run left no rows behind. Reads run under the caller’s user-JWT (RLS applies).

ParameterTypeRequiredDescription
business_unit_idstringyesstrategy.business_units.id
limitnumbernoDefault: 200.

Delete SEO Keywords

Hard-delete strategy.keywords rows by id, scoped to one business unit. Their keyword_metrics and keyword_relations rows cascade; child keywords keep their row and lose parent_keyword_id (SET NULL). The generic repair and test-cleanup path for the SEO corpus (e2e specs delete what they create). An id that is absent, belongs to another BU, or is hidden by RLS is not deleted and is listed in not_deleted_ids.

ParameterTypeRequiredDescription
business_unit_idstringyesstrategy.business_units.id — every id must belong to this BU
idsstring[]yesstrategy.keywords.id values to delete

Upsert SEO Keywords

Create strategy.keywords rows (the SEO corpus) in one business unit, with an optional keyword_metrics snapshot and an optional parent (parent_keyword_id, the mind-map tree). A keyword is identified by (business_unit_id, slug); the slug is the campaigns app slugifier of text. A keyword that already exists is reused and NOT changed, unless update_existing is true — then its category / cluster fields are overwritten and a new metrics snapshot is added. Group keywords into a cluster by giving them the same cluster_name + cluster_color. Writes run under the caller user-JWT (RLS applies). Not a transaction: a failure after the first insert leaves the earlier rows; re-run the same batch to finish, it is idempotent.

ParameterTypeRequiredDescription
business_unit_idstringyesstrategy.business_units.id
keywordsobject[]yes—
update_existingbooleannoDefault: false.