add_initiative
Saves a summarized product idea from conversations or notes into a local initiative file for scoring and spec generation.
Instructions
Write a new markdown_local initiative into PLAN_PROJECT_ROOT/initiatives/.md. Use this to capture an idea you (the AI client) already gathered via WebFetch / chat summary / customer-call notes — plan-master deliberately does NOT crawl URLs; you summarize, this tool persists. Only works when PLAN_SOURCE=markdown_local; for Linear / JIRA / Notion, create the issue in that platform instead. If id is omitted, auto-generates IDEA-NNN. Returns {id, written_to, source, overwritten, next_step_hint}. Typical chain: add_initiative -> score_initiative -> generate_spec_draft -> mk-spec-master.parse_spec.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| title | Yes | ||
| body | No | ||
| id | No | ||
| status | No | triage | |
| labels | No | ||
| reach | No | ||
| impact | No | ||
| confidence | No | ||
| effort | No | ||
| okr | No | ||
| out_of_scope | No | ||
| source_url | No | ||
| overwrite | No |
Implementation Reference
- The main handler function for the add_initiative tool. It validates that the active source is 'markdown_local', requires a 'title', auto-generates or accepts an ID, builds YAML frontmatter from optional fields (labels, reach, impact, confidence, effort, okr, out_of_scope, source_url), and writes a .md file to PLAN_PROJECT_ROOT/initiatives/. Supports overwrite. Returns {id, written_to, source, overwritten, next_step_hint}.
def add_initiative_tool(arguments: dict) -> dict[str, Any]: if SOURCE_NAME != "markdown_local": return _error( f"add_initiative only supports markdown_local (active: {SOURCE_NAME})", retryable=False, hint=( "For linear / jira / notion, create the issue in that platform's " "UI (or its own MCP), then list_initiatives picks it up. " "add_initiative is for capturing chat / web-research ideas locally." ), ) title = arguments.get("title") if not title: return _error( "title is required", retryable=False, hint="Pass a short human-readable title. Body + scoring inputs are optional.", ) body = (arguments.get("body") or "").rstrip() initiative_id = str(arguments.get("id") or _next_auto_id()) overwrite = bool(arguments.get("overwrite", False)) _cfg.INITIATIVES_DIR.mkdir(parents=True, exist_ok=True) target = _cfg.INITIATIVES_DIR / f"{initiative_id}.md" already_existed = target.exists() if already_existed and not overwrite: return _error( f"initiative_id={initiative_id!r} already exists at {target}", retryable=False, hint="Pass overwrite=true to replace, or choose a different id.", ) frontmatter: dict[str, Any] = { "id": initiative_id, "title": title, "status": arguments.get("status") or "triage", "created_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), } for key in ( "labels", "reach", "impact", "confidence", "effort", "okr", "out_of_scope", "source_url", ): if arguments.get(key) is not None: frontmatter[key] = arguments[key] ordered_keys = [ "id", "title", "status", "labels", "reach", "impact", "confidence", "effort", "okr", "out_of_scope", "source_url", "created_at", ] lines = ["---"] for key in ordered_keys: if key in frontmatter: lines.append(f"{key}: {_format_frontmatter_value(frontmatter[key])}") lines.append("---") lines.append("") lines.append(body) lines.append("") target.write_text("\n".join(lines), encoding="utf-8") return { "id": initiative_id, "written_to": str(target), "source": SOURCE_NAME, "overwritten": already_existed and overwrite, "next_step_hint": ( f"Run score_initiative(initiative_id='{initiative_id}') or " f"list_initiatives to confirm pickup." ), } - src/mk_plan_master/server.py:108-141 (schema)Schema (inputSchema) and tool description registered for the 'add_initiative' MCP tool. Defines all accepted properties: title (required), body, id, status, labels, reach, impact, confidence, effort, okr, out_of_scope, source_url, overwrite.
Tool( name="add_initiative", description=( "Write a new markdown_local initiative into " "PLAN_PROJECT_ROOT/initiatives/<id>.md. Use this to capture an " "idea you (the AI client) already gathered via WebFetch / chat " "summary / customer-call notes — plan-master deliberately does " "NOT crawl URLs; you summarize, this tool persists. Only works " "when PLAN_SOURCE=markdown_local; for Linear / JIRA / Notion, " "create the issue in that platform instead. If id is omitted, " "auto-generates IDEA-NNN. Returns {id, written_to, source, " "overwritten, next_step_hint}. Typical chain: add_initiative " "-> score_initiative -> generate_spec_draft -> " "mk-spec-master.parse_spec." ), inputSchema={ "type": "object", "properties": { "title": {"type": "string"}, "body": {"type": "string"}, "id": {"type": "string"}, "status": {"type": "string", "default": "triage"}, "labels": {"type": "array", "items": {"type": "string"}}, "reach": {"type": "number"}, "impact": {"type": "number"}, "confidence": {"type": "number"}, "effort": {"type": "number"}, "okr": {"type": "string"}, "out_of_scope": {"type": "array", "items": {"type": "string"}}, "source_url": {"type": "string"}, "overwrite": {"type": "boolean", "default": False}, }, "required": ["title"], }, - src/mk_plan_master/server.py:36-40 (registration)Registration of add_initiative in the _DISPATCH dictionary, mapping the string 'add_initiative' to the handler function initiatives_tools.add_initiative_tool.
_DISPATCH: dict[str, Callable[[dict], dict]] = { "get_plan_source_info": initiatives_tools.get_plan_source_info_tool, "list_initiatives": initiatives_tools.list_initiatives_tool, "fetch_initiative": initiatives_tools.fetch_initiative_tool, "add_initiative": initiatives_tools.add_initiative_tool, - Helper function _next_auto_id() which scans existing IDEA-NNN.md files in the initiatives directory to compute the next available ID (e.g., IDEA-001, IDEA-002).
def _next_auto_id() -> str: if not _cfg.INITIATIVES_DIR.exists(): return "IDEA-001" max_n = 0 for path in _cfg.INITIATIVES_DIR.glob("IDEA-*.md"): m = _AUTO_ID_PATTERN.match(path.name) if m: max_n = max(max_n, int(m.group(1))) return f"IDEA-{max_n + 1:03d}" - Helper function _format_frontmatter_value() which formats values for YAML frontmatter: lists as inline arrays, booleans as true/false, scalars as strings.
def _format_frontmatter_value(value: Any) -> str: """Inline-list + scalar formatter compatible with markdown_local._coerce.""" if isinstance(value, list): if not value: return "[]" return "[" + ", ".join(str(x) for x in value) + "]" if isinstance(value, bool): return "true" if value else "false" return str(value)