From 25485ca947dd2115cbc6245deddb8ced2d7c10f9 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 15 Nov 2025 16:56:24 +0000 Subject: [PATCH 1/2] fix: normalize paths to prevent double slashes causing 404 errors (#37) When querying the list_vault_files endpoint with a directory parameter containing a trailing slash (e.g., "DevOps/"), the system would generate paths with double slashes (e.g., "/vault/DevOps//"), resulting in HTTP 404 errors. This fix adds path normalization that strips trailing slashes before constructing the API path, ensuring consistent behavior regardless of whether users include trailing slashes in directory parameters. Fixes #37 --- packages/mcp-server/src/features/local-rest-api/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/mcp-server/src/features/local-rest-api/index.ts b/packages/mcp-server/src/features/local-rest-api/index.ts index 37a1424..e6c6eb0 100644 --- a/packages/mcp-server/src/features/local-rest-api/index.ts +++ b/packages/mcp-server/src/features/local-rest-api/index.ts @@ -251,7 +251,9 @@ export function registerLocalRestApiTools(tools: ToolRegistry, server: Server) { "List files in the root directory or a specified subdirectory of your vault.", ), async ({ arguments: args }) => { - const path = args.directory ? `${args.directory}/` : ""; + // Normalize path by removing trailing slashes to prevent double slashes + const directory = args.directory?.replace(/\/+$/, "") || ""; + const path = directory ? `${directory}/` : ""; const data = await makeRequest( LocalRestAPI.ApiVaultFileResponse.or( LocalRestAPI.ApiVaultDirectoryResponse, From b1ee5e94967a4aaca0e2821ff1ad105b8ced813f Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 15 Nov 2025 17:22:21 +0000 Subject: [PATCH 2/2] test: add tests for trailing slash normalization (#37) - Test removal of single and multiple trailing slashes - Test handling of paths without trailing slashes - Test empty and undefined directory inputs - Test nested paths with various trailing slash scenarios - Test API path construction prevents double slashes - Verify the fix for /vault//Documents/ bug --- .../local-rest-api/pathNormalization.test.ts | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 packages/mcp-server/src/features/local-rest-api/pathNormalization.test.ts diff --git a/packages/mcp-server/src/features/local-rest-api/pathNormalization.test.ts b/packages/mcp-server/src/features/local-rest-api/pathNormalization.test.ts new file mode 100644 index 0000000..1c41685 --- /dev/null +++ b/packages/mcp-server/src/features/local-rest-api/pathNormalization.test.ts @@ -0,0 +1,120 @@ +import { describe, expect, test } from "bun:test"; + +/** + * Tests for path normalization logic used in list_vault_files tool + * to prevent double slashes when constructing API paths + */ +describe("Path normalization for list_vault_files", () => { + /** + * Helper function that replicates the normalization logic from index.ts + * This ensures trailing slashes are removed before constructing the API path + */ + function normalizeDirectoryPath(directory?: string): string { + // This replicates the logic from index.ts line 255-256 + const normalized = directory?.replace(/\/+$/, "") || ""; + const path = normalized ? `${normalized}/` : ""; + return path; + } + + test("removes single trailing slash from directory", () => { + const input = "Documents/"; + const expected = "Documents/"; + expect(normalizeDirectoryPath(input)).toBe(expected); + }); + + test("removes multiple trailing slashes from directory", () => { + const input = "Documents///"; + const expected = "Documents/"; + expect(normalizeDirectoryPath(input)).toBe(expected); + }); + + test("handles directory without trailing slash", () => { + const input = "Documents"; + const expected = "Documents/"; + expect(normalizeDirectoryPath(input)).toBe(expected); + }); + + test("handles empty string directory", () => { + const input = ""; + const expected = ""; + expect(normalizeDirectoryPath(input)).toBe(expected); + }); + + test("handles undefined directory", () => { + const input = undefined; + const expected = ""; + expect(normalizeDirectoryPath(input)).toBe(expected); + }); + + test("handles nested paths with trailing slashes", () => { + const input = "Documents/Work/Projects/"; + const expected = "Documents/Work/Projects/"; + expect(normalizeDirectoryPath(input)).toBe(expected); + }); + + test("handles nested paths with multiple trailing slashes", () => { + const input = "Documents/Work/Projects///"; + const expected = "Documents/Work/Projects/"; + expect(normalizeDirectoryPath(input)).toBe(expected); + }); + + test("handles root directory with single slash", () => { + const input = "/"; + const expected = ""; + expect(normalizeDirectoryPath(input)).toBe(expected); + }); + + test("handles root directory with multiple slashes", () => { + const input = "///"; + const expected = ""; + expect(normalizeDirectoryPath(input)).toBe(expected); + }); + + test("preserves internal slashes while removing trailing ones", () => { + const input = "Documents/Work/Projects/2024/"; + const expected = "Documents/Work/Projects/2024/"; + expect(normalizeDirectoryPath(input)).toBe(expected); + }); +}); + +describe("API path construction prevents double slashes", () => { + /** + * Simulates the full path construction as done in index.ts + * to verify no double slashes are created + */ + function constructApiPath(directory?: string): string { + const normalized = directory?.replace(/\/+$/, "") || ""; + const path = normalized ? `${normalized}/` : ""; + return `/vault/${path}`; + } + + test("constructs correct path for directory without trailing slash", () => { + expect(constructApiPath("Documents")).toBe("/vault/Documents/"); + }); + + test("constructs correct path for directory with trailing slash", () => { + expect(constructApiPath("Documents/")).toBe("/vault/Documents/"); + }); + + test("constructs correct path for directory with multiple trailing slashes", () => { + expect(constructApiPath("Documents///")).toBe("/vault/Documents/"); + }); + + test("constructs correct path for root directory", () => { + expect(constructApiPath("")).toBe("/vault/"); + expect(constructApiPath(undefined)).toBe("/vault/"); + }); + + test("constructs correct path for nested directory", () => { + expect(constructApiPath("Documents/Work/Projects")).toBe( + "/vault/Documents/Work/Projects/", + ); + }); + + test("prevents double slashes at vault boundary", () => { + // This was the bug: /vault//Documents/ + const path = constructApiPath("Documents/"); + expect(path).not.toContain("//"); + expect(path).toBe("/vault/Documents/"); + }); +});