Skip to content

Commit e645f7b

Browse files
mandanyaclaude
andcommitted
Fix 0.3.1: get_logseq_guide crashed on literal braces (.format regression)
0.3.0 added a `{"project": "[[X]]"}` example to the guide, but render_guide used GUIDE.format(prefix=...), which parsed the literal braces as a format field -> KeyError: '"project"'. Switch to str.replace so literal braces are safe forever. Also: clarify edit_block's old_content must be the block's raw_content (with the TODO/DONE marker + property lines), not the cleaned text — a real gotcha hit in practice. Adds tests/test_guide.py to guard the render against this class. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 74d3ce2 commit e645f7b

4 files changed

Lines changed: 43 additions & 4 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "mcp-server-logseq"
3-
version = "0.3.0"
3+
version = "0.3.1"
44
description = "An MCP server for LogSeq API"
55
readme = "README.md"
66
requires-python = ">=3.11"

src/mcp_server_logseq/guide.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,20 @@
100100
as `old_content` — the edit is rejected if it doesn't match (you never read it, or
101101
it changed meanwhile). This is the block analogue of a file edit's old/new match and
102102
guards against clobbering a concurrent change.
103+
- `old_content` must be the block's **`raw_content`** (what read_block returns under
104+
that key), NOT the cleaned `text`: raw_content keeps the leading task marker
105+
(`TODO`/`DONE`) and any `key:: value` property lines. Passing the marker-stripped
106+
`text` will fail the match.
103107
- Like other content writes it is confined to `{prefix}/`: a uuid on a page outside
104108
the agent namespace is refused. To change a task's status marker use
105109
`set_task_status`, not `edit_block`.
106110
"""
107111

108112

109113
def render_guide(agent_prefix: str) -> str:
110-
"""Fill the guide with the deployment's actual agent write-prefix."""
111-
return GUIDE.format(prefix=agent_prefix or "byAgent")
114+
"""Fill the guide with the deployment's actual agent write-prefix.
115+
116+
Uses str.replace (not .format) so literal braces in the guide text — e.g. a
117+
`{"project": "[[X]]"}` example — don't get parsed as format fields.
118+
"""
119+
return GUIDE.replace("{prefix}", agent_prefix or "byAgent")

tests/test_guide.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""render_guide must fill {prefix} without choking on literal braces in the text.
2+
3+
Regression: 0.3.0 added a `{"project": "[[X]]"}` example and rendered via
4+
str.format(), which parsed the literal braces as a format field -> KeyError.
5+
"""
6+
7+
from __future__ import annotations
8+
9+
from mcp_server_logseq.guide import render_guide
10+
11+
12+
def test_render_guide_does_not_raise_and_fills_prefix() -> None:
13+
out = render_guide("byAgent")
14+
assert "byAgent/" in out
15+
assert "{prefix}" not in out # every placeholder substituted
16+
17+
18+
def test_render_guide_preserves_literal_braces() -> None:
19+
# a literal {"project": ...} example must survive verbatim (the 0.3.0 regression)
20+
out = render_guide("byAgent")
21+
assert '{"project"' in out
22+
23+
24+
def test_render_guide_custom_prefix() -> None:
25+
out = render_guide("agentX")
26+
assert "agentX/" in out
27+
28+
29+
def test_render_guide_empty_prefix_defaults() -> None:
30+
out = render_guide("")
31+
assert "byAgent/" in out

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)