strategy — full tool reference
BM Canvas write path: business units, canvas blocks, relationships, assumptions, sources, snapshots, competitors, personas.
| Endpoint | https://strategy.mcp.devfellowship.com/mcp |
| Package | packages/dfl-mcp-strategy |
| Tools | 24 |
| Tool | Description |
|---|---|
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). |
get_business_unit | Fetch one strategy.business_units row by 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). |
update_business_unit | Partially update a strategy.business_units row by id. Only the fields provided are changed. |
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. |
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). |
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). |
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. |
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. |
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. |
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. |
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. |
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). |
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). |
update_persona | Partially update a strategy.personas row by id. Only the fields provided are changed. |
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). |
list_customer_segments | List strategy.customer_segments rows for a business_unit_id. |
update_customer_segment | Partially update a strategy.customer_segments row by id. Only the fields provided are changed. |
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. |
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. |
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. |
list_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). |
delete_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. |
upsert_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. |
list_business_units
Section titled “list_business_units”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).
| Parameter | Type | Required | Description |
|---|---|---|---|
include_archived | boolean | no | Include is_archived=true rows Default: false. |
parent_business_unit_id | string | no | Filter to children of this business_unit_id (the parent-link column, also named business_unit_id on the row) |
limit | number | no | Default: 50. |
get_business_unit
Section titled “get_business_unit”Get Business Unit
Fetch one strategy.business_units row by id.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | yes | strategy.business_units.id |
create_business_unit
Section titled “create_business_unit”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).
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | yes | Business unit name |
description | string | no | — |
sector | string | no | — |
tags | string | no | — |
color | string | no | Hex or CSS color used by the canvas UI |
logo_url | string | no | — |
business_unit_id | string | no | Parent business_unit_id, if this BU is nested under another (e.g. a product line under a company) |
golden_circle_why | object | no | — |
golden_circle_how | object | no | — |
golden_circle_what | object | no | — |
update_business_unit
Section titled “update_business_unit”Update Business Unit
Partially update a strategy.business_units row by id. Only the fields provided are changed.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | yes | strategy.business_units.id |
name | string | no | — |
description | string | no | — |
sector | string | no | — |
tags | string | no | — |
color | string | no | — |
logo_url | string | no | — |
business_unit_id | string | no | Parent business_unit_id |
golden_circle_why | object | no | — |
golden_circle_how | object | no | — |
golden_circle_what | object | no | — |
archive_business_unit
Section titled “archive_business_unit”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.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | yes | strategy.business_units.id |
archived | boolean | no | Default: true. |
set_canvas_block
Section titled “set_canvas_block”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).
| Parameter | Type | Required | Description |
|---|---|---|---|
block | enum | yes | One of: value_proposition, customer_segment, customer_relationship, channel, revenue_stream, key_partner, key_activity, key_resource, expense_category. |
business_unit_id | string | yes | strategy.business_units.id this block row belongs to |
id | string | no | Row id to update. Omit to insert a new row. |
fields | object | no | Column name → value for the target table Default: {}. |
add_business_unit_relationship
Section titled “add_business_unit_relationship”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).
| Parameter | Type | Required | Description |
|---|---|---|---|
from_bu | string | yes | strategy.business_units.id — the source of the relationship |
to_bu | string | yes | strategy.business_units.id — the target of the relationship (must differ from from_bu) |
relationship_type | enum | yes | One of: funds, supplies_talent, commercializes, provides_methodology, shares_brand, incubates. |
mechanics | object | no | Default: {}. |
description | string | no | — |
is_active | boolean | no | Default: true. |
started_at | string | no | ISO date the relationship started |
add_assumption
Section titled “add_assumption”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.
| Parameter | Type | Required | Description |
|---|---|---|---|
business_unit_id | string | yes | strategy.business_units.id |
statement | string | yes | The assumption, stated as a testable claim |
category | enum | yes | One of: desirability, viability, feasibility. |
status | enum | no | One of: untested, testing, validated, invalidated. Default: "untested". |
evidence | object[] | no | Default: []. |
risk_if_wrong | string | no | — |
add_source
Section titled “add_source”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.
| Parameter | Type | Required | Description |
|---|---|---|---|
source_type | enum | yes | One of: fireflies_call, plans_app, company_brain, manual. |
external_ref | string | no | External id/URL for the source (call id, plan slug, brain node id, …) |
occurred_at | string | no | ISO timestamp the source event occurred |
summary | string | no | — |
link_artifact_source
Section titled “link_artifact_source”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.
| Parameter | Type | Required | Description |
|---|---|---|---|
artifact_table | string | yes | The strategy.* table the artifact lives in, e.g. “assumptions”, “value_propositions” |
artifact_id | string | yes | The artifact row id in artifact_table |
source_id | string | yes | strategy.sources.id (from add_source) |
extraction_note | string | no | How/why this source backs this artifact |
create_canvas_snapshot
Section titled “create_canvas_snapshot”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.
| Parameter | Type | Required | Description |
|---|---|---|---|
business_unit_id | string | yes | strategy.business_units.id to snapshot |
trigger | enum | no | One of: manual, weekly_synthesis, pre_deck_export, post_strategic_call. Default: "manual". |
diff_summary_md | string | no | Optional human-readable summary of what changed since the last snapshot |
add_competitor
Section titled “add_competitor”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.
| Parameter | Type | Required | Description |
|---|---|---|---|
business_unit_id | string | yes | strategy.business_units.id this competitor is tracked against |
entity_id | string | no | Existing strategy.entities.id — omit if creating a new entity inline |
entity_name | string | no | Name for a new entity (required if entity_id is omitted) |
entity_description | string | no | — |
entity_website | string | no | — |
market_share | number | no | — |
threat_level | enum | no | One of: high, medium, low. |
visible | boolean | no | Default: true. |
add_persona
Section titled “add_persona”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).
| Parameter | Type | Required | Description |
|---|---|---|---|
customer_segment_id | string | yes | strategy.customer_segments.id this persona belongs to |
name | string | yes | — |
occupation | string | no | — |
description | string | no | — |
image | string | no | Image URL |
sort_order | number | no | Default: 0. |
buying_role | enum | no | One of: economic_buyer, champion, user, influencer, gatekeeper, blocker. |
icp_priority | enum | no | One of: primary, secondary, tertiary. |
list_personas
Section titled “list_personas”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).
| Parameter | Type | Required | Description |
|---|---|---|---|
customer_segment_id | string | no | strategy.customer_segments.id — direct filter |
business_unit_id | string | no | strategy.business_units.id — filters personas across every segment of this BU (joins via customer_segments) |
limit | number | no | Default: 50. |
update_persona
Section titled “update_persona”Update Persona
Partially update a strategy.personas row by id. Only the fields provided are changed.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | yes | strategy.personas.id |
customer_segment_id | string | no | Re-parent the persona to a different customer_segment_id |
name | string | no | — |
occupation | string | no | — |
description | string | no | — |
image | string | no | Image URL |
sort_order | number | no | — |
buying_role | enum | no | One of: economic_buyer, champion, user, influencer, gatekeeper, blocker. |
icp_priority | enum | no | One of: primary, secondary, tertiary. |
delete_persona
Section titled “delete_persona”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).
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | yes | strategy.personas.id |
list_customer_segments
Section titled “list_customer_segments”List Customer Segments
List strategy.customer_segments rows for a business_unit_id.
| Parameter | Type | Required | Description |
|---|---|---|---|
business_unit_id | string | yes | strategy.business_units.id this segment belongs to |
limit | number | no | Default: 50. |
update_customer_segment
Section titled “update_customer_segment”Update Customer Segment
Partially update a strategy.customer_segments row by id. Only the fields provided are changed.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | yes | strategy.customer_segments.id |
business_unit_id | string | no | Re-parent the segment to a different business_unit_id |
name | string | no | — |
description | string | no | — |
demographics | string | no | — |
needs | object | no | — |
pain_points | object | no | — |
age_range | string | no | One of strategy.age_range_enum |
persona_name | string | no | Legacy inline-persona field (superseded by strategy.personas rows — prefer add_persona/update_persona) |
persona_description | string | no | — |
persona_occupation | string | no | — |
persona_image | string | no | — |
delete_customer_segment
Section titled “delete_customer_segment”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.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | yes | strategy.customer_segments.id |
list_writing_patterns
Section titled “list_writing_patterns”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.
| Parameter | Type | Required | Description |
|---|---|---|---|
business_unit_id | string | yes | strategy.business_units.id whose writing patterns to list |
slot | string | no | Filter to a single slot (e.g. “book”, “youtube”, “pedagogy_fala”) |
limit | number | no | Default: 50. |
upsert_writing_pattern
Section titled “upsert_writing_pattern”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.
| Parameter | Type | Required | Description |
|---|---|---|---|
business_unit_id | string | yes | strategy.business_units.id this writing pattern belongs to |
id | string | no | Row id to update. Omit to resolve by slot (update-or-insert). |
slot | string | no | Stable machine key for the voice surface (e.g. “book”, “youtube”). Used to resolve update-vs-insert when id is not given. |
name | string | no | Human-readable label shown in the BM Canvas UI |
pattern | object | no | Free-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. |
version | number | no | Version counter for this slot |
sort_order | number | no | — |
list_keywords
Section titled “list_keywords”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).
| Parameter | Type | Required | Description |
|---|---|---|---|
business_unit_id | string | yes | strategy.business_units.id |
limit | number | no | Default: 200. |
delete_keywords
Section titled “delete_keywords”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.
| Parameter | Type | Required | Description |
|---|---|---|---|
business_unit_id | string | yes | strategy.business_units.id — every id must belong to this BU |
ids | string[] | yes | strategy.keywords.id values to delete |
upsert_keywords
Section titled “upsert_keywords”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.
| Parameter | Type | Required | Description |
|---|---|---|---|
business_unit_id | string | yes | strategy.business_units.id |
keywords | object[] | yes | — |
update_existing | boolean | no | Default: false. |