@edilsonfjdev/mcp-setup 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.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +129 -0
  3. package/bin/mcp-setup.js +3 -0
  4. package/dist/add-2HOZTV5H.js +66 -0
  5. package/dist/chunk-H3PELLAE.js +162 -0
  6. package/dist/chunk-JNVJQZTK.js +169 -0
  7. package/dist/chunk-Q36CDQK6.js +28 -0
  8. package/dist/chunk-UZ72QCYF.js +79 -0
  9. package/dist/config-loader-O23HRZIK.js +14 -0
  10. package/dist/doctor-OBFIE3JN.js +161 -0
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.js +42 -0
  13. package/dist/install-DAWHBGCE.js +169 -0
  14. package/dist/list-QO4GBJHR.js +29 -0
  15. package/dist/profiles-HU3NVRTV.js +30 -0
  16. package/dist/remove-T6C2DD7O.js +39 -0
  17. package/dist/update-GOCA4IZG.js +55 -0
  18. package/dist/validate-6RKOV7JG.js +167 -0
  19. package/package.json +74 -0
  20. package/templates/mcps/clickup.json +35 -0
  21. package/templates/mcps/context7.json +19 -0
  22. package/templates/mcps/coolify.json +24 -0
  23. package/templates/mcps/figma.json +19 -0
  24. package/templates/mcps/github.json +22 -0
  25. package/templates/mcps/hetzner.json +23 -0
  26. package/templates/mcps/linear.json +33 -0
  27. package/templates/mcps/n8n-docs.json +20 -0
  28. package/templates/mcps/n8n-instance.json +22 -0
  29. package/templates/mcps/playwright.json +20 -0
  30. package/templates/mcps/postgres.json +20 -0
  31. package/templates/mcps/semgrep.json +19 -0
  32. package/templates/mcps/sentry.json +34 -0
  33. package/templates/mcps/slack.json +34 -0
  34. package/templates/mcps/stitch.json +22 -0
  35. package/templates/profiles/agents.json +7 -0
  36. package/templates/profiles/base.json +7 -0
  37. package/templates/profiles/full.json +7 -0
  38. package/templates/profiles/headless.json +20 -0
  39. package/templates/profiles/minimal.json +7 -0
  40. package/templates/profiles/saas.json +7 -0
  41. package/templates/scopes/scope-map.json +24 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Edilson Júnior
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.
package/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # MCP Setup
2
+
3
+ > Solução padronizada para instalação e configuração de MCP Servers no Claude Code, adaptável a qualquer tipo de projeto.
4
+
5
+ ## Visão Geral
6
+
7
+ O MCP Setup é uma solução que padroniza e automatiza a configuração de servidores MCP (Model Context Protocol) para o Claude Code. Permite que desenvolvedores, arquitetos e equipes configurem rapidamente as integrações necessárias para seus projetos, sem precisar repetir trabalho manual ou conhecer detalhes de autenticação de cada serviço.
8
+
9
+ Destinado a profissionais e equipes que trabalham com múltiplos projetos de software — desde sites estáticos até plataformas SaaS com pagamentos, automação e IA.
10
+
11
+ ## Problema
12
+
13
+ Configurar integrações MCP manualmente para cada novo projeto gera:
14
+
15
+ - **Retrabalho** — mesmas configurações repetidas em cada projeto
16
+ - **Inconsistência** — configurações divergem entre projetos e membros da equipe
17
+ - **Risco de segurança** — credenciais mal gerenciadas ou expostas em repositórios
18
+ - **Barreira de entrada** — cada integração exige conhecimento específico de autenticação e configuração
19
+
20
+ ## Funcionalidades
21
+
22
+ - Perfis de configuração pré-definidos por tipo de projeto (5 templates)
23
+ - Instalação automatizada e interativa de servidores MCP
24
+ - Gestão de escopos: global (user), projeto (project) e local
25
+ - Separação segura de credenciais via variáveis de ambiente
26
+ - Documentação integrada de credenciais, segurança e troubleshooting
27
+ - Suporte a 14 integrações MCP padronizadas
28
+
29
+ ## Templates Disponíveis
30
+
31
+ | Template | MCPs | Cenário de Uso |
32
+ |----------|------|----------------|
33
+ | `minimal` | 3 | Sites estáticos, portfólios, landing pages |
34
+ | `base` | 9 | Projetos padrão com PM, design e deploy |
35
+ | `saas` | 12 | SaaS, CRM, plataformas com pagamentos e banco de dados |
36
+ | `agents` | 14+ | Projetos com agentes IA, workflows N8N, automação |
37
+ | `full` | 14 | Setup global completo (escopo user) |
38
+
39
+ ## MCPs Suportados
40
+
41
+ | Categoria | MCP | Autenticação |
42
+ |-----------|-----|-------------|
43
+ | **Desenvolvimento** | GitHub | Bearer Token |
44
+ | **Documentação** | Context7 | Nenhuma |
45
+ | **Design** | Figma | OAuth |
46
+ | | Stitch | API Key |
47
+ | **Gestão e Comunicação** | Linear | OAuth |
48
+ | | Slack | OAuth |
49
+ | | ClickUp | OAuth |
50
+ | **Infraestrutura** | Hetzner | API Token |
51
+ | | Coolify | API Token |
52
+ | **Banco de Dados** | PostgreSQL | DSN |
53
+ | **Workflow** | N8N Docs | Nenhuma |
54
+ | | N8N Instance | Bearer Token |
55
+ | **Segurança e Monitoramento** | Semgrep | Nenhuma |
56
+ | | Sentry | OAuth |
57
+ | **Testes** | Playwright | Nenhuma |
58
+
59
+ ## Estrutura do Projeto
60
+
61
+ ```
62
+ mcp-setup/
63
+ ├── CLAUDE.md
64
+ ├── CHANGELOG.md
65
+ ├── README.md
66
+ ├── LICENSE
67
+ ├── .gitignore
68
+ ├── .claude/
69
+ │ ├── rules/
70
+ │ └── documents/
71
+ │ ├── tbs/ # Technical Briefs
72
+ │ ├── adrs/ # Architecture Decision Records
73
+ │ ├── specs/ # Especificações
74
+ │ └── irs/ # Implementation Records
75
+ ├── repo/
76
+ │ └── mcp-project-ecope/ # Protótipo e escopo original
77
+ ├── templates/ # Templates MCP por tipo de projeto
78
+ ├── scripts/ # Scripts de instalação e utilitários
79
+ ├── docs/ # Documentação do projeto
80
+ │ ├── credentials.md
81
+ │ ├── security.md
82
+ │ ├── scopes.md
83
+ │ └── troubleshooting.md
84
+ └── tests/ # Testes automatizados
85
+ ```
86
+
87
+ ## Quick Start
88
+
89
+ ```bash
90
+ # 1. Clone o repositório
91
+ git clone <repo-url> && cd mcp-setup
92
+
93
+ # 2. Copie o arquivo de variáveis de ambiente
94
+ cp .env.example .env
95
+
96
+ # 3. Preencha as credenciais no .env
97
+ # Consulte docs/credentials.md para instruções detalhadas
98
+
99
+ # 4. Execute a instalação com o template desejado
100
+ ./scripts/install.sh --template base
101
+ ```
102
+
103
+ > Instruções detalhadas de instalação serão disponibilizadas após a fase de implementação.
104
+
105
+ ## Documentação
106
+
107
+ | Documento | Descrição |
108
+ |-----------|-----------|
109
+ | [Credenciais](docs/credentials.md) | Como obter e configurar credenciais para cada MCP |
110
+ | [Segurança](docs/security.md) | Boas práticas de segurança para configuração MCP |
111
+ | [Escopos](docs/scopes.md) | Gestão de escopos: user, project e local |
112
+ | [Troubleshooting](docs/troubleshooting.md) | Resolução de problemas comuns |
113
+
114
+ ## Roadmap
115
+
116
+ - [ ] CLI próprio para instalação e gestão de MCPs
117
+ - [ ] Suporte nativo a Windows (PowerShell/CMD)
118
+ - [ ] Validação automática de credenciais durante instalação
119
+ - [ ] Composição dinâmica de templates (merge/overlay)
120
+ - [ ] Mecanismo de atualização de MCPs já instalados
121
+ - [ ] Testes automatizados para scripts e templates
122
+
123
+ ## Licença
124
+
125
+ MIT
126
+
127
+ ## Autor
128
+
129
+ **Edilson Jr** — Tech Advisor & Arquiteto e Engenheiro de Software
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ import('../dist/index.js')
@@ -0,0 +1,66 @@
1
+ import {
2
+ loadEnv,
3
+ validateCredential
4
+ } from "./chunk-JNVJQZTK.js";
5
+ import {
6
+ executeCommand,
7
+ generateCommand,
8
+ resolveScope
9
+ } from "./chunk-H3PELLAE.js";
10
+ import {
11
+ listMcps,
12
+ loadMcp
13
+ } from "./chunk-UZ72QCYF.js";
14
+ import {
15
+ logger
16
+ } from "./chunk-Q36CDQK6.js";
17
+
18
+ // src/commands/add.ts
19
+ async function addCommand(mcp, options) {
20
+ const { scope, dryRun } = options;
21
+ let mcpDef;
22
+ try {
23
+ mcpDef = loadMcp(mcp);
24
+ } catch {
25
+ const available = listMcps().map((m) => m.name).join(", ");
26
+ logger.error(`MCP "${mcp}" n\xE3o encontrado. Dispon\xEDveis: ${available}`);
27
+ process.exit(1);
28
+ }
29
+ logger.info(`Adicionando MCP "${mcpDef.displayName}"...`);
30
+ if (mcpDef.auth.envVar) {
31
+ const envResult = loadEnv();
32
+ if (envResult.success) {
33
+ const value = envResult.vars[mcpDef.auth.envVar];
34
+ if (!value) {
35
+ logger.warn(`Vari\xE1vel ${mcpDef.auth.envVar} n\xE3o encontrada no .env`);
36
+ } else {
37
+ const validation = validateCredential(mcpDef.auth.envVar, value);
38
+ if (!validation.valid) {
39
+ logger.warn(`${mcpDef.auth.envVar}: ${validation.error}`);
40
+ }
41
+ }
42
+ }
43
+ }
44
+ const resolvedScope = resolveScope(mcp, scope);
45
+ const cmd = generateCommand(mcpDef, resolvedScope);
46
+ if (dryRun) {
47
+ logger.info(`[dry-run] ${cmd.command}`);
48
+ return;
49
+ }
50
+ const spinner = logger.spinner(`Instalando ${mcpDef.displayName}...`);
51
+ const result = await executeCommand(cmd, false);
52
+ spinner.stop();
53
+ if (result.success) {
54
+ if (result.skipped) {
55
+ logger.warn(`MCP ${mcpDef.displayName} j\xE1 est\xE1 configurado`);
56
+ } else {
57
+ logger.success(`MCP ${mcpDef.displayName} adicionado com sucesso (escopo: ${resolvedScope})`);
58
+ }
59
+ } else {
60
+ logger.error(`Falha ao adicionar ${mcpDef.displayName}: ${result.message}`);
61
+ process.exit(1);
62
+ }
63
+ }
64
+ export {
65
+ addCommand
66
+ };
@@ -0,0 +1,162 @@
1
+ import {
2
+ loadScopeMap
3
+ } from "./chunk-UZ72QCYF.js";
4
+
5
+ // src/core/scope-manager.ts
6
+ function resolveScope(mcpName, overrideScope) {
7
+ if (overrideScope) {
8
+ const scopeMap2 = loadScopeMap();
9
+ if (scopeMap2.project.includes(mcpName) && overrideScope === "user") {
10
+ return "project";
11
+ }
12
+ return overrideScope;
13
+ }
14
+ const scopeMap = loadScopeMap();
15
+ if (scopeMap.user.includes(mcpName)) return "user";
16
+ if (scopeMap.project.includes(mcpName)) return "project";
17
+ return "user";
18
+ }
19
+ function resolveScopesForProfile(mcpNames, overrideScope) {
20
+ const scopes = /* @__PURE__ */ new Map();
21
+ for (const name of mcpNames) {
22
+ scopes.set(name, resolveScope(name, overrideScope));
23
+ }
24
+ return scopes;
25
+ }
26
+
27
+ // src/core/mcp-installer.ts
28
+ import { exec as execCb } from "child_process";
29
+ import { promisify } from "util";
30
+ var exec = promisify(execCb);
31
+ async function getInstalledMcps() {
32
+ try {
33
+ const { stdout } = await exec("claude mcp list");
34
+ return stdout.split("\n").map((line) => line.trim().split(/\s+/)[0]).filter((name) => name && name.length > 0 && !name.startsWith("-") && !name.startsWith("="));
35
+ } catch {
36
+ return [];
37
+ }
38
+ }
39
+ function filterAlreadyInstalled(mcps, installed) {
40
+ const installedSet = new Set(installed.map((n) => n.toLowerCase()));
41
+ const pending = [];
42
+ const existing = [];
43
+ for (const mcp of mcps) {
44
+ if (installedSet.has(mcp.name.toLowerCase())) {
45
+ existing.push(mcp);
46
+ } else {
47
+ pending.push(mcp);
48
+ }
49
+ }
50
+ return { pending, existing };
51
+ }
52
+ function applyHeadlessMode(mcps) {
53
+ const resolved = [];
54
+ const skipped = [];
55
+ for (const mcp of mcps) {
56
+ if (mcp.auth.type === "oauth") {
57
+ const raw = mcp;
58
+ if (raw.headlessConfig && raw.auth?.headless) {
59
+ resolved.push({
60
+ ...mcp,
61
+ config: raw.headlessConfig,
62
+ auth: {
63
+ type: raw.auth.headless.type,
64
+ envVar: raw.auth.headless.envVar,
65
+ required: raw.auth.headless.required ?? true
66
+ }
67
+ });
68
+ } else {
69
+ skipped.push(mcp);
70
+ }
71
+ } else {
72
+ resolved.push(mcp);
73
+ }
74
+ }
75
+ return { resolved, skipped };
76
+ }
77
+ function generateCommand(mcp, scope) {
78
+ const parts = ["claude", "mcp", "add", mcp.name, "-s", scope];
79
+ if (mcp.config.type === "http") {
80
+ parts.push("--url", mcp.config.url);
81
+ if (mcp.config.headers) {
82
+ for (const [key, value] of Object.entries(mcp.config.headers)) {
83
+ parts.push("-H", `"${key}: ${value}"`);
84
+ }
85
+ }
86
+ } else if (mcp.config.type === "stdio") {
87
+ parts.push("--", mcp.config.command);
88
+ if (mcp.config.args) {
89
+ parts.push(...mcp.config.args);
90
+ }
91
+ if (mcp.config.env) {
92
+ for (const [key, value] of Object.entries(mcp.config.env)) {
93
+ parts.push("-e", `${key}=${value}`);
94
+ }
95
+ }
96
+ }
97
+ return {
98
+ mcpName: mcp.name,
99
+ command: parts.join(" "),
100
+ scope
101
+ };
102
+ }
103
+ function generateCommands(mcps, scopes) {
104
+ return mcps.map((mcp) => {
105
+ const scope = scopes.get(mcp.name) || "user";
106
+ return generateCommand(mcp, scope);
107
+ });
108
+ }
109
+ async function executeCommand(cmd, dryRun) {
110
+ if (dryRun) {
111
+ return {
112
+ mcpName: cmd.mcpName,
113
+ success: true,
114
+ message: `[dry-run] ${cmd.command}`,
115
+ skipped: false
116
+ };
117
+ }
118
+ try {
119
+ const { stdout } = await exec(cmd.command);
120
+ return {
121
+ mcpName: cmd.mcpName,
122
+ success: true,
123
+ message: stdout.trim() || `MCP ${cmd.mcpName} instalado com sucesso`,
124
+ skipped: false
125
+ };
126
+ } catch (error) {
127
+ if (error.stderr?.includes("already exists") || error.stderr?.includes("j\xE1 existe")) {
128
+ return {
129
+ mcpName: cmd.mcpName,
130
+ success: true,
131
+ message: `MCP ${cmd.mcpName} j\xE1 est\xE1 configurado (ignorado)`,
132
+ skipped: true
133
+ };
134
+ }
135
+ return {
136
+ mcpName: cmd.mcpName,
137
+ success: false,
138
+ message: error.stderr || error.message,
139
+ skipped: false
140
+ };
141
+ }
142
+ }
143
+ async function executeCommands(commands, dryRun) {
144
+ const results = [];
145
+ for (const cmd of commands) {
146
+ const result = await executeCommand(cmd, dryRun);
147
+ results.push(result);
148
+ }
149
+ return results;
150
+ }
151
+
152
+ export {
153
+ resolveScope,
154
+ resolveScopesForProfile,
155
+ getInstalledMcps,
156
+ filterAlreadyInstalled,
157
+ applyHeadlessMode,
158
+ generateCommand,
159
+ generateCommands,
160
+ executeCommand,
161
+ executeCommands
162
+ };
@@ -0,0 +1,169 @@
1
+ // src/validation/schemas.ts
2
+ import { z } from "zod";
3
+ var githubTokenSchema = z.string({
4
+ required_error: "GITHUB_TOKEN \xE9 obrigat\xF3rio"
5
+ }).regex(
6
+ /^(ghp_[a-zA-Z0-9]{36}|github_pat_[a-zA-Z0-9_]{82,})$/,
7
+ { message: "GITHUB_TOKEN deve come\xE7ar com ghp_ (classic) ou github_pat_ (fine-grained)" }
8
+ );
9
+ var hetznerTokenSchema = z.string({
10
+ required_error: "HETZNER_API_TOKEN \xE9 obrigat\xF3rio"
11
+ }).regex(
12
+ /^[a-zA-Z0-9]{64}$/,
13
+ { message: "HETZNER_API_TOKEN deve ter exatamente 64 caracteres alfanum\xE9ricos" }
14
+ );
15
+ var coolifyUrlSchema = z.string({
16
+ required_error: "COOLIFY_BASE_URL \xE9 obrigat\xF3rio"
17
+ }).regex(
18
+ /^https?:\/\/.+/,
19
+ { message: "COOLIFY_BASE_URL deve ser uma URL v\xE1lida (http:// ou https://)" }
20
+ );
21
+ var coolifyTokenSchema = z.string({
22
+ required_error: "COOLIFY_ACCESS_TOKEN \xE9 obrigat\xF3rio"
23
+ }).min(40, { message: "COOLIFY_ACCESS_TOKEN deve ter pelo menos 40 caracteres" });
24
+ var databaseUrlSchema = z.string({
25
+ required_error: "DATABASE_URL \xE9 obrigat\xF3rio"
26
+ }).regex(
27
+ /^postgres(ql)?:\/\/.+/,
28
+ { message: "DATABASE_URL deve come\xE7ar com postgres:// ou postgresql://" }
29
+ );
30
+ var stripeKeySchema = z.string({
31
+ required_error: "STRIPE_SECRET_KEY \xE9 obrigat\xF3rio"
32
+ }).refine(
33
+ (val) => !val.startsWith("sk_live_"),
34
+ { message: "PROIBIDO: Chave Stripe de produ\xE7\xE3o (sk_live_) detectada. Use apenas sk_test_ para seguran\xE7a." }
35
+ ).pipe(
36
+ z.string().regex(
37
+ /^sk_test_[a-zA-Z0-9]{24,}$/,
38
+ { message: "STRIPE_SECRET_KEY deve come\xE7ar com sk_test_ seguido de no m\xEDnimo 24 caracteres" }
39
+ )
40
+ );
41
+ var n8nUrlSchema = z.string({
42
+ required_error: "N8N_URL \xE9 obrigat\xF3rio"
43
+ }).regex(
44
+ /^https?:\/\/.+/,
45
+ { message: "N8N_URL deve ser uma URL v\xE1lida (http:// ou https://)" }
46
+ );
47
+ var n8nTokenSchema = z.string({
48
+ required_error: "N8N_MCP_TOKEN \xE9 obrigat\xF3rio"
49
+ }).min(1, { message: "N8N_MCP_TOKEN n\xE3o pode ser vazio" });
50
+ var stitchKeySchema = z.string({
51
+ required_error: "STITCH_API_KEY \xE9 obrigat\xF3rio"
52
+ }).min(1, { message: "STITCH_API_KEY n\xE3o pode ser vazio" });
53
+ var credentialSchemas = {
54
+ GITHUB_TOKEN: githubTokenSchema,
55
+ HETZNER_API_TOKEN: hetznerTokenSchema,
56
+ COOLIFY_BASE_URL: coolifyUrlSchema,
57
+ COOLIFY_ACCESS_TOKEN: coolifyTokenSchema,
58
+ DATABASE_URL: databaseUrlSchema,
59
+ STRIPE_SECRET_KEY: stripeKeySchema,
60
+ N8N_URL: n8nUrlSchema,
61
+ N8N_MCP_TOKEN: n8nTokenSchema,
62
+ STITCH_API_KEY: stitchKeySchema
63
+ };
64
+ var requiredVarsByTemplate = {
65
+ minimal: ["GITHUB_TOKEN"],
66
+ base: ["GITHUB_TOKEN", "STITCH_API_KEY", "COOLIFY_BASE_URL", "COOLIFY_ACCESS_TOKEN"],
67
+ saas: ["GITHUB_TOKEN", "STITCH_API_KEY", "COOLIFY_BASE_URL", "COOLIFY_ACCESS_TOKEN", "DATABASE_URL", "STRIPE_SECRET_KEY"],
68
+ agents: ["GITHUB_TOKEN", "STITCH_API_KEY", "COOLIFY_BASE_URL", "COOLIFY_ACCESS_TOKEN", "DATABASE_URL", "HETZNER_API_TOKEN", "N8N_URL", "N8N_MCP_TOKEN"],
69
+ full: ["GITHUB_TOKEN", "STITCH_API_KEY", "COOLIFY_BASE_URL", "COOLIFY_ACCESS_TOKEN", "HETZNER_API_TOKEN"]
70
+ };
71
+ function validateCredential(name, value) {
72
+ const schema = credentialSchemas[name];
73
+ if (!schema) {
74
+ return { valid: true, variable: name };
75
+ }
76
+ const result = schema.safeParse(value);
77
+ return {
78
+ valid: result.success,
79
+ variable: name,
80
+ error: result.success ? void 0 : result.error.errors[0]?.message
81
+ };
82
+ }
83
+ function validateAllCredentials(templateName, env) {
84
+ const required = requiredVarsByTemplate[templateName] || [];
85
+ return required.map((varName) => {
86
+ const value = env[varName];
87
+ if (!value) {
88
+ return {
89
+ valid: false,
90
+ variable: varName,
91
+ error: `${varName} \xE9 obrigat\xF3rio para o template "${templateName}"`
92
+ };
93
+ }
94
+ return validateCredential(varName, value);
95
+ });
96
+ }
97
+
98
+ // src/utils/env.ts
99
+ import { config as dotenvConfig } from "dotenv";
100
+ import { existsSync } from "fs";
101
+ import { resolve } from "path";
102
+ function loadEnv(envPath) {
103
+ const path = envPath || resolve(process.cwd(), ".env");
104
+ if (!existsSync(path)) {
105
+ return {
106
+ success: false,
107
+ vars: {},
108
+ path,
109
+ error: `Arquivo .env n\xE3o encontrado em ${path}. Copie .env.example para .env e preencha as credenciais.`
110
+ };
111
+ }
112
+ const result = dotenvConfig({ path });
113
+ if (result.error) {
114
+ return {
115
+ success: false,
116
+ vars: {},
117
+ path,
118
+ error: `Erro ao carregar .env: ${result.error.message}`
119
+ };
120
+ }
121
+ return {
122
+ success: true,
123
+ vars: result.parsed || {},
124
+ path
125
+ };
126
+ }
127
+ var varDescriptions = {
128
+ GITHUB_TOKEN: { description: "Token de acesso pessoal do GitHub (PAT)", mcpName: "github" },
129
+ STITCH_API_KEY: { description: "Chave de API do Google Stitch", mcpName: "stitch" },
130
+ HETZNER_API_TOKEN: { description: "Token de API do Hetzner Cloud", mcpName: "hetzner" },
131
+ COOLIFY_BASE_URL: { description: "URL base da inst\xE2ncia Coolify", mcpName: "coolify" },
132
+ COOLIFY_ACCESS_TOKEN: { description: "Token de acesso da API Coolify", mcpName: "coolify" },
133
+ DATABASE_URL: { description: "String de conex\xE3o PostgreSQL (DSN)", mcpName: "postgres" },
134
+ STRIPE_SECRET_KEY: { description: "Chave secreta do Stripe (apenas sk_test_)", mcpName: "stripe" },
135
+ N8N_URL: { description: "URL da inst\xE2ncia N8N", mcpName: "n8n-instance" },
136
+ N8N_MCP_TOKEN: { description: "Token MCP da inst\xE2ncia N8N", mcpName: "n8n-instance" }
137
+ };
138
+ function getRequiredVars(templateName) {
139
+ const varNames = requiredVarsByTemplate[templateName] || [];
140
+ return varNames.map((name) => ({
141
+ name,
142
+ description: varDescriptions[name]?.description || name,
143
+ mcpName: varDescriptions[name]?.mcpName || "desconhecido"
144
+ }));
145
+ }
146
+ function validatePresence(env, required) {
147
+ const present = [];
148
+ const missing = [];
149
+ for (const req of required) {
150
+ if (env[req.name] && env[req.name].trim() !== "") {
151
+ present.push(req.name);
152
+ } else {
153
+ missing.push(req);
154
+ }
155
+ }
156
+ return {
157
+ valid: missing.length === 0,
158
+ present,
159
+ missing
160
+ };
161
+ }
162
+
163
+ export {
164
+ validateCredential,
165
+ validateAllCredentials,
166
+ loadEnv,
167
+ getRequiredVars,
168
+ validatePresence
169
+ };
@@ -0,0 +1,28 @@
1
+ // src/utils/logger.ts
2
+ import chalk from "chalk";
3
+ import ora from "ora";
4
+ var noColor = process.argv.includes("--no-color");
5
+ var verbose = process.argv.includes("--verbose");
6
+ if (noColor) {
7
+ chalk.level = 0;
8
+ }
9
+ var logger = {
10
+ success: (msg) => console.log(chalk.green("\u2713"), msg),
11
+ error: (msg) => console.error(chalk.red("\u2717"), msg),
12
+ warn: (msg) => console.log(chalk.yellow("!"), msg),
13
+ info: (msg) => console.log(chalk.blue("\u2139"), msg),
14
+ debug: (msg) => {
15
+ if (verbose) console.log(chalk.gray("\u22EF"), msg);
16
+ },
17
+ spinner: (msg) => {
18
+ if (noColor) {
19
+ console.log(`... ${msg}`);
20
+ return ora({ text: msg, isEnabled: false });
21
+ }
22
+ return ora(msg).start();
23
+ }
24
+ };
25
+
26
+ export {
27
+ logger
28
+ };
@@ -0,0 +1,79 @@
1
+ // src/core/config-loader.ts
2
+ import { readFileSync, existsSync, readdirSync } from "fs";
3
+ import { resolve, dirname } from "path";
4
+ import { fileURLToPath } from "url";
5
+ function templatesDir() {
6
+ const fromCwd = resolve(process.cwd(), "templates");
7
+ if (existsSync(fromCwd)) return fromCwd;
8
+ const __filename2 = fileURLToPath(import.meta.url);
9
+ const __dirname2 = dirname(__filename2);
10
+ return resolve(__dirname2, "..", "templates");
11
+ }
12
+ function loadMcp(name) {
13
+ const filePath = resolve(templatesDir(), "mcps", `${name}.json`);
14
+ if (!existsSync(filePath)) {
15
+ throw new Error(`MCP "${name}" n\xE3o encontrado em templates/mcps/${name}.json`);
16
+ }
17
+ const content = readFileSync(filePath, "utf-8");
18
+ return JSON.parse(content);
19
+ }
20
+ function loadProfile(name) {
21
+ const customPaths = [
22
+ resolve(process.cwd(), ".mcp-setup", "profiles", `${name}.json`),
23
+ resolve(process.env.HOME || process.env.USERPROFILE || "", ".mcp-setup", "profiles", `${name}.json`)
24
+ ];
25
+ for (const customPath of customPaths) {
26
+ if (existsSync(customPath)) {
27
+ const content2 = readFileSync(customPath, "utf-8");
28
+ return JSON.parse(content2);
29
+ }
30
+ }
31
+ const filePath = resolve(templatesDir(), "profiles", `${name}.json`);
32
+ if (!existsSync(filePath)) {
33
+ const available = listProfiles().map((p) => p.name).join(", ");
34
+ throw new Error(`Profile "${name}" n\xE3o encontrado. Dispon\xEDveis: ${available}`);
35
+ }
36
+ const content = readFileSync(filePath, "utf-8");
37
+ return JSON.parse(content);
38
+ }
39
+ function loadScopeMap() {
40
+ const filePath = resolve(templatesDir(), "scopes", "scope-map.json");
41
+ const content = readFileSync(filePath, "utf-8");
42
+ return JSON.parse(content);
43
+ }
44
+ function listProfiles() {
45
+ const profilesDir = resolve(templatesDir(), "profiles");
46
+ const files = readdirSync(profilesDir).filter((f) => f.endsWith(".json"));
47
+ return files.map((f) => {
48
+ const content = readFileSync(resolve(profilesDir, f), "utf-8");
49
+ const profile = JSON.parse(content);
50
+ return {
51
+ name: profile.name,
52
+ displayName: profile.displayName,
53
+ description: profile.description,
54
+ mcpCount: profile.mcps.length
55
+ };
56
+ });
57
+ }
58
+ function listMcps() {
59
+ const mcpsDir = resolve(templatesDir(), "mcps");
60
+ const files = readdirSync(mcpsDir).filter((f) => f.endsWith(".json"));
61
+ return files.map((f) => {
62
+ const content = readFileSync(resolve(mcpsDir, f), "utf-8");
63
+ const mcp = JSON.parse(content);
64
+ return {
65
+ name: mcp.name,
66
+ displayName: mcp.displayName,
67
+ category: mcp.category,
68
+ authType: mcp.auth.type
69
+ };
70
+ });
71
+ }
72
+
73
+ export {
74
+ loadMcp,
75
+ loadProfile,
76
+ loadScopeMap,
77
+ listProfiles,
78
+ listMcps
79
+ };
@@ -0,0 +1,14 @@
1
+ import {
2
+ listMcps,
3
+ listProfiles,
4
+ loadMcp,
5
+ loadProfile,
6
+ loadScopeMap
7
+ } from "./chunk-UZ72QCYF.js";
8
+ export {
9
+ listMcps,
10
+ listProfiles,
11
+ loadMcp,
12
+ loadProfile,
13
+ loadScopeMap
14
+ };