vault-go 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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gutierrez Henrique
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # vault-go
2
+
3
+ Servidor MCP local para pesquisar, ler e registrar conhecimento sincronizado
4
+ pelo MemVault. Usa transporte `stdio`, roda com Node.js e trabalha somente nos
5
+ caminhos permitidos em `~/.memvault/config.json`.
6
+
7
+ ## Instalação
8
+
9
+ Execução direta:
10
+
11
+ ```sh
12
+ npx -y vault-go@latest
13
+ ```
14
+
15
+ Instalação global:
16
+
17
+ ```sh
18
+ npm install --global vault-go
19
+ vault-go --version
20
+ ```
21
+
22
+ Configuração MCP genérica:
23
+
24
+ ```json
25
+ {
26
+ "mcpServers": {
27
+ "vault-go": {
28
+ "command": "npx",
29
+ "args": ["-y", "vault-go@latest"]
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ Para usar outro diretório local:
36
+
37
+ ```json
38
+ {
39
+ "mcpServers": {
40
+ "vault-go": {
41
+ "command": "npx",
42
+ "args": ["-y", "vault-go@latest"],
43
+ "env": {
44
+ "VAULT_GO_HOME": "/caminho/privado/.memvault"
45
+ }
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ ## Ferramentas MCP
52
+
53
+ - `vault_go_status`: configuração sanitizada, autenticação e caminhos.
54
+ - `vault_go_list_files`: lista arquivos textuais monitorados.
55
+ - `vault_go_search`: busca texto e retorna linha/trecho.
56
+ - `vault_go_read_file`: lê até 1 MiB dentro dos caminhos permitidos.
57
+ - `vault_go_write_note`: cria ou acrescenta Markdown em `mcp-notes`.
58
+ - `vault_go_cloud_health`: testa a API pública sem enviar tokens ou conteúdo.
59
+
60
+ O recurso `vault-go://status` fornece o mesmo resumo sanitizado em formato JSON.
61
+
62
+ ## Integração com a nuvem
63
+
64
+ O servidor MCP não implementa uma segunda sincronização e não lê chaves do
65
+ cofre. Ao criar a primeira nota, ele adiciona `~/.memvault/mcp-notes` aos
66
+ caminhos monitorados. O agente local existente:
67
+
68
+ 1. detecta a nota;
69
+ 2. cifra o conteúdo no dispositivo;
70
+ 3. envia somente o blob cifrado para `vault.resolveup.com.br`;
71
+ 4. sincroniza o arquivo com os outros dispositivos autorizados.
72
+
73
+ Mantenha o agente em execução para sincronização contínua:
74
+
75
+ ```sh
76
+ memvault start
77
+ ```
78
+
79
+ ## Segurança
80
+
81
+ - Nunca retorna access token ou refresh token.
82
+ - Restringe leitura aos caminhos configurados.
83
+ - Resolve caminhos reais e bloqueia travessia/symlinks externos.
84
+ - Ignora diretórios ocultos, `node_modules`, `dist` e `.cache`.
85
+ - Limita leitura e busca por arquivo a 1 MiB.
86
+ - Grava notas somente em `mcp-notes`, com permissões privadas.
87
+ - Usa HTTPS para health remoto; HTTP só é aceito em localhost.
88
+ - Não escreve logs no `stdout`, que é reservado ao protocolo MCP.
89
+
90
+ O cliente MCP conectado pode receber o conteúdo dos arquivos que solicitar.
91
+ Instale este servidor apenas em clientes confiáveis e revise as permissões antes
92
+ de autorizar chamadas de leitura ou escrita.
93
+
94
+ ## Desenvolvimento
95
+
96
+ ```sh
97
+ npm install
98
+ npm run typecheck
99
+ npm test
100
+ npm run pack:check
101
+ ```
102
+
103
+ Requer Node.js `>= 20`.
104
+
105
+ ## Publicação automática
106
+
107
+ O GitHub Actions executa testes em cada pull request e push para `main`. Depois
108
+ da versão inicial, o workflow de release interpreta commits convencionais,
109
+ atualiza a versão, publica no npm e cria a release e a tag no GitHub:
110
+
111
+ - `fix:` publica uma versão patch;
112
+ - `feat:` publica uma versão minor;
113
+ - `feat!:` ou `BREAKING CHANGE:` publica uma versão major;
114
+ - `docs:`, `test:`, `chore:` e `ci:` não publicam uma nova versão.
115
+
116
+ O repositório precisa do segredo `NPM_TOKEN`; ele não deve ser salvo em arquivo,
117
+ commit ou log.
@@ -0,0 +1,25 @@
1
+ export declare const DEFAULT_API_URL = "https://vault.resolveup.com.br/api";
2
+ export interface VaultConfig {
3
+ apiUrl: string;
4
+ email?: string;
5
+ deviceId?: string;
6
+ intervalSeconds: number;
7
+ watchPaths: string[];
8
+ }
9
+ export interface VaultStatus {
10
+ vaultHome: string;
11
+ apiUrl: string;
12
+ configured: boolean;
13
+ authenticated: boolean;
14
+ watchPaths: string[];
15
+ notesPath: string;
16
+ }
17
+ export declare function vaultHome(environment?: NodeJS.ProcessEnv): string;
18
+ export declare function configPath(home?: string): string;
19
+ export declare function authPath(home?: string): string;
20
+ export declare function notesPath(home?: string): string;
21
+ export declare function ensurePrivateDirectory(directory: string): void;
22
+ export declare function loadConfig(home?: string): VaultConfig;
23
+ export declare function saveConfig(config: VaultConfig, home?: string): void;
24
+ export declare function ensureNotesPath(home?: string): string;
25
+ export declare function getStatus(home?: string): VaultStatus;
package/dist/config.js ADDED
@@ -0,0 +1,75 @@
1
+ import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { dirname, join, resolve } from 'node:path';
4
+ export const DEFAULT_API_URL = 'https://vault.resolveup.com.br/api';
5
+ const DEFAULT_CONFIG = {
6
+ apiUrl: DEFAULT_API_URL,
7
+ intervalSeconds: 60,
8
+ watchPaths: [],
9
+ };
10
+ export function vaultHome(environment = process.env) {
11
+ const configured = environment['VAULT_GO_HOME'] ?? environment['MEMVAULT_HOME'];
12
+ return resolve(configured || join(homedir(), '.memvault'));
13
+ }
14
+ export function configPath(home = vaultHome()) {
15
+ return join(home, 'config.json');
16
+ }
17
+ export function authPath(home = vaultHome()) {
18
+ return join(home, 'auth.json');
19
+ }
20
+ export function notesPath(home = vaultHome()) {
21
+ return join(home, 'mcp-notes');
22
+ }
23
+ export function ensurePrivateDirectory(directory) {
24
+ mkdirSync(directory, { recursive: true, mode: 0o700 });
25
+ chmodSync(directory, 0o700);
26
+ }
27
+ export function loadConfig(home = vaultHome()) {
28
+ const file = configPath(home);
29
+ if (!existsSync(file))
30
+ return { ...DEFAULT_CONFIG, watchPaths: [] };
31
+ try {
32
+ const parsed = JSON.parse(readFileSync(file, 'utf8'));
33
+ const watchPaths = Array.isArray(parsed.watchPaths)
34
+ ? parsed.watchPaths.filter((entry) => typeof entry === 'string')
35
+ : [];
36
+ return {
37
+ ...DEFAULT_CONFIG,
38
+ ...parsed,
39
+ apiUrl: parsed.apiUrl || DEFAULT_API_URL,
40
+ watchPaths,
41
+ };
42
+ }
43
+ catch {
44
+ return { ...DEFAULT_CONFIG, watchPaths: [] };
45
+ }
46
+ }
47
+ export function saveConfig(config, home = vaultHome()) {
48
+ ensurePrivateDirectory(home);
49
+ const destination = configPath(home);
50
+ const temporary = join(dirname(destination), `.config.${process.pid}.tmp`);
51
+ writeFileSync(temporary, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
52
+ renameSync(temporary, destination);
53
+ chmodSync(destination, 0o600);
54
+ }
55
+ export function ensureNotesPath(home = vaultHome()) {
56
+ const directory = notesPath(home);
57
+ ensurePrivateDirectory(directory);
58
+ const config = loadConfig(home);
59
+ const normalized = new Set(config.watchPaths.map((entry) => resolve(entry)));
60
+ if (!normalized.has(resolve(directory))) {
61
+ saveConfig({ ...config, watchPaths: [...config.watchPaths, directory] }, home);
62
+ }
63
+ return directory;
64
+ }
65
+ export function getStatus(home = vaultHome()) {
66
+ const config = loadConfig(home);
67
+ return {
68
+ vaultHome: home,
69
+ apiUrl: config.apiUrl,
70
+ configured: existsSync(configPath(home)),
71
+ authenticated: existsSync(authPath(home)),
72
+ watchPaths: config.watchPaths.map((entry) => resolve(entry)),
73
+ notesPath: notesPath(home),
74
+ };
75
+ }
@@ -0,0 +1,25 @@
1
+ export declare const MAX_READ_BYTES = 1048576;
2
+ export interface VaultFile {
3
+ path: string;
4
+ root: string;
5
+ relativePath: string;
6
+ size: number;
7
+ modifiedAt: string;
8
+ }
9
+ export interface SearchMatch {
10
+ path: string;
11
+ relativePath: string;
12
+ line: number;
13
+ snippet: string;
14
+ }
15
+ export declare function resolveAllowedFile(input: string, roots: string[]): string;
16
+ export declare function listVaultFiles(roots: string[], options?: {
17
+ query?: string;
18
+ limit?: number;
19
+ }): VaultFile[];
20
+ export declare function readVaultFile(input: string, roots: string[]): {
21
+ path: string;
22
+ text: string;
23
+ };
24
+ export declare function searchVaultFiles(roots: string[], query: string, limit?: number): SearchMatch[];
25
+ export declare function writeVaultNote(notesRoot: string, title: string, content: string, mode?: 'create' | 'append'): string;
package/dist/files.js ADDED
@@ -0,0 +1,161 @@
1
+ import { appendFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync, writeFileSync, } from 'node:fs';
2
+ import { basename, extname, isAbsolute, join, relative, resolve, sep } from 'node:path';
3
+ export const MAX_READ_BYTES = 1_048_576;
4
+ const DEFAULT_LIMIT = 50;
5
+ const MAX_LIMIT = 200;
6
+ const TEXT_EXTENSIONS = new Set([
7
+ '.md',
8
+ '.mdx',
9
+ '.txt',
10
+ '.json',
11
+ '.jsonl',
12
+ '.yaml',
13
+ '.yml',
14
+ '.toml',
15
+ '.csv',
16
+ '.log',
17
+ ]);
18
+ const IGNORED_DIRECTORIES = new Set(['.git', 'node_modules', 'dist', '.cache']);
19
+ function boundedLimit(limit) {
20
+ if (!limit || !Number.isFinite(limit))
21
+ return DEFAULT_LIMIT;
22
+ return Math.min(MAX_LIMIT, Math.max(1, Math.trunc(limit)));
23
+ }
24
+ function isTextFile(file) {
25
+ return TEXT_EXTENSIONS.has(extname(file).toLowerCase());
26
+ }
27
+ function normalizedRoots(roots) {
28
+ return [...new Set(roots.map((root) => resolve(root)))].filter((root) => {
29
+ try {
30
+ return statSync(root).isDirectory();
31
+ }
32
+ catch {
33
+ return false;
34
+ }
35
+ });
36
+ }
37
+ function isInside(candidate, root) {
38
+ return candidate === root || candidate.startsWith(`${root}${sep}`);
39
+ }
40
+ export function resolveAllowedFile(input, roots) {
41
+ const availableRoots = normalizedRoots(roots);
42
+ const candidates = isAbsolute(input)
43
+ ? [resolve(input)]
44
+ : availableRoots.map((root) => resolve(root, input));
45
+ for (const candidate of candidates) {
46
+ if (!existsSync(candidate))
47
+ continue;
48
+ const actual = realpathSync(candidate);
49
+ if (!lstatSync(actual).isFile())
50
+ continue;
51
+ if (availableRoots.some((root) => isInside(actual, realpathSync(root))))
52
+ return actual;
53
+ }
54
+ throw new Error('Arquivo inexistente ou fora dos caminhos monitorados.');
55
+ }
56
+ export function listVaultFiles(roots, options = {}) {
57
+ const limit = boundedLimit(options.limit);
58
+ const query = options.query?.trim().toLocaleLowerCase();
59
+ const results = [];
60
+ for (const root of normalizedRoots(roots)) {
61
+ const stack = [root];
62
+ while (stack.length > 0 && results.length < limit) {
63
+ const current = stack.pop();
64
+ if (!current)
65
+ break;
66
+ const entries = readdirSync(current, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
67
+ for (const entry of entries) {
68
+ if (entry.name.startsWith('.'))
69
+ continue;
70
+ const absolute = join(current, entry.name);
71
+ if (entry.isSymbolicLink())
72
+ continue;
73
+ if (entry.isDirectory()) {
74
+ if (!IGNORED_DIRECTORIES.has(entry.name))
75
+ stack.push(absolute);
76
+ continue;
77
+ }
78
+ if (!entry.isFile() || !isTextFile(entry.name))
79
+ continue;
80
+ const relativePath = relative(root, absolute);
81
+ if (query && !relativePath.toLocaleLowerCase().includes(query))
82
+ continue;
83
+ const stats = statSync(absolute);
84
+ results.push({
85
+ path: absolute,
86
+ root,
87
+ relativePath,
88
+ size: stats.size,
89
+ modifiedAt: stats.mtime.toISOString(),
90
+ });
91
+ if (results.length >= limit)
92
+ break;
93
+ }
94
+ }
95
+ if (results.length >= limit)
96
+ break;
97
+ }
98
+ return results;
99
+ }
100
+ export function readVaultFile(input, roots) {
101
+ const file = resolveAllowedFile(input, roots);
102
+ const size = statSync(file).size;
103
+ if (size > MAX_READ_BYTES) {
104
+ throw new Error(`Arquivo excede o limite de ${MAX_READ_BYTES} bytes.`);
105
+ }
106
+ if (!isTextFile(file))
107
+ throw new Error('Tipo de arquivo não permitido para leitura textual.');
108
+ return { path: file, text: readFileSync(file, 'utf8') };
109
+ }
110
+ export function searchVaultFiles(roots, query, limit) {
111
+ const normalizedQuery = query.trim().toLocaleLowerCase();
112
+ if (!normalizedQuery)
113
+ throw new Error('A consulta não pode ser vazia.');
114
+ const maxResults = boundedLimit(limit);
115
+ const files = listVaultFiles(roots, { limit: MAX_LIMIT });
116
+ const matches = [];
117
+ for (const file of files) {
118
+ if (file.size > MAX_READ_BYTES)
119
+ continue;
120
+ const lines = readFileSync(file.path, 'utf8').split(/\r?\n/u);
121
+ for (let index = 0; index < lines.length; index += 1) {
122
+ const line = lines[index] ?? '';
123
+ if (!line.toLocaleLowerCase().includes(normalizedQuery))
124
+ continue;
125
+ matches.push({
126
+ path: file.path,
127
+ relativePath: file.relativePath,
128
+ line: index + 1,
129
+ snippet: line.trim().slice(0, 500),
130
+ });
131
+ if (matches.length >= maxResults)
132
+ return matches;
133
+ }
134
+ }
135
+ return matches;
136
+ }
137
+ function safeSlug(title) {
138
+ const slug = title
139
+ .normalize('NFKD')
140
+ .replace(/[\u0300-\u036f]/gu, '')
141
+ .toLocaleLowerCase()
142
+ .replace(/[^a-z0-9]+/gu, '-')
143
+ .replace(/^-+|-+$/gu, '')
144
+ .slice(0, 80);
145
+ return slug || `note-${Date.now()}`;
146
+ }
147
+ export function writeVaultNote(notesRoot, title, content, mode = 'create') {
148
+ mkdirSync(notesRoot, { recursive: true, mode: 0o700 });
149
+ const file = join(notesRoot, `${safeSlug(title)}.md`);
150
+ const body = `# ${title.trim()}\n\n${content.trim()}\n`;
151
+ if (mode === 'create' && existsSync(file)) {
152
+ throw new Error(`A nota ${basename(file)} já existe; use mode=append.`);
153
+ }
154
+ if (mode === 'append' && existsSync(file)) {
155
+ appendFileSync(file, `\n${content.trim()}\n`, { encoding: 'utf8', mode: 0o600 });
156
+ }
157
+ else {
158
+ writeFileSync(file, body, { encoding: 'utf8', mode: 0o600, flag: 'wx' });
159
+ }
160
+ return file;
161
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { createVaultGoServer, VERSION } from './server.js';
4
+ const argument = process.argv[2];
5
+ if (argument === '--version' || argument === '-v') {
6
+ process.stdout.write(`${VERSION}\n`);
7
+ process.exit(0);
8
+ }
9
+ if (argument === '--help' || argument === '-h') {
10
+ process.stdout.write(`vault-go ${VERSION}\n\nServidor MCP local por stdio.\n\nVariáveis:\n VAULT_GO_HOME Diretório local do vault (padrão: ~/.memvault)\n MEMVAULT_HOME Alias compatível com o agente local\n`);
11
+ process.exit(0);
12
+ }
13
+ async function main() {
14
+ const server = createVaultGoServer();
15
+ const transport = new StdioServerTransport();
16
+ await server.connect(transport);
17
+ }
18
+ main().catch((error) => {
19
+ const message = error instanceof Error ? error.stack ?? error.message : String(error);
20
+ process.stderr.write(`vault-go falhou: ${message}\n`);
21
+ process.exitCode = 1;
22
+ });
@@ -0,0 +1,3 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare const VERSION = "0.1.0";
3
+ export declare function createVaultGoServer(home?: string): McpServer;
package/dist/server.js ADDED
@@ -0,0 +1,138 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { z } from 'zod';
3
+ import { ensureNotesPath, getStatus, loadConfig, notesPath, vaultHome, } from './config.js';
4
+ import { listVaultFiles, readVaultFile, searchVaultFiles, writeVaultNote, } from './files.js';
5
+ export const VERSION = '0.1.0';
6
+ function jsonResult(value) {
7
+ const text = JSON.stringify(value, null, 2);
8
+ return {
9
+ content: [{ type: 'text', text }],
10
+ structuredContent: value,
11
+ };
12
+ }
13
+ function errorResult(error) {
14
+ const message = error instanceof Error ? error.message : 'Erro inesperado.';
15
+ return {
16
+ content: [{ type: 'text', text: message }],
17
+ isError: true,
18
+ };
19
+ }
20
+ function rootsFor(home) {
21
+ const config = loadConfig(home);
22
+ return [...config.watchPaths, notesPath(home)];
23
+ }
24
+ function validateApiUrl(value) {
25
+ const url = new URL(value);
26
+ const local = url.hostname === 'localhost' || url.hostname === '127.0.0.1';
27
+ if (url.protocol !== 'https:' && !(local && url.protocol === 'http:')) {
28
+ throw new Error('A API deve usar HTTPS, exceto em localhost.');
29
+ }
30
+ return url;
31
+ }
32
+ export function createVaultGoServer(home = vaultHome()) {
33
+ const server = new McpServer({ name: 'vault-go', version: VERSION });
34
+ server.registerTool('vault_go_status', {
35
+ title: 'Status do Vault',
36
+ description: 'Mostra configuração local, autenticação e caminhos monitorados sem revelar tokens.',
37
+ inputSchema: {},
38
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
39
+ }, async () => jsonResult(getStatus(home)));
40
+ server.registerTool('vault_go_list_files', {
41
+ title: 'Listar arquivos do Vault',
42
+ description: 'Lista arquivos textuais dentro dos caminhos monitorados.',
43
+ inputSchema: {
44
+ query: z.string().trim().optional().describe('Filtro opcional aplicado ao caminho relativo.'),
45
+ limit: z.number().int().min(1).max(200).default(50),
46
+ },
47
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
48
+ }, async ({ query, limit }) => {
49
+ try {
50
+ const options = query === undefined ? { limit } : { query, limit };
51
+ return jsonResult({ files: listVaultFiles(rootsFor(home), options) });
52
+ }
53
+ catch (error) {
54
+ return errorResult(error);
55
+ }
56
+ });
57
+ server.registerTool('vault_go_search', {
58
+ title: 'Pesquisar no Vault',
59
+ description: 'Pesquisa texto nos arquivos monitorados e retorna trechos com número da linha.',
60
+ inputSchema: {
61
+ query: z.string().trim().min(1).max(500),
62
+ limit: z.number().int().min(1).max(200).default(50),
63
+ },
64
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
65
+ }, async ({ query, limit }) => {
66
+ try {
67
+ return jsonResult({ matches: searchVaultFiles(rootsFor(home), query, limit) });
68
+ }
69
+ catch (error) {
70
+ return errorResult(error);
71
+ }
72
+ });
73
+ server.registerTool('vault_go_read_file', {
74
+ title: 'Ler arquivo do Vault',
75
+ description: 'Lê um arquivo textual permitido, limitado a 1 MiB e sem atravessar os caminhos monitorados.',
76
+ inputSchema: {
77
+ path: z.string().trim().min(1).describe('Caminho absoluto ou relativo a um diretório monitorado.'),
78
+ },
79
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
80
+ }, async ({ path }) => {
81
+ try {
82
+ return jsonResult(readVaultFile(path, rootsFor(home)));
83
+ }
84
+ catch (error) {
85
+ return errorResult(error);
86
+ }
87
+ });
88
+ server.registerTool('vault_go_write_note', {
89
+ title: 'Registrar nota no Vault',
90
+ description: 'Cria ou acrescenta uma nota Markdown em mcp-notes, monitorada pelo agente de sincronização.',
91
+ inputSchema: {
92
+ title: z.string().trim().min(1).max(200),
93
+ content: z.string().trim().min(1).max(200_000),
94
+ mode: z.enum(['create', 'append']).default('create'),
95
+ },
96
+ annotations: { readOnlyHint: false, idempotentHint: false, openWorldHint: false },
97
+ }, async ({ title, content, mode }) => {
98
+ try {
99
+ const directory = ensureNotesPath(home);
100
+ return jsonResult({ path: writeVaultNote(directory, title, content, mode), mode });
101
+ }
102
+ catch (error) {
103
+ return errorResult(error);
104
+ }
105
+ });
106
+ server.registerTool('vault_go_cloud_health', {
107
+ title: 'Verificar nuvem do Vault',
108
+ description: 'Verifica o health público da API configurada sem enviar arquivos, notas ou tokens.',
109
+ inputSchema: {},
110
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
111
+ }, async () => {
112
+ try {
113
+ const status = getStatus(home);
114
+ const base = validateApiUrl(status.apiUrl);
115
+ const healthUrl = new URL(`${base.pathname.replace(/\/$/u, '')}/health`, base.origin);
116
+ const response = await fetch(healthUrl, {
117
+ headers: { Accept: 'application/json' },
118
+ signal: AbortSignal.timeout(10_000),
119
+ });
120
+ const body = await response.text();
121
+ return jsonResult({ ok: response.ok, status: response.status, url: healthUrl.href, body });
122
+ }
123
+ catch (error) {
124
+ return errorResult(error);
125
+ }
126
+ });
127
+ server.registerResource('vault-go-status', 'vault-go://status', {
128
+ title: 'Status do Vault Go',
129
+ description: 'Resumo sanitizado da configuração local.',
130
+ mimeType: 'application/json',
131
+ }, async (uri) => {
132
+ const status = getStatus(home);
133
+ return {
134
+ contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(status, null, 2) }],
135
+ };
136
+ });
137
+ return server;
138
+ }
package/package.json ADDED
@@ -0,0 +1,84 @@
1
+ {
2
+ "name": "vault-go",
3
+ "version": "0.1.0",
4
+ "description": "Servidor MCP local para pesquisar, ler e registrar conhecimento sincronizado pelo MemVault.",
5
+ "type": "module",
6
+ "bin": {
7
+ "vault-go": "dist/index.js",
8
+ "vault-go-mcp": "dist/index.js"
9
+ },
10
+ "main": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ },
17
+ "./server": {
18
+ "types": "./dist/server.d.ts",
19
+ "import": "./dist/server.js"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist/index.js",
24
+ "dist/index.d.ts",
25
+ "dist/server.js",
26
+ "dist/server.d.ts",
27
+ "dist/config.js",
28
+ "dist/config.d.ts",
29
+ "dist/files.js",
30
+ "dist/files.d.ts",
31
+ "README.md",
32
+ "LICENSE"
33
+ ],
34
+ "scripts": {
35
+ "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
36
+ "build": "npm run clean && tsc -p tsconfig.json",
37
+ "typecheck": "tsc -p tsconfig.json --noEmit",
38
+ "test": "npm run build && node --test dist/*.test.js",
39
+ "pack:check": "npm pack --dry-run",
40
+ "release": "semantic-release",
41
+ "prepublishOnly": "npm test && npm run pack:check"
42
+ },
43
+ "engines": {
44
+ "node": ">=20"
45
+ },
46
+ "publishConfig": {
47
+ "access": "public",
48
+ "provenance": false
49
+ },
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://github.com/GutierrezHenrique/vault-go.git"
53
+ },
54
+ "bugs": {
55
+ "url": "https://github.com/GutierrezHenrique/vault-go/issues"
56
+ },
57
+ "homepage": "https://vault.resolveup.com.br",
58
+ "keywords": [
59
+ "mcp",
60
+ "model-context-protocol",
61
+ "memory",
62
+ "vault",
63
+ "memvault",
64
+ "knowledge"
65
+ ],
66
+ "author": "Gutierrez Henrique",
67
+ "license": "MIT",
68
+ "dependencies": {
69
+ "@modelcontextprotocol/sdk": "1.29.0",
70
+ "zod": "3.25.76"
71
+ },
72
+ "devDependencies": {
73
+ "@semantic-release/commit-analyzer": "13.0.1",
74
+ "@semantic-release/github": "12.0.9",
75
+ "@semantic-release/npm": "13.1.5",
76
+ "@semantic-release/release-notes-generator": "14.1.1",
77
+ "@types/node": "^22.15.0",
78
+ "semantic-release": "25.0.8",
79
+ "typescript": "^5.9.3"
80
+ },
81
+ "overrides": {
82
+ "@hono/node-server": "2.0.11"
83
+ }
84
+ }