cantrip_entity_add
Add new entities like customers, pain points, or experiments to Cantrip's GoToMarket platform to organize and track business development data.
Instructions
Create a new entity. Automatically marked as 'accepted'. Fields vary by type:
icp: name, description, demographics, jobs_to_be_done, willingness_to_pay, current_alternatives, priority, is_beachhead
pain_point: description, severity (low|medium|high|critical), frequency (rare|occasional|frequent|constant), evidence
value_prop: framing (required — use instead of 'name'; 'description' is stored in extensions), tagline, evidence
channel: name, channel_type, lifecycle_stage (exploring|testing|scaling|maintaining|killed), cac, estimated_reach, conversion_rate (note: 'description' maps to 'notes' column)
experiment: title (required — use instead of 'name'), hypothesis, description, status (proposed|designed|active|completed|analyzed|abandoned), success_metrics, outcome_notes, value_prop_id, channel_id
competitor: name, description, url, positioning, strengths, weaknesses, pricing_model
contact: name, email, phone, company, role, source, url, notes Extra fields (any field not in the schema above) are stored in extensions. After adding entities, pause and confirm with the user before adding more. Pass
projectto override.cantrip.json— useful in cloud-hosted or multi-project contexts.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| entity_type | Yes | Entity type: icp, pain_point, value_prop, experiment, channel, competitor, contact | |
| name | No | Entity name (mapped to 'framing' for value_prop, 'title' for experiment) | |
| description | No | Entity description | |
| fields | No | Additional fields as key-value pairs (e.g. {severity: 'high', frequency: 'constant'}) | |
| project | No | Project slug — overrides .cantrip.json. Required in environments where cantrip_connect cannot write to the filesystem. |
Implementation Reference
- src/tools.ts:476-487 (handler)Handler function for cantrip_entity_add tool. Builds flags from name, description, and fields parameters, resolves the project context, and calls client.post to create the entity via the API.
handler: async (p) => { const flags: Record<string, string> = {}; if (p.name) flags.name = String(p.name); if (p.description) flags.description = String(p.description); if (p.fields && typeof p.fields === "object") { for (const [k, v] of Object.entries(p.fields as Record<string, string>)) { flags[k] = v; } } flags.project = resolveProject(p.project as string | undefined); return client.post(String(p.entity_type), ["add"], flags); }, - src/tools.ts:451-488 (registration)Tool registration in createTools function. Defines the tool name, description (including detailed field documentation for each entity type), input schema shape, and handler function.
{ name: "cantrip_entity_add", description: "Create a new entity. Automatically marked as 'accepted'. " + "Fields vary by type:\n" + "- icp: name, description, demographics, jobs_to_be_done, willingness_to_pay, current_alternatives, priority, is_beachhead\n" + "- pain_point: description, severity (low|medium|high|critical), frequency (rare|occasional|frequent|constant), evidence\n" + "- value_prop: framing (required — use instead of 'name'; 'description' is stored in extensions), tagline, evidence\n" + "- channel: name, channel_type, lifecycle_stage (exploring|testing|scaling|maintaining|killed), cac, estimated_reach, conversion_rate (note: 'description' maps to 'notes' column)\n" + "- experiment: title (required — use instead of 'name'), hypothesis, description, status (proposed|designed|active|completed|analyzed|abandoned), success_metrics, outcome_notes, value_prop_id, channel_id\n" + "- competitor: name, description, url, positioning, strengths, weaknesses, pricing_model\n" + "- contact: name, email, phone, company, role, source, url, notes\n" + "Extra fields (any field not in the schema above) are stored in extensions. " + "After adding entities, pause and confirm with the user before adding more." + PROJECT_DESC_SUFFIX, shape: { entity_type: entityTypeSchema, name: z.string().optional().describe("Entity name (mapped to 'framing' for value_prop, 'title' for experiment)"), description: z.string().optional().describe("Entity description"), fields: z .record(z.string()) .optional() .describe("Additional fields as key-value pairs (e.g. {severity: 'high', frequency: 'constant'})"), project: projectSchema, }, handler: async (p) => { const flags: Record<string, string> = {}; if (p.name) flags.name = String(p.name); if (p.description) flags.description = String(p.description); if (p.fields && typeof p.fields === "object") { for (const [k, v] of Object.entries(p.fields as Record<string, string>)) { flags[k] = v; } } flags.project = resolveProject(p.project as string | undefined); return client.post(String(p.entity_type), ["add"], flags); }, }, - src/tools.ts:466-475 (schema)Input schema definition using Zod. Defines entity_type (required), name (optional), description (optional), fields (optional record for additional fields), and project (optional) parameters.
shape: { entity_type: entityTypeSchema, name: z.string().optional().describe("Entity name (mapped to 'framing' for value_prop, 'title' for experiment)"), description: z.string().optional().describe("Entity description"), fields: z .record(z.string()) .optional() .describe("Additional fields as key-value pairs (e.g. {severity: 'high', frequency: 'constant'})"), project: projectSchema, }, - src/tools.ts:27-35 (helper)buildFlags helper function that converts parameters to a flags object, filtering out undefined, null, and empty string values. Used by the handler to prepare API request parameters.
function buildFlags(params: Record<string, unknown>): Record<string, string> { const flags: Record<string, string> = {}; for (const [k, v] of Object.entries(params)) { if (v !== undefined && v !== null && v !== "") { flags[k] = String(v); } } return flags; } - src/client.ts:79-125 (helper)CantripClient.post method that executes the API call. Constructs the request envelope with command, args, and flags, injects project context, adds authentication headers, and handles the HTTP response with error handling.
async post( command: string, args: string[] = [], flags: Record<string, string> = {}, ): Promise<CantripResponse> { const url = `${this.apiUrl}/api/cantrip`; // Inject project from .cantrip.json if not provided if (!flags.project) { const project = readProjectContext(); if (project) flags.project = project; } const body: CantripRequest = { command, args, flags }; const headers: Record<string, string> = { "Content-Type": "application/json", }; if (this.apiKey) { headers["Authorization"] = `Bearer ${this.apiKey}`; } let res: Response; try { res = await fetch(url, { method: "POST", headers, body: JSON.stringify(body) }); } catch (err) { throw new Error( `Cannot reach Cantrip API at ${this.apiUrl}. ` + `Check your network connection and CANTRIP_API_KEY.\n` + `(${err instanceof Error ? err.message : String(err)})`, ); } const json = (await res.json()) as CantripResponse; if ("error" in json && typeof json.error === "string") { let message = json.error; if (/insufficient credits/i.test(message)) { message += "\n\nPurchase credits at https://cantrip.ai"; } if (/not authenticated/i.test(message) || /unauthorized/i.test(message)) { message += "\n\nSet CANTRIP_API_KEY in your MCP server config. Get a key at https://cantrip.ai"; } throw new Error(`cantrip error: ${message}`); } return json; }