Add create_note_from_template MCP tool #28

Closed
opened 2026-03-09 14:21:08 +00:00 by forgejo_admin · 1 comment

Lineage

plan-2026-03-09-template-rendering → Phase 2 (SDK + MCP tool)

Repo

forgejo_admin/pal-e-docs-mcp

User Story

As a Claude agent using MCP tools
I want a create_note_from_template tool
So that I can create notes from Jinja2 templates using structured data instead of writing raw HTML

Context

Phase 1 (PR #131, pal-e-docs) shipped POST /notes/from-template. The SDK method create_note_from_template() is being added in a companion issue on pal-e-docs-sdk. This issue adds the MCP tool that wraps that SDK method.

The tool should follow the exact same pattern as the existing create_note tool in tools/notes.py: @mcp.tool() decorator, Annotated[..., Field(description=...)] params, try/except with _ok() / _error_response().

API parameters:

  • template_slug (str, required) — slug of the template note (e.g. "template-plan")
  • slug (str, required) — slug for the new note
  • title (str, required) — title for the new note
  • data (str, required) — JSON string of template variables (MCP tools receive strings, so the tool must json.loads() this into a dict before passing to SDK)
  • tags (str | None) — comma-separated tag names (split to list, same as create_note)
  • project (str | None) — project slug
  • note_type (str | None) — note type
  • status (str | None) — status
  • parent_slug (str | None) — parent note slug
  • position (int | None) — position within parent

Important: The data param is a JSON string at the MCP layer (LLMs pass strings). The tool must parse it with json.loads() and pass the resulting dict to the SDK. Return 422-style error if JSON is invalid.

File Targets

Files the agent should modify:

  • src/pal_e_docs_mcp/tools/notes.py — add create_note_from_template tool function

Files the agent should NOT touch:

  • src/pal_e_docs_mcp/tools/__init__.py — no changes needed (notes module already imported)
  • src/pal_e_docs_mcp/server.py — no changes needed
  • Any other tool files

Acceptance Criteria

  • create_note_from_template tool is registered and visible to MCP clients
  • Tool accepts template_slug, slug, title, data (JSON string), and optional metadata params
  • Tool parses data from JSON string to dict via json.loads()
  • Tool splits comma-separated tags string to list (same pattern as create_note)
  • Tool calls get_sdk().create_note_from_template(...) with correct params
  • Invalid JSON in data returns a clear error (not a crash)
  • Tool description documents what data expects for plan templates

Test Expectations

  • Unit test: verify tool function exists and handles valid/invalid JSON data param
  • Run command: cd ~/pal-e-docs-mcp && PALDOCS_BASE_URL=https://paldocs.tail5b443a.ts.net .venv/bin/pytest tests/ -v

Constraints

  • Follow the existing tool pattern in tools/notes.py — same _ok() / _error_response() helpers
  • data must be a JSON string param (not a dict) because MCP tool params are strings from the LLM
  • Import json at the top of the file
  • The SDK method must already exist — this depends on the companion SDK issue being merged first
  • Tool description should mention that for plan templates, data should include keys like vision, repos, phases, decisions, etc.

Checklist

  • PR opened
  • Tests pass
  • No unrelated changes
  • plan-2026-03-09-template-rendering — parent plan
  • phase-2026-03-09-2-sdk-mcp — phase note in pal-e-docs
  • SDK companion issue: forgejo_admin/pal-e-docs-sdk (must merge first)
### Lineage `plan-2026-03-09-template-rendering` → Phase 2 (SDK + MCP tool) ### Repo `forgejo_admin/pal-e-docs-mcp` ### User Story As a Claude agent using MCP tools I want a `create_note_from_template` tool So that I can create notes from Jinja2 templates using structured data instead of writing raw HTML ### Context Phase 1 (PR #131, pal-e-docs) shipped `POST /notes/from-template`. The SDK method `create_note_from_template()` is being added in a companion issue on `pal-e-docs-sdk`. This issue adds the MCP tool that wraps that SDK method. The tool should follow the exact same pattern as the existing `create_note` tool in `tools/notes.py`: `@mcp.tool()` decorator, `Annotated[..., Field(description=...)]` params, try/except with `_ok()` / `_error_response()`. **API parameters:** - `template_slug` (str, required) — slug of the template note (e.g. "template-plan") - `slug` (str, required) — slug for the new note - `title` (str, required) — title for the new note - `data` (str, required) — JSON string of template variables (MCP tools receive strings, so the tool must `json.loads()` this into a dict before passing to SDK) - `tags` (str | None) — comma-separated tag names (split to list, same as `create_note`) - `project` (str | None) — project slug - `note_type` (str | None) — note type - `status` (str | None) — status - `parent_slug` (str | None) — parent note slug - `position` (int | None) — position within parent **Important:** The `data` param is a JSON string at the MCP layer (LLMs pass strings). The tool must parse it with `json.loads()` and pass the resulting dict to the SDK. Return 422-style error if JSON is invalid. ### File Targets Files the agent should modify: - `src/pal_e_docs_mcp/tools/notes.py` — add `create_note_from_template` tool function Files the agent should NOT touch: - `src/pal_e_docs_mcp/tools/__init__.py` — no changes needed (notes module already imported) - `src/pal_e_docs_mcp/server.py` — no changes needed - Any other tool files ### Acceptance Criteria - [ ] `create_note_from_template` tool is registered and visible to MCP clients - [ ] Tool accepts `template_slug`, `slug`, `title`, `data` (JSON string), and optional metadata params - [ ] Tool parses `data` from JSON string to dict via `json.loads()` - [ ] Tool splits comma-separated `tags` string to list (same pattern as `create_note`) - [ ] Tool calls `get_sdk().create_note_from_template(...)` with correct params - [ ] Invalid JSON in `data` returns a clear error (not a crash) - [ ] Tool description documents what `data` expects for plan templates ### Test Expectations - [ ] Unit test: verify tool function exists and handles valid/invalid JSON `data` param - Run command: `cd ~/pal-e-docs-mcp && PALDOCS_BASE_URL=https://paldocs.tail5b443a.ts.net .venv/bin/pytest tests/ -v` ### Constraints - Follow the existing tool pattern in `tools/notes.py` — same `_ok()` / `_error_response()` helpers - `data` must be a JSON string param (not a dict) because MCP tool params are strings from the LLM - Import `json` at the top of the file - The SDK method must already exist — this depends on the companion SDK issue being merged first - Tool description should mention that for plan templates, `data` should include keys like `vision`, `repos`, `phases`, `decisions`, etc. ### Checklist - [ ] PR opened - [ ] Tests pass - [ ] No unrelated changes ### Related - `plan-2026-03-09-template-rendering` — parent plan - `phase-2026-03-09-2-sdk-mcp` — phase note in pal-e-docs - SDK companion issue: `forgejo_admin/pal-e-docs-sdk` (must merge first)
Author
Owner

PR #31 Review

BLOCKERS

None.

NITS

  1. Missing is_public / revised_by params -- create_note exposes both, but create_note_from_template does not. The SDK method also omits them, so this is correct at the MCP layer. However, if the SDK adds them later, this tool will need updating. Not blocking -- just flagging for awareness.

  2. reference and post note types missing from description -- The note_type Field description lists 12 types but the DB has 16. Missing: reference, post, journal, incident. Same gap exists in create_note already, so this is pre-existing. Consider a follow-up to sync all note_type descriptions across tools.

SOP COMPLIANCE

  • Branch named after issue (28-add-create-note-from-template-tool references issue #28)
  • PR body has: Summary, Changes, Test Plan, Related
  • Related section references plan slug (plan-2026-03-09-template-rendering)
  • PR body includes Closes #28
  • Tests exist (10 new tests in TestCreateNoteFromTemplate)
  • No secrets, .env files, or credentials committed
  • No unnecessary file changes (3 files: tool, tests, lockfile sync)
  • tools/__init__.py unchanged (notes module already imported)

CODE REVIEW

Tool implementation (src/pal_e_docs_mcp/tools/notes.py):

  • Follows the exact @mcp.tool() / Annotated[..., Field()] / _ok() / _error_response() pattern established by create_note.
  • data param is str at the MCP boundary, parsed via json.loads() before SDK call. Correct.
  • Invalid JSON returns structured 422 error via json.dumps() (not _error_response()) -- correct choice since this is pre-SDK validation, consistent with bulk_move_items.
  • Non-object JSON (arrays, scalars) rejected with clear error message.
  • Tags splitting uses the identical comprehension as create_note: [t.strip() for t in tags.split(",") if t.strip()] if tags else [].
  • SDK call forwards all 10 keyword args matching the SDK's create_note_from_template() signature exactly.
  • Outer try/except Exception catches SDK errors and routes through _error_response().

Tests (tests/test_param_alignment.py):

  • 10 tests covering: valid JSON, invalid JSON, non-object JSON, scalar JSON, tags CSV, tags None, trailing comma, optional metadata forwarding, SDK error propagation, empty object.
  • Uses the shared mock_sdk fixture from conftest.py (which patches get_sdk in notes_mod).
  • All assertions verify both error content and SDK call args.

Lockfile (uv.lock):

  • Specifier updated from >=0.1.0 to >=0.2.0 to match pyproject.toml. Legitimate sync.

VERDICT: APPROVED

## PR #31 Review ### BLOCKERS None. ### NITS 1. **Missing `is_public` / `revised_by` params** -- `create_note` exposes both, but `create_note_from_template` does not. The SDK method also omits them, so this is correct at the MCP layer. However, if the SDK adds them later, this tool will need updating. Not blocking -- just flagging for awareness. 2. **`reference` and `post` note types missing from description** -- The `note_type` Field description lists 12 types but the DB has 16. Missing: `reference`, `post`, `journal`, `incident`. Same gap exists in `create_note` already, so this is pre-existing. Consider a follow-up to sync all note_type descriptions across tools. ### SOP COMPLIANCE - [x] Branch named after issue (`28-add-create-note-from-template-tool` references issue #28) - [x] PR body has: Summary, Changes, Test Plan, Related - [x] Related section references plan slug (`plan-2026-03-09-template-rendering`) - [x] PR body includes `Closes #28` - [x] Tests exist (10 new tests in `TestCreateNoteFromTemplate`) - [x] No secrets, .env files, or credentials committed - [x] No unnecessary file changes (3 files: tool, tests, lockfile sync) - [x] `tools/__init__.py` unchanged (notes module already imported) ### CODE REVIEW **Tool implementation** (`src/pal_e_docs_mcp/tools/notes.py`): - Follows the exact `@mcp.tool()` / `Annotated[..., Field()]` / `_ok()` / `_error_response()` pattern established by `create_note`. - `data` param is `str` at the MCP boundary, parsed via `json.loads()` before SDK call. Correct. - Invalid JSON returns structured 422 error via `json.dumps()` (not `_error_response()`) -- correct choice since this is pre-SDK validation, consistent with `bulk_move_items`. - Non-object JSON (arrays, scalars) rejected with clear error message. - Tags splitting uses the identical comprehension as `create_note`: `[t.strip() for t in tags.split(",") if t.strip()] if tags else []`. - SDK call forwards all 10 keyword args matching the SDK's `create_note_from_template()` signature exactly. - Outer `try/except Exception` catches SDK errors and routes through `_error_response()`. **Tests** (`tests/test_param_alignment.py`): - 10 tests covering: valid JSON, invalid JSON, non-object JSON, scalar JSON, tags CSV, tags None, trailing comma, optional metadata forwarding, SDK error propagation, empty object. - Uses the shared `mock_sdk` fixture from `conftest.py` (which patches `get_sdk` in `notes_mod`). - All assertions verify both error content and SDK call args. **Lockfile** (`uv.lock`): - Specifier updated from `>=0.1.0` to `>=0.2.0` to match `pyproject.toml`. Legitimate sync. ### VERDICT: APPROVED
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
forgejo_admin/pal-e-mcp#28
No description provided.