Atlas / MCP servers / the-40-thieves / obsidian-tc

obsidian-tcBLOCK

mcp/the-40-thieves/obsidian-tc

Obsidian Turbocharged — governed, agent-ready Obsidian MCP server. 163 tools across 31 domains, multi-vault native, pluggable embeddings. TypeScript + Rust. AGPL-3.0-only.

Verdict
BLOCK
Grade
F
Trust score
29 /100
Exposed tools
128 102r · 20w · 6d
Transport
stdio · streamable-http
License
AGPL-3.0
Stars
9
01

Overview

From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.

Obsidian Turbocharged — governed, agent-ready vault access over MCP.

[](https://www.gnu.org/licenses/agpl-3.0)

What it is

obsidian-tc is a governed, agent-ready Model Context Protocol server for Obsidian vaults, for humans and agents alike. Instead of raw filesystem access to years of notes, every tool call runs through one pipeline — auth, folder ACLs, a read-only kill switch, HITL confirmation on destructive ops, and an audit log. It also adds fused retrieval (full-text, vector, graph) and a memory tier — episodes, decay, forgetting — living inside your vault under that same ACL. A full tool surface across every domain (all visible by default; a smaller set with opt-in profile: "core"), via a 3-tool discovery facade plus the standard search/fetch pair, listed in the tool catalog. Pitch: docs/WHY.md.

60-second start

No install:

npx obsidian-tc /path/to/vault

Every note tool and lexical search work immediately. Semantic search defaults to a bundled embedder — see When NOT to use for which install methods it reaches.

For multi-vault, auth, or ACLs, use a config file:

npm install -g obsidian-tc
obsidian-tc ./obsidian-tc.config.json   # Node >= 24 or Bun >= 1.1

Also ships as a Docker image, .mcpb bundle, and standalone binaries. More: docs/QUICKSTART.md.

When NOT to use obsidian-tc

Honest guidance — this is a heavier product than most:

  • Smallest possible footprint, read-only access, or no MCP at all. A single trusted human

over one vault, a read-only wrapper, or the Ob

Read from source at commit ae1e9d4020d0OBSERVED · 2026-10-11
02

Connect

Built from this server's own package name, version and transport as found in its source — not copied from anyone's documentation, so it cannot drift against a page we do not control. Replace the environment placeholders with a token scoped to the least it needs.

claude-code (npm)
claude mcp add obsidian-tc -- npx -y [email protected]
03

Exposed tools (128)

102 read · 20 write · 6 destructive. Blast radius: 6 tools can delete or overwrite — an agent that can be talked into calling a tool can be talked into calling this one.

ToolRiskDescription
aborts_in_precheckreadd
append_notereadAppend content to the END of a note (optionally creating it with create_if_missing: true), preserving existing bytes. It takes no anchor: to add under a specific heading, or anywhere other than the end, use patch_note instead. Set provenance:
array_or_objectreada union that admits an array
bad_outreadcommits then violates its output schema
bigreadoverflows the governor
big_keyedwritebig keyed write
big_keyed2writebig keyed write with a side effect
block_until_signalreadtest-only: resolves once ctx.signal aborts or a safety timeout fires
boomreadd
call_capabilitywriteInvoke a capability by name with its arguments. Routes into the same authorization, ACL, HITL, idempotency, and rate-limit pipeline as a direct tool call, so every safety gate applies and the target
clash_toolreadthe flat one
commit_wiki_pagewriteApply a wiki changeset in one step: a new page (path, frontmatter, the body you wrote) plus patches to existing pages that link them to it, from draft_wiki_page. The page must be inside the vault
conceptreadAn idea or term
copy_notereadCopy a note to a new path (backlinks are not rewritten for copies).
count_itemsreadtest-only
dangerreaddestructive
danger_writewritetest-only destructive tool
decompose_and_researchreadAnswer a compound question by decomposing it into facets, retrieving per facet, and synthesizing with citations and open questions.
delete_notedestructiveDelete a note (to the vault
describe_capabilityreadReturn the full input schema, required scopes, and safety hints (read-only / destructive) for a single capability by name.
destroydestructivex
double_gatereadtest-only dispatch-gated tool whose handler ALSO calls requireConfirmation
draft_wiki_pagereadPlan a new wiki page WITHOUT writing anything: the step between find_existing_page and commit_wiki_page. Give a topic (and optionally a page
drifterreadreturns a number where its schema promises a string
dup_toolreadx
fastreadd
find_capabilityreadSearch this server
find_connectionsreadFind and explain how notes relate to a topic or note.
fixture_toolreadDoes a thing with <angle> brackets | and a pipe.
flakyreadthrows before any effect
guardedreaddestructive with a precheck that always rejects
honestreadmatches its schema
keyedwritekeyed write
knowledge_get_criticalreadList the critical-severity docs in a vendor / external-docs corpus: the breaking changes, security issues, and production gotchas to read before starting work. A tight metadata pre-filter over frontmatter severity ==
knowledge_searchreadSemantic + keyword search over a vendor / external-docs corpus (a reserved read-only docs vault), with wikilink graph expansion and RRF fusion. The docs-scoped analogue of vault_graph_search: bind
kv_putwritekeyed write
limitreadHow many recent notes to review (default 20)
list_contradictionsreadList open contradictions (judge_verdict:
list_notesreadList notes under a folder (read-ACL filtered), with cursor pagination. response_format=concise returns {vault, notes: [{path}], next_cursor}, without size, mtime, folder and total_returned.
live_targetreadtarget chosen by live state
max_variantsreadHow many additional phrasings to fan out over (1-8, default 4)
mismatch_toolreadtest-only tool whose handler hashes a fresh nonce every call, on purpose
move_notewriteMove/rename a note and update backlinks. Crossing a folder boundary OR overwriting an existing destination requires confirmation; an overwritten destination is soft-deleted to .trash (recoverable).
needs_writewritetest-only tool requiring a write scope
nested_test_toolreadtest-only tool with a nested strict object
net_indexreadstands in for index_vault: long, and sends content to an external provider
no_vault_echoreadtakes no vault at all
noopreadtest-only
note_existsreadCheck whether a path exists in the vault and whether it is a file or folder.
note_mutatedestructiveread or delete a note, by action
object_unionreadall-object union keeps the fold
observereadd
ok_toolreadno-op precheck
optional_vault_echoreadvault is already optional
otherreadother keyed
oversized_echo_parityreadreturns a payload over the configured ceiling
patch_notewriteAnchored edit.
pathreadVault-relative path to the note (e.g. projects/foo.md)
peekreadreports whether the signal was installed
pingreadd
plainreadd
plain_objectreadconstrained output
plain_string_vault_echoreada
plain_writewriteno resolvePolicy at all
probereadtest-only: reports the session the context carries
probe_batchwritetest-only multi-note write
probe_hashreadtest-only
probe_idemreadtest-only
probe_noopreadtest-only
probe_plainreadtest-only
probe_readreadtest-only
probe_retrievalreadtest-only: logs one retrieval hit under whatever session the context carries
probe_skipped_patchwritetest-only partial batch
probe_wreadtest-only
probe_writewritetest-only write
proxy_arrayreadunconstrained output; the backend returned an array
proxy_scalarreadunconstrained output; the backend returned a primitive
purgedestructivedestructive
questionreadThe compound or multi-facet question to research against the vault
readreadx
read_itreadreads a declared path
read_notereadRead a note.
read_notesreadBatch-read notes. Returns successful notes and a per-path error list (partial). The response is held under the server
read_probereadtest-only
read_resourcesreadBatch resources/read: read many obsidian-tc://<vault>/<path> note resource URIs in one call. Returns one result per URI in request order: {ok: true, uri, mimeType, text} (identical to a single resources/read) or {ok: false, uri, error} for a malformed or unsupported URI, another vault
read_thingreadscoped read
recent_changes_digestreadSummarize what changed in the vault recently.
reflectreadThe reflect verb (retain/recall/reflect): recall over the vault, then a gateway synthesis pass — one on-demand, query-scoped operation returning a grounded answer with source provenance. mode
renamed_mutatorreadmutates, gated by acl.readOnly
renamed_vault_echoreadechoes vault_id
renamed_writewritedeclares pathAcl(write); handler never calls enforcePathAcl itself
rendezvousreadtest-only: completes only once
reset_vault_cachedestructiveDrop cache.
rollback_faultreadfails inside a transaction whose ROLLBACK then also fails
rr_writewritetest-only mutating tool
scope_free_probereadtest-only, declares no scopes (like server_health)
scope_parity_probereadTHE-514 parity probe — requires read:notes like resources/read does
scopedreadrequires a scope the caller lacks
search_and_readreadSearch a vault and return the top-k full notes in one call, instead of a search followed by read_notes. Ranking is vault_graph_search
seq_stepreadordinary keyed tool
shared_toolreadsame text
signals_then_succeedsreadsignals, then returns normally so #13
slowreadd
slow_stepreadsignals, then awaits so a second dispatch can overlap it
slow_thingreadtest-only task-augmentable tool
slow_toolreadtest tool needing several event-loop turns
sneakydestructivedeclares read, resolves delete
summarize_notereadSummarize a single vault note.
synthetic_writewritetest-only: declares pathAcl(write) but its handler never calls enforcePathAcl
treadx
titledreadtitled desc
topicreadA topic, question, or note path to explore connections for
touch_activereadtest tool whose target is resolved at dispatch
two_stepreadcommits effect #1, signals, then fails on later work
txn_stepreadsignals inside its own transaction, which then rolls back
unserializablereadcommits then returns a BigInt payload
vault_echoreadechoes the vault arg
vault_echo_parityreadechoes the vault arg
vault_graph_searchreadCross-domain / multi-hop semantic search with wikilink graph expansion (GraphRAG). Seeds by vector similarity, expands through the links_to graph (vault_edges), and fuses by RRF. Run index_vault first so the edge graph is populated. Returns chunks tagged seed|expansion with hop + via_edge. Optional
walkerreadreports three steps
wbulkreadbulk key
whoamireadtest-only: echoes the caller context it was dispatched with
wide_test_toolreadtest-only tool with four required fields
woptswritewrite options key
write_notewritewrites
writerwritetest-only: needs write:notes, to see a narrowed grant refuse a real call
xreada | b\nc
yreada\\|b
04

Trust audit

BLOCKgrade F · trust 29/100 Do not install this without reading the findings. The audit found something that could harm you or your machine.

LayerWhat it checksResult
L0Provenance & inventoryPASS
L1Static analysis of the codeFAIL
L2Instruction surface (what it tells the agent)FAIL
L3Class-specific surfaceWARN
L4Behavioural (sandbox)SKIPPED

What the source does

Filesystem
declared (11 observation(s))
Network
declared (5 observation(s))
Shell
declared (9 observation(s))
Dependencies
not all pinned
Secrets in source
found

Findings (25)

HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
packages/server/eval/perf/harness.ts:141
exec(sql: string) {
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
packages/server/src/db/node-node-sqlite.ts:13
exec(sql: string): void;
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
packages/server/src/db/types.ts:13
exec(sql: string): void;
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
packages/server/src/plur/local.ts:88
const out = parseOut(await exec(argv));
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHFilesystem / path · fs.credential_store · CWE-22, CWE-59
packages/server/src/cli/setup/client-install-agents.ts:197
"Current mechanism (per-agent JSON, ~/.aws/amazonq/cli-agents/<agent>.json) — add to that",
Why it matters. touches a credential store
HIGHFilesystem / path · fs.credential_store · CWE-22, CWE-59
packages/server/src/cli/setup/client-install-agents.ts:201
"Legacy mechanism (~/.aws/amazonq/mcp.json) — only read if the agent's own JSON sets",
Why it matters. touches a credential store
HIGHObfuscation / stealth · obf.rtl_override · CWE-506, CWE-94
packages/server/src/experiential/poison.ts:86
const BIDI_OVERRIDE = /[--]/;
HIGHObfuscation / stealth · obf.rtl_override · CWE-506, CWE-94
packages/server/src/experiential/poison.ts:115
const INVISIBLE_CONTROLS = /[---]/gu;
HIGHPrompt injection · prompt.zero_width · CWE-94, CWE-1427
CHANGELOG.md:5072
homoglyph (fullwidth "ignore") and interleaved-invisible ("ignore ... instructions")
Why it matters. invisible characters in instruction text
Fix. strip non-printing characters
MEDIUMNetwork egress · net.beacon_words · CWE-200, CWE-319
packages/server/src/experiential/poison.ts:96
const EXFIL = [
MEDIUMNetwork egress · net.beacon_words · CWE-200, CWE-319
packages/server/src/experiential/poison.ts:97
/\b(?:send|forward|post|upload|exfiltrate)\s+(?:this|that|it|the\s+\w+)\s+to\s+\S+/i,
MEDIUMNetwork egress · net.beacon_words · CWE-200, CWE-319
packages/server/src/experiential/poison.ts:169
if (EXFIL.some((p) => p.test(scan))) signals.push("exfil");
MEDIUMNetwork egress · net.beacon_words · CWE-200, CWE-319
packages/server/src/experiential/poison.ts:173
if (signals.includes("override") || signals.includes("exfil")) risk = "high";
MEDIUMObfuscation / stealth · obf.homoglyph · CWE-506, CWE-94
packages/server/eval/compare-baseline.ts:65
{ sel: (q) => q.baseline.ndcg_at_10, label: "ΔnDCG@10  " },
MEDIUMObfuscation / stealth · obf.homoglyph · CWE-506, CWE-94
packages/server/eval/compare-baseline.ts:66
{ sel: (q) => q.baseline.recall_at_10, label: "Δrecall@10" },
MEDIUMObfuscation / stealth · obf.homoglyph · CWE-506, CWE-94
packages/server/eval/compare-baseline.ts:67
{ sel: (q) => q.baseline.mrr_at_10, label: "ΔMRR@10   " },
MEDIUMObfuscation / stealth · obf.homoglyph · CWE-506, CWE-94
packages/server/eval/compare-baseline.ts:85
if (primary) process.stdout.write(`\n  ${describePower(primary.deltas, "power ΔnDCG@10  ")}\n`);
MEDIUMObfuscation / stealth · obf.homoglyph · CWE-506, CWE-94
packages/server/eval/compare.ts:42
{ sel: (q) => q.graph.ndcg_at_10, label: "ΔnDCG@10 " },
MEDIUMObfuscation / stealth · obf.zero_width · CWE-506, CWE-94
packages/server/src/experiential/poison.ts:85
const ZERO_WIDTH = /[-]/;
MEDIUMObfuscation / stealth · obf.zero_width · CWE-506, CWE-94
packages/server/src/experiential/poison.ts:115
const INVISIBLE_CONTROLS = /[---]/gu;
MEDIUMObfuscation / stealth · obf.zero_width · CWE-506, CWE-94
packages/server/src/search/llm-edges.ts:43
out = out.split(s).join(`${s[0]}${s.slice(1)}`);
MEDIUMHard-coded secrets · secret.aws · CWE-798, CWE-321
packages/server/test/ambient-import-pensieve.test.ts:65
{ rec_txt: "Access key: AKIAABCDEFGHIJKLMNOP", score: 0.95 },
MEDIUMHard-coded secrets · secret.aws · CWE-798, CWE-321
packages/server/test/ambient-import-pensieve.test.ts:131
expect(aws?.text).toContain("AKIAABCDEFGHIJKLMNOP"); // adapter maps raw text; redaction is ingestAmbient's job
MEDIUMHard-coded secrets · secret.aws · CWE-798, CWE-321
packages/server/test/ambient-import-pensieve.test.ts:216
expect(awsRow?.content).not.toContain("AKIAABCDEFGHIJKLMNOP");
MEDIUMHard-coded secrets · secret.aws · CWE-798, CWE-321
packages/server/test/episodes.test.ts:193
const secret = "AKIAABCDEFGHIJKLMNOP"; // AKIA + 16 chars: matches the AWS-key pattern exactly

Gates applied: no_behavioural_pass.

Audited 2026-10-11 · audit v0.4.1 · source sha ae1e9d4020d0full audit observations/trust-audit/mcp-server/the-40-thieves__obsidian-tc.json · Report an issue / request a re-scan
05

Audit history

Every audit this server has had. A grade with a past is a grade somebody is still checking.

DateSourceVerdictGradeScoreChange
2026-10-11ae1e9d4020d0BLOCKF29first audit
06

Questions

What is the obsidian-tc MCP server?

Obsidian Turbocharged — governed, agent-ready Obsidian MCP server. 163 tools across 31 domains, multi-vault native, pluggable embeddings. TypeScript + Rust. AGPL-3.0-only.

What tools does obsidian-tc expose?

128 in total: 102 read-only, 20 that write, and 6 that can delete or overwrite (delete_note, destroy, note_mutate, purge, reset_vault_cache). Every one is listed on this page with its risk.

Is obsidian-tc safe to connect to an agent?

No — not without reading the findings first. The audit graded it F (29/100) and found 9 critical or high issues in the source. Each one is listed on this page with the file and line it is on. Separately from the audit: 6 of its tools can destroy data, so scope the token you give it to what you actually need.

What credentials does obsidian-tc need?

It reads BGE_AUTH_TOKEN, CRAWL4AI_API_TOKEN, DOCGEN_LLM_KEY, LANGEXTRACT_API_KEY, LINEAR_API_KEY, LITELLM_AGENT_KEY, MY_GATEWAY_KEY, MY_TS_KEY, OBSIDIAN_TC_AS_SETUP_TOKEN, OBSIDIAN_TC_GATEWAY_TOKEN, OBSIDIAN_TC_JWT_SECRET and OBSIDIAN_TC_LIVE_KEY from the environment. Give it a token scoped to the least it needs — an agent that can be talked into calling a tool can be talked into calling it with your credentials.

How does obsidian-tc run?

It speaks stdio and streamable-http, so it runs as a local process your client starts. It is published on npm as @the-40-thieves/obsidian-tc-shared at 1.32.0.

How current is this page?

The grade is for one exact copy of the source (ae1e9d4020d0), read on 2026-10-11. The repository is watched and re-audited when it changes.

Advertisement