zhengchen.tao a4492d4938
Build Docker Image / build (push) Failing after 11m23s
Build Docker Image / deploy (push) Has been skipped
fix(deps): drop explicit pin on Microsoft.IdentityModel.Tokens
JwtBearer 10.0.7 transitively brings in Microsoft.IdentityModel.JsonWebTokens
9.x, which calls Base64UrlEncoder.Decode(ReadOnlySpan<char>, Span<byte>) —
an overload only present in Microsoft.IdentityModel.Tokens 9.x. The
explicit pin to 8.9.0 forced NuGet to downgrade Tokens to 8.9, producing
a runtime MissingMethodException on every JWT validation. Symptom: every
incoming request gets 401, never visible at default log level.

Drop the explicit pin so JwtBearer's transitive dep wins. The code only
uses SymmetricSecurityKey etc., which are stable across versions.
2026-05-17 13:47:26 +08:00

obsidian-mcp

Read and write an Obsidian vault via MCP (Model Context Protocol), with OAuth authentication via nas-auth.

Architecture

Claude.ai / MCP client
    │
    │ ① GET /.well-known/oauth-authorization-server
    │ ② OAuth Authorization Code + PKCE (via nas-auth)
    │ ③ Bearer JWT (aud=obsidian, scope=read:obsidian | write:obsidian)
    │
    ▼
obsidian-mcp.zhengchentao.win/mcp  (this service, port 9090 → 8080)
    │  JWT verify (HS256, shared key with nas-auth)
    │  VaultPathResolver — chroot + blacklist
    │  VaultWriteGuard   — whitelist for writes
    │
    ▼
/vault  (Docker volume, backed by WebDAV share synced via Remotely Save)

MCP Tools

Tool Auth required Description
list_vault_tree read:obsidian Depth-limited directory tree of the vault
list_files read:obsidian Files and subdirs in a directory
read_file read:obsidian Read file content (UTF-8), with optional byte-range params
search read:obsidian Literal substring search, glob-filterable
get_metadata read:obsidian Size, modified_at, has_frontmatter
write_file write:obsidian Overwrite a whitelisted file
append_file write:obsidian Append to a whitelisted file

Configuration (environment variables)

Variable Default Description
Vault__Root ./test-vault Vault root directory inside the container
Vault__Blacklist__0 Extra blacklist path segments (01-Secret, .obsidian, .trash, .git are hardcoded)
Vault__WriteWhitelist__0 Extra writable path prefixes
Jwt__Issuer https://auth.zhengchentao.win Expected iss claim
Jwt__Audience obsidian Expected aud claim
Jwt__SigningKey__Current required HS256 signing key (share with nas-auth)
Jwt__SigningKey__Previous Previous key during rotation
Mcp__OAuthDiscovery__Issuer https://auth.zhengchentao.win /.well-known issuer field
AuditLog__Directory /app/logs Directory for audit log files
ASPNETCORE_ENVIRONMENT Production Set to Development for verbose logs

Local development

# 1. Create a test vault
mkdir -p test-vault/NAS test-vault/Coding
echo "# Test" > test-vault/NAS/test.md

# 2. Set a dev signing key
export Jwt__SigningKey__Current=dev-secret-key-at-least-32-chars-long

# 3. Run
dotnet run

# 4. Generate a test JWT (requires dotnet user-jwts)
dotnet user-jwts create \
    --issuer https://auth.zhengchentao.win \
    --audience obsidian \
    --name tao \
    --claim sub=tao \
    --claim scope="read:obsidian write:obsidian"

# 5. Test with MCP Inspector
npx @modelcontextprotocol/inspector
# Transport: Streamable HTTP
# URL: http://localhost:5000/mcp
# Bearer Token: <paste JWT from step 4>

Write whitelist

Write tools only accept paths matching (hardcoded, extendable via env):

  • Prefix 02-ShengquGames/logs/
  • Prefix Coding/
  • Exact NAS/NAS 待办清单.md

Always forbidden (any directory): AGENTS.md, PROFILE.md, README.md, CLAUDE.md, 01-Secret/

Running tests

cd obsidian-mcp.Tests
dotnet test
S
Description
No description provided
Readme 142 KiB
Languages
C# 96.6%
Dockerfile 3.4%