Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion Tools/TranscriptedCLI/Sources/TranscriptedCLI/ContextStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,11 @@ struct CLIContextDirectories {
let data = try? Data(contentsOf: manifestURL),
let manifest = try? JSONDecoder().decode(CLIAppDirectoryManifest.self, from: data),
manifest.version >= 1,
let captureLibrary = validatedConfiguredDirectory(manifest.captureLibraryDirectory, homeDirectory: home),
let meetings = validatedConfiguredDirectory(manifest.meetingsDirectory, homeDirectory: home),
let dictations = validatedConfiguredDirectory(manifest.dictationsDirectory, homeDirectory: home) else {
let dictations = validatedConfiguredDirectory(manifest.dictationsDirectory, homeDirectory: home),
isManifestDirectory(meetings, named: "meetings", under: captureLibrary),
isManifestDirectory(dictations, named: "dictations", under: captureLibrary) else {
return nil
}

Expand Down Expand Up @@ -256,6 +259,19 @@ struct CLIContextDirectories {

return configuredURL
}

private static func isManifestDirectory(_ directory: URL, named name: String, under captureLibrary: URL) -> Bool {
let expected = captureLibrary
.appendingPathComponent(name, isDirectory: true)
.standardizedFileURL
.resolvingSymlinksInPath()
.standardizedFileURL
let actual = directory
.standardizedFileURL
.resolvingSymlinksInPath()
.standardizedFileURL
return actual.path == expected.path
}
}

struct CLIContextPathOptions: ParsableArguments {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,49 @@ final class ContextDirectoriesTests: XCTestCase {
])
}

func testResolveIgnoresManifestWhenDirectoriesDoNotMatchCaptureLibrary() throws {
let tempHome = makeTempDir()
defer { removeTempDir(tempHome) }

let transcriptedRoot = tempHome
.appendingPathComponent("Library", isDirectory: true)
.appendingPathComponent("Application Support", isDirectory: true)
.appendingPathComponent("Transcripted", isDirectory: true)
let manifestRoot = tempHome.appendingPathComponent("manifest-captures", isDirectory: true)
let unrelatedMeetings = tempHome.appendingPathComponent("unrelated-meetings", isDirectory: true)
let unrelatedDictations = tempHome.appendingPathComponent("unrelated-dictations", isDirectory: true)
let defaultCaptures = transcriptedRoot.appendingPathComponent("captures", isDirectory: true)
let defaultMeetings = defaultCaptures.appendingPathComponent("meetings", isDirectory: true)
let defaultDictations = defaultCaptures.appendingPathComponent("dictations", isDirectory: true)
try FileManager.default.createDirectory(at: transcriptedRoot, withIntermediateDirectories: true)

let manifestURL = transcriptedRoot.appendingPathComponent("mcp-directories.json", isDirectory: false)
try """
{
"version": 1,
"captureLibraryDirectory": "\(manifestRoot.path)",
"meetingsDirectory": "\(unrelatedMeetings.path)",
"dictationsDirectory": "\(unrelatedDictations.path)"
}
""".write(to: manifestURL, atomically: true, encoding: .utf8)

let directories = CLIContextDirectories.resolve(
dataDir: nil,
meetingsDir: nil,
dictationsDir: nil,
environment: [:],
fileManager: .default,
homeDirectory: tempHome
)

XCTAssertEqual(directories.meetingDirs.map(\.standardizedFileURL.path), [
defaultMeetings.standardizedFileURL.path,
])
XCTAssertEqual(directories.dictationDirs.map(\.standardizedFileURL.path), [
defaultDictations.standardizedFileURL.path,
])
}

func testResolveUsesAppCaptureLibraryPreferenceWhenManifestIsMissing() throws {
let tempHome = makeTempDir()
defer { removeTempDir(tempHome) }
Expand Down
1 change: 1 addition & 0 deletions Tools/TranscriptedMCP/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ This lets the server answer both meeting-specific queries (`who_is`, `read_meeti
cd Tools/TranscriptedMCP
swift build -c release
swift test
./.build/release/transcripted-mcp --help
./.build/release/transcripted-mcp --self-test
```

Expand Down
22 changes: 22 additions & 0 deletions Tools/TranscriptedMCP/Sources/TranscriptedMCP/Main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import MCP
@main
struct TranscriptedMCP {
static func main() async throws {
if CommandLine.arguments.contains("--help") || CommandLine.arguments.contains("-h") {
print(Self.helpText)
return
}

if CommandLine.arguments.contains("--version") {
print("transcripted-mcp 1.0.0")
return
Expand Down Expand Up @@ -69,6 +74,23 @@ struct TranscriptedMCP {
log("MCP server stopped")
}

private static let helpText = """
OVERVIEW: Read-only MCP server for Transcripted meetings and dictations.

USAGE: transcripted-mcp [--self-test] [--version] [--help]

OPTIONS:
--self-test Verify directory resolution and SQLite indexing, then exit.
--version Show the version.
-h, --help Show help information.

ENVIRONMENT:
TRANSCRIPTED_DATA_DIR Shared root with meetings/ and dictations/.
TRANSCRIPTED_MEETINGS_DIR Meeting directory override.
TRANSCRIPTED_DICTATIONS_DIR Dictation directory override.
TRANSCRIPTED_INDEX_DIR SQLite index directory override.
"""

private static func runSelfTest() throws {
let directories = TranscriptedDataDirectories.resolve()

Expand Down