Initial public release
Build Docker Image / build (push) Failing after 1m22s

MCP (Model Context Protocol) server for reading and writing an Obsidian
vault, gated by OAuth-issued JWT bearer tokens. See README.md for setup.
This commit is contained in:
2026-05-17 23:53:00 +08:00
commit 515763bc72
31 changed files with 1931 additions and 0 deletions
+55
View File
@@ -0,0 +1,55 @@
using System.ComponentModel;
using ModelContextProtocol.Server;
using ObsidianMcp.Auth;
using ObsidianMcp.Services;
namespace ObsidianMcp.Tools;
[McpServerToolType]
public class ReadFileTool(VaultPathResolver resolver, IHttpContextAccessor http)
{
[McpServerTool]
[Description(
"Read the full content of a vault file (UTF-8). " +
"This is the primary tool for reading notes, design docs, logs, and config files. " +
"Use get_metadata first if you want to check the file size before reading. " +
"For very large files (>100 KB), use the offset and limit parameters (in bytes) " +
"to read specific byte ranges and avoid context window overflow. " +
"Returns the raw Markdown text including frontmatter.")]
public async Task<string> ReadFile(
[Description("Vault-relative path to the file. " +
"e.g. 'Notes/index.md', 'README.md', 'Projects/logs/2026-05.md'")] string path,
[Description("Byte offset to start reading from (0-based). Omit to read from the beginning.")] long? offset = null,
[Description("Number of bytes to read. Omit to read the entire file.")] long? limit = null)
{
ToolScopeGuard.EnsureScope(http, ScopePolicies.ReadObsidian);
var absPath = resolver.Resolve(path);
if (!File.Exists(absPath))
throw new FileNotFoundException($"文件不存在:{path}");
// 无分页时直接读全文
if (offset is null && limit is null)
return await File.ReadAllTextAsync(absPath);
// 有分页参数时按字节切片
await using var fs = new FileStream(absPath, FileMode.Open, FileAccess.Read, FileShare.Read);
long startByte = offset ?? 0;
if (startByte < 0) startByte = 0;
if (startByte >= fs.Length)
return string.Empty;
fs.Seek(startByte, SeekOrigin.Begin);
long bytesToRead = limit.HasValue
? Math.Min(limit.Value, fs.Length - startByte)
: fs.Length - startByte;
var buffer = new byte[bytesToRead];
var read = await fs.ReadAsync(buffer.AsMemory(0, (int)bytesToRead));
return System.Text.Encoding.UTF8.GetString(buffer, 0, read);
}
}