@openparachute/vault 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +31 -0
- package/.dockerignore +8 -0
- package/.env.example +9 -0
- package/.playwright-mcp/console-2026-04-14T04-17-25-395Z.log +2 -0
- package/.playwright-mcp/console-2026-04-14T04-18-11-767Z.log +1 -0
- package/.playwright-mcp/console-2026-04-14T04-19-07-733Z.log +2 -0
- package/.playwright-mcp/console-2026-04-14T04-20-45-440Z.log +2 -0
- package/.playwright-mcp/page-2026-04-14T04-17-25-536Z.yml +1 -0
- package/.playwright-mcp/page-2026-04-14T04-18-11-816Z.yml +1 -0
- package/.playwright-mcp/page-2026-04-14T04-18-31-674Z.yml +211 -0
- package/.playwright-mcp/page-2026-04-14T04-19-07-795Z.yml +59 -0
- package/.playwright-mcp/page-2026-04-14T04-19-36-239Z.yml +232 -0
- package/.playwright-mcp/page-2026-04-14T04-19-58-327Z.yml +182 -0
- package/.playwright-mcp/page-2026-04-14T04-20-10-517Z.yml +91 -0
- package/.playwright-mcp/page-2026-04-14T04-20-14-796Z.yml +70 -0
- package/.playwright-mcp/page-2026-04-14T04-20-45-509Z.yml +59 -0
- package/CLAUDE.md +115 -0
- package/Caddyfile +3 -0
- package/Dockerfile +22 -0
- package/LICENSE +661 -0
- package/README.md +356 -0
- package/bun.lock +219 -0
- package/bunfig.toml +2 -0
- package/core/package.json +7 -0
- package/core/src/core.test.ts +940 -0
- package/core/src/hooks.test.ts +361 -0
- package/core/src/hooks.ts +234 -0
- package/core/src/links.ts +352 -0
- package/core/src/mcp.ts +672 -0
- package/core/src/notes.ts +520 -0
- package/core/src/obsidian.test.ts +380 -0
- package/core/src/obsidian.ts +322 -0
- package/core/src/paths.test.ts +197 -0
- package/core/src/paths.ts +53 -0
- package/core/src/schema.ts +331 -0
- package/core/src/store.ts +303 -0
- package/core/src/tag-schemas.ts +104 -0
- package/core/src/test-preload.ts +8 -0
- package/core/src/types.ts +140 -0
- package/core/src/wikilinks.test.ts +277 -0
- package/core/src/wikilinks.ts +402 -0
- package/deploy/parachute-vault.service +20 -0
- package/docker-compose.yml +50 -0
- package/docs/HTTP_API.md +328 -0
- package/fly.toml +24 -0
- package/package.json +32 -0
- package/railway.json +14 -0
- package/religions-abrahamic-filter.png +0 -0
- package/religions-buddhism-v2.png +0 -0
- package/religions-buddhism.png +0 -0
- package/religions-final.png +0 -0
- package/religions-v1.png +0 -0
- package/religions-v2.png +0 -0
- package/religions-zen.png +0 -0
- package/scripts/migrate-audio-to-opus.test.ts +237 -0
- package/scripts/migrate-audio-to-opus.ts +499 -0
- package/src/auth.ts +170 -0
- package/src/cli.ts +1131 -0
- package/src/config-triggers.test.ts +83 -0
- package/src/config.test.ts +125 -0
- package/src/config.ts +716 -0
- package/src/db.ts +14 -0
- package/src/launchd.ts +109 -0
- package/src/mcp-http.ts +113 -0
- package/src/mcp-tools.ts +155 -0
- package/src/oauth.test.ts +1242 -0
- package/src/oauth.ts +729 -0
- package/src/owner-auth.ts +159 -0
- package/src/prompt.ts +141 -0
- package/src/published.test.ts +214 -0
- package/src/qrcode-terminal.d.ts +9 -0
- package/src/routes.ts +822 -0
- package/src/server.ts +450 -0
- package/src/systemd.ts +84 -0
- package/src/token-store.test.ts +174 -0
- package/src/token-store.ts +241 -0
- package/src/triggers.test.ts +397 -0
- package/src/triggers.ts +412 -0
- package/src/two-factor.test.ts +246 -0
- package/src/two-factor.ts +222 -0
- package/src/vault-store.ts +47 -0
- package/src/vault.test.ts +1309 -0
- package/tsconfig.json +29 -0
- package/web/README.md +73 -0
- package/web/bun.lock +827 -0
- package/web/eslint.config.js +23 -0
- package/web/index.html +15 -0
- package/web/package.json +36 -0
- package/web/public/favicon.svg +1 -0
- package/web/public/icons.svg +24 -0
- package/web/src/App.tsx +149 -0
- package/web/src/Graph.tsx +200 -0
- package/web/src/NoteView.tsx +155 -0
- package/web/src/Sidebar.tsx +186 -0
- package/web/src/api.ts +21 -0
- package/web/src/index.css +50 -0
- package/web/src/main.tsx +10 -0
- package/web/src/types.ts +37 -0
- package/web/src/utils.ts +107 -0
- package/web/tsconfig.app.json +25 -0
- package/web/tsconfig.json +7 -0
- package/web/tsconfig.node.json +24 -0
- package/web/vite.config.ts +15 -0
package/src/db.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vault database — opens bun:sqlite databases for vaults.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Database } from "bun:sqlite";
|
|
6
|
+
import { vaultDbPath, vaultDir } from "./config.ts";
|
|
7
|
+
import { mkdirSync } from "fs";
|
|
8
|
+
|
|
9
|
+
/** Open (or create) a vault's SQLite database. */
|
|
10
|
+
export function openVaultDb(name: string): Database {
|
|
11
|
+
const dir = vaultDir(name);
|
|
12
|
+
mkdirSync(dir, { recursive: true });
|
|
13
|
+
return new Database(vaultDbPath(name));
|
|
14
|
+
}
|
package/src/launchd.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* macOS launchd agent management for the vault daemon.
|
|
3
|
+
*
|
|
4
|
+
* The plist runs a wrapper script that sources ~/.parachute/.env
|
|
5
|
+
* before starting the server, so env vars (API keys, providers)
|
|
6
|
+
* are available to the daemon.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { homedir } from "os";
|
|
10
|
+
import { join, resolve, dirname } from "path";
|
|
11
|
+
import { writeFile, unlink } from "fs/promises";
|
|
12
|
+
import { $ } from "bun";
|
|
13
|
+
import { CONFIG_DIR, ENV_PATH, LOG_PATH, ERR_PATH } from "./config.ts";
|
|
14
|
+
|
|
15
|
+
const LABEL = "computer.parachute.vault";
|
|
16
|
+
const PLIST_PATH = join(homedir(), "Library", "LaunchAgents", `${LABEL}.plist`);
|
|
17
|
+
const WRAPPER_PATH = join(CONFIG_DIR, "start.sh");
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Generate a shell wrapper that loads .env before starting the server.
|
|
21
|
+
*/
|
|
22
|
+
function generateWrapper(serverPath: string, bunPath: string): string {
|
|
23
|
+
return `#!/bin/bash
|
|
24
|
+
# Auto-generated by parachute vault init
|
|
25
|
+
# Loads user PATH + ~/.parachute/.env then starts the vault server
|
|
26
|
+
|
|
27
|
+
# Source user shell profile for PATH (needed for parakeet-mlx, ffmpeg, etc.)
|
|
28
|
+
[ -f "$HOME/.zprofile" ] && source "$HOME/.zprofile" 2>/dev/null
|
|
29
|
+
[ -f "$HOME/.zshrc" ] && source "$HOME/.zshrc" 2>/dev/null
|
|
30
|
+
|
|
31
|
+
if [ -f "${ENV_PATH}" ]; then
|
|
32
|
+
set -a
|
|
33
|
+
source "${ENV_PATH}"
|
|
34
|
+
set +a
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
exec "${bunPath}" "${serverPath}"
|
|
38
|
+
`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function generatePlist(): string {
|
|
42
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
43
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
44
|
+
<plist version="1.0">
|
|
45
|
+
<dict>
|
|
46
|
+
<key>Label</key>
|
|
47
|
+
<string>${LABEL}</string>
|
|
48
|
+
<key>ProgramArguments</key>
|
|
49
|
+
<array>
|
|
50
|
+
<string>/bin/bash</string>
|
|
51
|
+
<string>${WRAPPER_PATH}</string>
|
|
52
|
+
</array>
|
|
53
|
+
<key>KeepAlive</key>
|
|
54
|
+
<true/>
|
|
55
|
+
<key>RunAtLoad</key>
|
|
56
|
+
<true/>
|
|
57
|
+
<key>StandardOutPath</key>
|
|
58
|
+
<string>${LOG_PATH}</string>
|
|
59
|
+
<key>StandardErrorPath</key>
|
|
60
|
+
<string>${ERR_PATH}</string>
|
|
61
|
+
<key>WorkingDirectory</key>
|
|
62
|
+
<string>${CONFIG_DIR}</string>
|
|
63
|
+
</dict>
|
|
64
|
+
</plist>`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function installAgent(): Promise<void> {
|
|
68
|
+
if (process.platform !== "darwin") {
|
|
69
|
+
throw new Error("launchd is only available on macOS. Use systemd on Linux.");
|
|
70
|
+
}
|
|
71
|
+
const serverPath = resolve(dirname(import.meta.path), "server.ts");
|
|
72
|
+
const bunPath = Bun.which("bun") || join(homedir(), ".bun", "bin", "bun");
|
|
73
|
+
|
|
74
|
+
// Write the wrapper script
|
|
75
|
+
await writeFile(WRAPPER_PATH, generateWrapper(serverPath, bunPath), { mode: 0o755 });
|
|
76
|
+
|
|
77
|
+
// Write and load the plist
|
|
78
|
+
const plist = generatePlist();
|
|
79
|
+
await writeFile(PLIST_PATH, plist);
|
|
80
|
+
await $`launchctl load ${PLIST_PATH}`.quiet();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function uninstallAgent(): Promise<void> {
|
|
84
|
+
try {
|
|
85
|
+
await $`launchctl unload ${PLIST_PATH}`.quiet();
|
|
86
|
+
} catch {}
|
|
87
|
+
try {
|
|
88
|
+
await unlink(PLIST_PATH);
|
|
89
|
+
} catch {}
|
|
90
|
+
try {
|
|
91
|
+
await unlink(WRAPPER_PATH);
|
|
92
|
+
} catch {}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function isAgentLoaded(): Promise<boolean> {
|
|
96
|
+
try {
|
|
97
|
+
const result = await $`launchctl list ${LABEL}`.quiet();
|
|
98
|
+
return result.exitCode === 0;
|
|
99
|
+
} catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function restartAgent(): Promise<void> {
|
|
105
|
+
try {
|
|
106
|
+
await $`launchctl unload ${PLIST_PATH}`.quiet();
|
|
107
|
+
} catch {}
|
|
108
|
+
await $`launchctl load ${PLIST_PATH}`.quiet();
|
|
109
|
+
}
|
package/src/mcp-http.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streamable HTTP MCP transport — stateless mode.
|
|
3
|
+
*
|
|
4
|
+
* Each request gets a fresh transport+server pair with no session ID
|
|
5
|
+
* generator. The SDK skips session validation when sessionIdGenerator
|
|
6
|
+
* is undefined, so clients can send `tools/call` or `tools/list`
|
|
7
|
+
* directly without a prior `initialize` handshake.
|
|
8
|
+
*
|
|
9
|
+
* This means server restarts never break existing MCP clients — the
|
|
10
|
+
* root cause of vault#56. The `initialize` method still works if a
|
|
11
|
+
* client sends it (the Server class handles it natively).
|
|
12
|
+
*
|
|
13
|
+
* Two modes:
|
|
14
|
+
* /mcp — unified, all vaults via `vault` param + list-vaults
|
|
15
|
+
* /vaults/{name}/mcp — scoped to one vault, no vault param
|
|
16
|
+
*
|
|
17
|
+
* Vault description is sent as the MCP server instruction.
|
|
18
|
+
* Read-only keys see fewer tools.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
22
|
+
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
23
|
+
import {
|
|
24
|
+
ListToolsRequestSchema,
|
|
25
|
+
CallToolRequestSchema,
|
|
26
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
27
|
+
import { generateUnifiedMcpTools, generateScopedMcpTools, getServerInstruction } from "./mcp-tools.ts";
|
|
28
|
+
import { isToolAllowed } from "./auth.ts";
|
|
29
|
+
import type { AuthResult } from "./auth.ts";
|
|
30
|
+
import type { McpToolDef } from "../core/src/mcp.ts";
|
|
31
|
+
|
|
32
|
+
/** Handle unified MCP at /mcp (all vaults). */
|
|
33
|
+
export async function handleUnifiedMcp(req: Request, auth: AuthResult): Promise<Response> {
|
|
34
|
+
const instruction = getServerInstruction();
|
|
35
|
+
return handleMcp(req, () => generateUnifiedMcpTools(), "parachute-vault", auth, instruction);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Handle scoped MCP at /vaults/{name}/mcp (single vault). */
|
|
39
|
+
export async function handleScopedMcp(req: Request, vaultName: string, auth: AuthResult): Promise<Response> {
|
|
40
|
+
const instruction = getServerInstruction(vaultName);
|
|
41
|
+
return handleMcp(req, () => generateScopedMcpTools(vaultName), `parachute-vault/${vaultName}`, auth, instruction);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function handleMcp(
|
|
45
|
+
req: Request,
|
|
46
|
+
getTools: () => McpToolDef[],
|
|
47
|
+
serverName: string,
|
|
48
|
+
auth: AuthResult,
|
|
49
|
+
instruction: string,
|
|
50
|
+
): Promise<Response> {
|
|
51
|
+
const { permission } = auth;
|
|
52
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
53
|
+
sessionIdGenerator: undefined,
|
|
54
|
+
enableJsonResponse: true,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const server = new Server(
|
|
58
|
+
{ name: serverName, version: "0.1.0" },
|
|
59
|
+
{
|
|
60
|
+
capabilities: { tools: {} },
|
|
61
|
+
instructions: instruction,
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const mcpTools = getTools();
|
|
66
|
+
|
|
67
|
+
// For read-only keys, only list readable tools
|
|
68
|
+
const visibleTools = permission === "read"
|
|
69
|
+
? mcpTools.filter((t) => isToolAllowed(t.name, "read"))
|
|
70
|
+
: mcpTools;
|
|
71
|
+
|
|
72
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
73
|
+
tools: visibleTools.map((t) => ({
|
|
74
|
+
name: t.name,
|
|
75
|
+
description: t.description,
|
|
76
|
+
inputSchema: t.inputSchema,
|
|
77
|
+
})),
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
81
|
+
const { name, arguments: args } = request.params;
|
|
82
|
+
|
|
83
|
+
if (!isToolAllowed(name, permission)) {
|
|
84
|
+
return {
|
|
85
|
+
content: [{ type: "text" as const, text: `Forbidden: insufficient permissions to call ${name}` }],
|
|
86
|
+
isError: true,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const tool = mcpTools.find((t) => t.name === name);
|
|
91
|
+
if (!tool) {
|
|
92
|
+
return {
|
|
93
|
+
content: [{ type: "text" as const, text: `Unknown tool: ${name}` }],
|
|
94
|
+
isError: true,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
const result = tool.execute((args ?? {}) as Record<string, unknown>);
|
|
99
|
+
return {
|
|
100
|
+
content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }],
|
|
101
|
+
};
|
|
102
|
+
} catch (err) {
|
|
103
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
104
|
+
return {
|
|
105
|
+
content: [{ type: "text" as const, text: `Error: ${message}` }],
|
|
106
|
+
isError: true,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
await server.connect(transport);
|
|
112
|
+
return transport.handleRequest(req);
|
|
113
|
+
}
|
package/src/mcp-tools.ts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tool generation for multi-vault.
|
|
3
|
+
*
|
|
4
|
+
* Wraps core tools with vault resolution (optional `vault` param) and
|
|
5
|
+
* overrides vault-info with actual vault config access.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { generateMcpTools } from "../core/src/mcp.ts";
|
|
9
|
+
import type { McpToolDef } from "../core/src/mcp.ts";
|
|
10
|
+
import { readVaultConfig, writeVaultConfig, readGlobalConfig, listVaults as getVaultNames } from "./config.ts";
|
|
11
|
+
import { getVaultStore } from "./vault-store.ts";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get the MCP server instruction for a vault (or the default vault).
|
|
15
|
+
* Sent once at session init — not per tool.
|
|
16
|
+
*/
|
|
17
|
+
export function getServerInstruction(vaultName?: string): string {
|
|
18
|
+
const globalConfig = readGlobalConfig();
|
|
19
|
+
const name = vaultName ?? globalConfig.default_vault ?? "default";
|
|
20
|
+
const config = readVaultConfig(name);
|
|
21
|
+
|
|
22
|
+
const parts: string[] = [
|
|
23
|
+
`You are connected to Parachute Vault "${name}".`,
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
if (config?.description) {
|
|
27
|
+
parts.push("", config.description);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return parts.join("\n");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Generate the unified MCP tool set.
|
|
35
|
+
* Each tool has an optional `vault` param that defaults to the default vault.
|
|
36
|
+
*/
|
|
37
|
+
export function generateUnifiedMcpTools(): McpToolDef[] {
|
|
38
|
+
const globalConfig = readGlobalConfig();
|
|
39
|
+
const defaultVault = globalConfig.default_vault ?? "default";
|
|
40
|
+
const vaultNames = getVaultNames();
|
|
41
|
+
const multiVault = vaultNames.length > 1;
|
|
42
|
+
|
|
43
|
+
// Get tool definitions from core (using default vault for schema)
|
|
44
|
+
const defaultStore = getVaultStore(defaultVault);
|
|
45
|
+
const coreTools = generateMcpTools(defaultStore);
|
|
46
|
+
|
|
47
|
+
// Wrap each core tool with vault resolution
|
|
48
|
+
const tools: McpToolDef[] = coreTools.map((coreTool) => {
|
|
49
|
+
let description = coreTool.description;
|
|
50
|
+
if (multiVault) {
|
|
51
|
+
description += `\n\nMulti-vault: pass 'vault' to target a specific vault. Default: "${defaultVault}". Available: ${vaultNames.join(", ")}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const inputSchema = {
|
|
55
|
+
...coreTool.inputSchema,
|
|
56
|
+
properties: {
|
|
57
|
+
vault: {
|
|
58
|
+
type: "string",
|
|
59
|
+
description: `Vault name (default: "${defaultVault}")`,
|
|
60
|
+
},
|
|
61
|
+
...(coreTool.inputSchema as any).properties,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
name: coreTool.name,
|
|
67
|
+
description,
|
|
68
|
+
inputSchema,
|
|
69
|
+
execute: (params) => {
|
|
70
|
+
const vaultName = (params.vault as string) ?? defaultVault;
|
|
71
|
+
const config = readVaultConfig(vaultName);
|
|
72
|
+
if (!config) {
|
|
73
|
+
throw new Error(`Vault "${vaultName}" not found. Available: ${getVaultNames().join(", ")}`);
|
|
74
|
+
}
|
|
75
|
+
const store = getVaultStore(vaultName);
|
|
76
|
+
const vaultTools = generateMcpTools(store);
|
|
77
|
+
const tool = vaultTools.find((t) => t.name === coreTool.name)!;
|
|
78
|
+
const { vault: _, ...rest } = params;
|
|
79
|
+
return tool.execute(rest);
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Override vault-info with actual vault config access
|
|
85
|
+
overrideVaultInfo(tools, defaultVault);
|
|
86
|
+
|
|
87
|
+
// Add list-vaults (multi-vault only, not in core)
|
|
88
|
+
if (multiVault) {
|
|
89
|
+
tools.push({
|
|
90
|
+
name: "list-vaults",
|
|
91
|
+
description: "List all available vaults with their descriptions.",
|
|
92
|
+
inputSchema: { type: "object", properties: {} },
|
|
93
|
+
execute: () => {
|
|
94
|
+
const names = getVaultNames();
|
|
95
|
+
return names.map((name) => {
|
|
96
|
+
const config = readVaultConfig(name);
|
|
97
|
+
return {
|
|
98
|
+
name,
|
|
99
|
+
description: config?.description,
|
|
100
|
+
created_at: config?.created_at,
|
|
101
|
+
is_default: name === defaultVault,
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return tools;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Generate MCP tools scoped to a single vault.
|
|
113
|
+
* No vault param — tools operate on that vault only.
|
|
114
|
+
*/
|
|
115
|
+
export function generateScopedMcpTools(vaultName: string): McpToolDef[] {
|
|
116
|
+
const store = getVaultStore(vaultName);
|
|
117
|
+
const tools = generateMcpTools(store);
|
|
118
|
+
|
|
119
|
+
// Override vault-info with actual vault config access
|
|
120
|
+
overrideVaultInfo(tools, vaultName);
|
|
121
|
+
|
|
122
|
+
return tools;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Override vault-info's placeholder execute with real vault config access.
|
|
127
|
+
*/
|
|
128
|
+
function overrideVaultInfo(tools: McpToolDef[], defaultVault: string): void {
|
|
129
|
+
const vaultInfo = tools.find((t) => t.name === "vault-info");
|
|
130
|
+
if (!vaultInfo) return;
|
|
131
|
+
|
|
132
|
+
vaultInfo.execute = (params) => {
|
|
133
|
+
const vaultName = (params.vault as string) ?? defaultVault;
|
|
134
|
+
const config = readVaultConfig(vaultName);
|
|
135
|
+
if (!config) throw new Error(`Vault "${vaultName}" not found`);
|
|
136
|
+
|
|
137
|
+
// Update description if provided
|
|
138
|
+
if (params.description !== undefined) {
|
|
139
|
+
config.description = params.description as string;
|
|
140
|
+
writeVaultConfig(config);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const result: any = {
|
|
144
|
+
name: config.name,
|
|
145
|
+
description: config.description ?? null,
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
if (params.include_stats) {
|
|
149
|
+
const store = getVaultStore(vaultName);
|
|
150
|
+
result.stats = store.getVaultStats();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return result;
|
|
154
|
+
};
|
|
155
|
+
}
|