Replies: 5 comments
|
The three symptoms have different causes, and the repository code gives a fairly direct way to separate them.
sys.stdin.reconfigure(encoding="utf-8", newline="")
sys.stdout.reconfigure(encoding="utf-8", newline="\n")Keep diagnostics on stderr; stdout must remain JSON-RPC only.
Relevant source:
|
|
Thanks again for taking the time to dig into this — following your pointers, we managed to trace all three issues to ground truth. Here's the full picture, including the precise root cause for #3, which we didn't expect to find but got there via the actual source. 1. UTF-8 fix — confirmed, fully resolved. 2. 3. Your if (
valueSchema.expression === "boolean" &&
typeof value === "string" &&
["true", "false"].includes(value)
) {
fixed[key] = value === "true";
}
Suggested fix: change Thanks for bearing with the back-and-forth on this — appreciate the plugin, and glad we could bring something concrete back for #3 instead of just a symptom report. |
|
Thanks for going to the source on #3, and sorry it took this long to close the loop. You were right, and it's wider than I'm taking your fix. Retyping them as ArkType Good to hear the UTF-8 and |
|
I merged it as #447. All six fields take a real JSON boolean now. One thing your report could not have shown from outside: the four handler-level test files could never have caught this. They call the handlers directly, below the registry, so they never touch the coercion seam at all. The new coverage sits in Not in a release yet. It's on |
|
All three are now in a release, and the UTF-8 one took far longer than it should have. That corruption is fixed in 2.0.1, with the fix you proposed. The bridge forces I owe you an apology for the delay. You root-caused this on 26 July and confirmed the fix The new tests assert that the bridge forces the encoding, not that a round-trip works. Your second point needed no code change, as you found. The Thanks for going to the source. Three bugs, each one isolated, with a working fix attached |
Uh oh!
There was an error while loading. Please reload this page.
Thank you for at good and effective plugin. I've run into some issues:
Summary
While using MCP Connector against a vault with Danish/special characters (æ, ø, å, ü) in file and folder names, we found three distinct bugs. One (encoding) is root-caused and confirmed fixed. The other two are reproducible but not root-caused, since they live in the plugin's TypeScript code, which we don't have visibility into from the client side.
Environment: Windows, Claude Desktop, using scripts/obsidian_mcp_bridge.py (the documented Windows workaround for the mcp-remote hang).
Bug 1 — Non-ASCII characters corrupted end-to-end (encoding) — ROOT CAUSE FOUND, FIX CONFIRMED
Symptom
Any tool argument or file path containing non-ASCII characters was corrupted in a way consistent with UTF-8 bytes being misread as a single-byte codepage. Examples observed:
ø → ø
ü → ü
A vault-relative path like Personer/Person - Søren Møller-Nielsen.md was looked up as Personer/Person - Søren Møller-Nielsen.md and failed with "File not found."
Frontmatter content written through patch_vault_file came back corrupted the same way (e.g. writing the alias Søren produced Søren in the file).
Re-sending an already-corrupted string doubled the corruption (ø → ü-style), confirming a single reinterpretation step happening repeatedly whenever non-ASCII bytes passed through.
Root cause
scripts/obsidian_mcp_bridge.py reads incoming JSON-RPC messages from sys.stdin and writes responses to sys.stdout without forcing an encoding:
python
def main(argv: Optional[list[str]] = None, stdin=None) -> None:
argv = sys.argv if argv is None else argv
stdin = sys.stdin if stdin is None else stdin
...
On Windows, sys.stdin/sys.stdout default to the process's locale-preferred encoding (commonly cp1252), not UTF-8, unless the environment forces it (e.g. PYTHONUTF8=1 or PYTHONIOENCODING=utf-8). Claude Desktop writes UTF-8-encoded bytes down the stdio pipe. When the bridge decodes those bytes as cp1252/Latin-1 instead of UTF-8, every non-ASCII character gets corrupted in exactly the observed way:
ø is 0xC3 0xB8 in UTF-8.
Read as Latin-1/cp1252, those two bytes become two separate characters: à (0xC3) and ¸ (0xB8) → literal string "ø".
This affects every code path that touches request/response text, since decoding happens once at the stdin boundary before the message is ever parsed as JSON.
Fix (confirmed working)
Force UTF-8 explicitly on stdin/stdout at the top of main(), before reading any input:
python
argv = sys.argv if argv is None else argv
if stdin is None:
try:
sys.stdin.reconfigure(encoding="utf-8", newline="")
except AttributeError:
pass
stdin = sys.stdin
try:
sys.stdout.reconfigure(encoding="utf-8", newline="\n")
except AttributeError:
pass
Verification
After applying the fix and fully restarting Claude Desktop:
get_vault_file on a path containing ü (Personer/Person - Gert Corfitzen Jürgensen.md) succeeded and returned correctly-encoded content, no corruption.
patch_vault_file writing Jürgensen into a frontmatter field round-tripped correctly (aliases: Jürgensen, not Jürgensen).
Reading several other files with æ/ø/å and even Japanese characters (チンクチ) in their path or content all round-tripped cleanly afterward.
Suggested action
Ship this fix (or the equivalent — e.g. setting PYTHONIOENCODING=utf-8 in the documented launch config, or using io.TextIOWrapper(sys.stdin.buffer, encoding="utf-8")) in scripts/obsidian_mcp_bridge.py, since this is very likely to affect any Windows user with non-ASCII content in their vault, not just Danish characters specifically.
Bug 2 — search_and_replace intermittently rejects a valid dry_run value
Symptom
Calling search_and_replace with dry_run: "false" (the documented string literal to actually apply a change) frequently fails with:
MCP error -32603: arguments.dry_run must be When "true" (default), no files are
modified — returns a preview of changes. Pass "false" to apply. Always preview
first to verify scope and intent. (was false)
The error message itself states the value false is what was received and is rejected — but "false" is exactly the documented, schema-valid value (anyOf: [const "false", const "true"]). Retrying the identical call, unchanged, sometimes succeeds after a handful of attempts (observed range: 1–7+ retries) and sometimes doesn't succeed at all within a reasonable number of tries. dry_run: "true" (the default/preview mode) fails far less often than dry_run: "false".
This was reproduced on multiple different files, including plain-ASCII filenames/content, so it is not related to Bug 1 — we confirmed this by triggering it on a file with no non-ASCII characters anywhere in its name or the pattern/replacement text.
What we ruled out
Not an encoding issue (reproduced on pure-ASCII content).
Restarting Obsidian entirely did not resolve it.
We do not believe it's a concurrency/race issue in the client-side bridge, since the plugin's tool logic runs inside Obsidian's single JS/TS runtime regardless of how many concurrent HTTP requests the bridge fires.
Hypothesis (unconfirmed — we don't have visibility into the plugin's TS source)
The error phrasing ("was false") suggests the validator may be receiving a native JSON boolean in some calls and the literal string "false" in others, and the schema (likely an ArkType union of string literals "true"/"false") only accepts the string form. If some code path in the tool-call serialization occasionally passes through a boolean instead of coercing/quoting it as the documented string literal, that would explain both the symptom and its intermittency. We can't confirm this without seeing how the MCP tool-call arguments are parsed/validated on the plugin side.
Suggested action
If feasible, have the dry_run field accept a JS boolean or the string literals, to remove the ambiguity at the boundary.
Add server-side logging of the raw received type/value for dry_run to help pin down whether it's ever arriving as a boolean vs. string.
Bug 3 — patch_vault_file (frontmatter target) doesn't produce a YAML list
Symptom
Using patch_vault_file with targetType: "frontmatter", target: "aliases", and multi-line/list-shaped content does not produce a proper YAML sequence. Examples observed:
operation: "append", content: "- Pearlman" on a file with no existing aliases key produced:
yaml
aliases: Pearlman- Pearlman
(a single malformed scalar, not a list — and with a strange duplicated "Pearlman").
operation: "replace", content: "- Pearlman" produced:
yaml
aliases: "- Pearlman"
(a valid but wrong YAML — a single quoted string containing a literal dash, not a sequence of one item).
operation: "replace", content: "\n - Pearlman" produced a YAML block scalar (aliases: |2- ...) instead of a clean list.
None of these match the format used elsewhere in the same vault for existing aliases: fields (a plain YAML block sequence, e.g.):
yaml
aliases:
We were only able to produce that correct shape by using search_and_replace to directly rewrite the frontmatter text ourselves — bypassing patch_vault_file for this use case entirely.
Hypothesis
patch_vault_file's frontmatter handling likely assigns content as a raw string value to the frontmatter key, rather than parsing it into a YAML sequence (array) when it's list-shaped, or accepting a native array via the tool schema. This may be intentional/by design (the tool may only support scalar frontmatter values), in which case it's more a documentation gap than a bug — it wasn't obvious from the tool description that list-valued frontmatter fields (like aliases, which is an Obsidian-native multi-value property type) aren't supported cleanly.
Suggested action
Either support a native array for content when targetType: "frontmatter" and the target key is meant to be a list, or
Document clearly that patch_vault_file cannot produce list-valued frontmatter, and that aliases/tags-style fields need a different tool or a manual search_and_replace on the raw text.
How we found these
All three were found through ordinary interactive use (asking an MCP client to read notes and add an aliases: frontmatter field across a folder of notes), not targeted fuzzing — so they're likely to surface for any user with non-ASCII vault content (Bug 1) or anyone adding list-valued frontmatter via patch_vault_file (Bug 3). Bug 2 seems more elusive/intermittent but was reproduced repeatedly across an entire session.
Happy to provide more repro detail, exact request/response payloads, or test on a later plugin version if useful.
All reactions