@atehra/mcp 0.1.0 → 0.2.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/README.md CHANGED
@@ -4,6 +4,29 @@ Servidor MCP (Model Context Protocol) oficial da Atehra. Permite que Claude, Cur
4
4
 
5
5
  > **A primeira fintech brasileira projetada pra rodar via agente de IA.**
6
6
 
7
+ ## Dois jeitos de usar
8
+
9
+ | Jeito | Quando usar | Como |
10
+ |---|---|---|
11
+ | **Remoto (HTTP/SSE)** | Maioria dos casos. Sem instalar nada. | Cola URL na UI dos Connectors do Claude |
12
+ | **Local (stdio)** | Self-hosting ou ambientes sem internet | `npx -y @atehra/mcp` via config JSON |
13
+
14
+ ## Uso remoto (recomendado) — UI dos Connectors
15
+
16
+ No Claude Desktop, vai em **Settings → Connectors → Adicionar conector personalizado** e cola:
17
+
18
+ ```
19
+ URL: https://atehra-mcp-production.up.railway.app/mcp
20
+ ```
21
+
22
+ Em **Configurações avançadas**, adiciona header:
23
+
24
+ ```
25
+ x-api-key: atk_test_SUA_CHAVE_AQUI
26
+ ```
27
+
28
+ Salva. Pronto — 16 ferramentas Atehra disponíveis no Claude.
29
+
7
30
  ## O que dá pra fazer
8
31
 
9
32
  Com este servidor ligado, seu agente de IA pode:
@@ -17,12 +40,13 @@ Com este servidor ligado, seu agente de IA pode:
17
40
  - **Ver métricas do negócio** (receita recorrente mensal, taxa de cancelamento, valor por cliente)
18
41
  - **Listar clientes, assinaturas e faturas** com filtros
19
42
 
20
- ## Instalação
43
+ ## Uso local (stdio)
21
44
 
22
45
  ### Pré-requisitos
23
46
 
24
47
  1. Conta na Atehra (`atehra.com`)
25
48
  2. API key (`atk_test_...` pra sandbox, `atk_live_...` pra produção)
49
+ 3. Node.js 18+
26
50
 
27
51
  ### No Claude Desktop
28
52
 
package/dist/env.d.ts CHANGED
@@ -1,11 +1,16 @@
1
1
  /**
2
- * e valida variáveis de ambiente.
2
+ * Configuração do servidor MCP.
3
3
  *
4
- * ATEHRA_API_KEY (obrigatória) chave da API. Prefixo determina o modo:
4
+ * Duas formas de criar:
5
+ *
6
+ * 1. loadEnv() — lê do process.env (uso stdio: ATEHRA_API_KEY + ATEHRA_BASE_URL)
7
+ *
8
+ * 2. envFromApiKey(apiKey, baseUrl?) — constrói a partir de uma chave passada
9
+ * em runtime (uso HTTP: chave vem do header da requisição)
10
+ *
11
+ * Em ambos os casos, o modo (sandbox vs produção) é detectado pelo prefixo da chave:
5
12
  * - atk_test_... → sandbox
6
13
  * - atk_live_... → produção
7
- *
8
- * ATEHRA_BASE_URL (opcional) — URL base da API. Default: https://api.atehra.com
9
14
  */
10
15
  export type Mode = "test" | "live";
11
16
  export interface Env {
@@ -13,4 +18,6 @@ export interface Env {
13
18
  baseUrl: string;
14
19
  mode: Mode;
15
20
  }
21
+ export declare function detectMode(apiKey: string): Mode;
22
+ export declare function envFromApiKey(apiKey: string, baseUrl?: string): Env;
16
23
  export declare function loadEnv(): Env;
package/dist/env.js CHANGED
@@ -1,24 +1,34 @@
1
1
  /**
2
- * e valida variáveis de ambiente.
2
+ * Configuração do servidor MCP.
3
3
  *
4
- * ATEHRA_API_KEY (obrigatória) chave da API. Prefixo determina o modo:
4
+ * Duas formas de criar:
5
+ *
6
+ * 1. loadEnv() — lê do process.env (uso stdio: ATEHRA_API_KEY + ATEHRA_BASE_URL)
7
+ *
8
+ * 2. envFromApiKey(apiKey, baseUrl?) — constrói a partir de uma chave passada
9
+ * em runtime (uso HTTP: chave vem do header da requisição)
10
+ *
11
+ * Em ambos os casos, o modo (sandbox vs produção) é detectado pelo prefixo da chave:
5
12
  * - atk_test_... → sandbox
6
13
  * - atk_live_... → produção
7
- *
8
- * ATEHRA_BASE_URL (opcional) — URL base da API. Default: https://api.atehra.com
9
14
  */
15
+ const DEFAULT_BASE_URL = "https://api.atehra.com";
16
+ export function detectMode(apiKey) {
17
+ if (apiKey.startsWith("atk_live_"))
18
+ return "live";
19
+ if (apiKey.startsWith("atk_test_"))
20
+ return "test";
21
+ throw new Error("Chave Atehra com prefixo inválido. Use atk_test_... (sandbox) ou atk_live_... (produção).");
22
+ }
23
+ export function envFromApiKey(apiKey, baseUrl) {
24
+ const mode = detectMode(apiKey);
25
+ const resolvedBaseUrl = (baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
26
+ return { apiKey, baseUrl: resolvedBaseUrl, mode };
27
+ }
10
28
  export function loadEnv() {
11
29
  const apiKey = process.env["ATEHRA_API_KEY"];
12
30
  if (!apiKey) {
13
31
  throw new Error("ATEHRA_API_KEY não definida. Gere uma chave em atehra.com e defina no ambiente.");
14
32
  }
15
- const mode = apiKey.startsWith("atk_live_")
16
- ? "live"
17
- : apiKey.startsWith("atk_test_")
18
- ? "test"
19
- : (() => {
20
- throw new Error("ATEHRA_API_KEY tem prefixo inválido. Use atk_test_... (sandbox) ou atk_live_... (produção).");
21
- })();
22
- const baseUrl = (process.env["ATEHRA_BASE_URL"] ?? "https://api.atehra.com").replace(/\/+$/, "");
23
- return { apiKey, baseUrl, mode };
33
+ return envFromApiKey(apiKey, process.env["ATEHRA_BASE_URL"]);
24
34
  }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Handler HTTP do MCP — adapta uma requisição HTTP em uma sessão MCP.
3
+ *
4
+ * Usa o transporte Streamable HTTP do SDK oficial (modo stateless — cada
5
+ * requisição cria seu próprio servidor MCP). Isso simplifica deploy em
6
+ * ambientes serverless (Vercel, Cloudflare Workers, etc.) onde não há
7
+ * estado persistente entre invocações.
8
+ *
9
+ * Auth: lê a chave Atehra do header `x-api-key` ou `authorization: Bearer ...`.
10
+ * Sem chave, responde 401.
11
+ */
12
+ import type { IncomingMessage, ServerResponse } from "node:http";
13
+ export interface HttpHandlerOptions {
14
+ /** URL base da API Atehra. Default: https://api.atehra.com */
15
+ baseUrl?: string;
16
+ }
17
+ /**
18
+ * Handler principal. Use em qualquer framework HTTP Node.js
19
+ * (express, fastify, vercel function, raw http server).
20
+ *
21
+ * Espera o body já parseado em opts.body (pra POST). Pra GET (SSE), passa null.
22
+ */
23
+ export declare function handleMcpRequest(req: IncomingMessage, res: ServerResponse, body: unknown, options?: HttpHandlerOptions): Promise<void>;
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Handler HTTP do MCP — adapta uma requisição HTTP em uma sessão MCP.
3
+ *
4
+ * Usa o transporte Streamable HTTP do SDK oficial (modo stateless — cada
5
+ * requisição cria seu próprio servidor MCP). Isso simplifica deploy em
6
+ * ambientes serverless (Vercel, Cloudflare Workers, etc.) onde não há
7
+ * estado persistente entre invocações.
8
+ *
9
+ * Auth: lê a chave Atehra do header `x-api-key` ou `authorization: Bearer ...`.
10
+ * Sem chave, responde 401.
11
+ */
12
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
13
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
14
+ import { envFromApiKey } from "./env.js";
15
+ import { AtehraClient } from "./client.js";
16
+ import { registerAllTools } from "./register-tools.js";
17
+ /**
18
+ * Lê a chave Atehra dos headers da requisição.
19
+ * Aceita `x-api-key: atk_xxx` OU `authorization: Bearer atk_xxx`.
20
+ */
21
+ function extractApiKey(req) {
22
+ const xApiKey = req.headers["x-api-key"];
23
+ if (typeof xApiKey === "string" && xApiKey.length > 0)
24
+ return xApiKey;
25
+ if (Array.isArray(xApiKey) && xApiKey[0])
26
+ return xApiKey[0];
27
+ const auth = req.headers["authorization"];
28
+ if (typeof auth === "string" && auth.toLowerCase().startsWith("bearer ")) {
29
+ return auth.slice(7).trim();
30
+ }
31
+ return null;
32
+ }
33
+ /**
34
+ * Handler principal. Use em qualquer framework HTTP Node.js
35
+ * (express, fastify, vercel function, raw http server).
36
+ *
37
+ * Espera o body já parseado em opts.body (pra POST). Pra GET (SSE), passa null.
38
+ */
39
+ export async function handleMcpRequest(req, res, body, options = {}) {
40
+ const apiKey = extractApiKey(req);
41
+ if (!apiKey) {
42
+ res.statusCode = 401;
43
+ res.setHeader("content-type", "application/json");
44
+ res.end(JSON.stringify({
45
+ error: "missing_api_key",
46
+ message: "Passe a chave Atehra no header 'x-api-key' ou em 'authorization: Bearer atk_...'.",
47
+ }));
48
+ return;
49
+ }
50
+ let env;
51
+ try {
52
+ env = envFromApiKey(apiKey, options.baseUrl);
53
+ }
54
+ catch (err) {
55
+ res.statusCode = 401;
56
+ res.setHeader("content-type", "application/json");
57
+ res.end(JSON.stringify({
58
+ error: "invalid_api_key",
59
+ message: err instanceof Error ? err.message : String(err),
60
+ }));
61
+ return;
62
+ }
63
+ const client = new AtehraClient(env);
64
+ const server = new McpServer({ name: "atehra", version: "0.2.0" });
65
+ registerAllTools(server, client, env);
66
+ // Modo stateless: cada requisição cria seu próprio servidor + transporte.
67
+ // sessionIdGenerator: undefined desliga sessão persistente (compatível com serverless).
68
+ const transport = new StreamableHTTPServerTransport({
69
+ sessionIdGenerator: undefined,
70
+ });
71
+ // Limpa quando a conexão fecha (importante em serverless)
72
+ res.on("close", () => {
73
+ void transport.close();
74
+ void server.close();
75
+ });
76
+ await server.connect(transport);
77
+ await transport.handleRequest(req, res, body);
78
+ }
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Entry point HTTP standalone — sobe um servidor HTTP escutando em PORT (default 3000)
4
+ * que aceita requisições MCP em /mcp.
5
+ *
6
+ * Pra uso local (testar antes de deploy) ou self-hosting (rodar em VPS/Railway/etc).
7
+ *
8
+ * Pra deploy serverless (Vercel), use api/mcp.ts em vez disso.
9
+ *
10
+ * Variáveis de ambiente:
11
+ * PORT — porta do servidor (default: 3000)
12
+ * ATEHRA_BASE_URL — URL da API Atehra (default: https://api.atehra.com)
13
+ */
14
+ export {};
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Entry point HTTP standalone — sobe um servidor HTTP escutando em PORT (default 3000)
4
+ * que aceita requisições MCP em /mcp.
5
+ *
6
+ * Pra uso local (testar antes de deploy) ou self-hosting (rodar em VPS/Railway/etc).
7
+ *
8
+ * Pra deploy serverless (Vercel), use api/mcp.ts em vez disso.
9
+ *
10
+ * Variáveis de ambiente:
11
+ * PORT — porta do servidor (default: 3000)
12
+ * ATEHRA_BASE_URL — URL da API Atehra (default: https://api.atehra.com)
13
+ */
14
+ import { createServer } from "node:http";
15
+ import { handleMcpRequest } from "./http-handler.js";
16
+ const PORT = Number.parseInt(process.env["PORT"] ?? "3000", 10);
17
+ const BASE_URL = process.env["ATEHRA_BASE_URL"];
18
+ async function readBody(req) {
19
+ return new Promise((resolve, reject) => {
20
+ const chunks = [];
21
+ req.on("data", (chunk) => chunks.push(chunk));
22
+ req.on("end", () => {
23
+ const raw = Buffer.concat(chunks).toString("utf8");
24
+ if (!raw)
25
+ return resolve(null);
26
+ try {
27
+ resolve(JSON.parse(raw));
28
+ }
29
+ catch (err) {
30
+ reject(err);
31
+ }
32
+ });
33
+ req.on("error", reject);
34
+ });
35
+ }
36
+ const server = createServer(async (req, res) => {
37
+ // CORS pra cliente MCP web
38
+ res.setHeader("access-control-allow-origin", "*");
39
+ res.setHeader("access-control-allow-methods", "GET, POST, OPTIONS");
40
+ res.setHeader("access-control-allow-headers", "content-type, x-api-key, authorization, mcp-session-id, mcp-protocol-version");
41
+ res.setHeader("access-control-expose-headers", "mcp-session-id");
42
+ if (req.method === "OPTIONS") {
43
+ res.statusCode = 204;
44
+ res.end();
45
+ return;
46
+ }
47
+ // Health check
48
+ if (req.url === "/health" || req.url === "/") {
49
+ res.statusCode = 200;
50
+ res.setHeader("content-type", "application/json");
51
+ res.end(JSON.stringify({
52
+ name: "atehra-mcp",
53
+ version: "0.2.0",
54
+ status: "ok",
55
+ usage: "POST /mcp com header x-api-key: atk_test_... ou atk_live_...",
56
+ }));
57
+ return;
58
+ }
59
+ // MCP endpoint
60
+ if (req.url === "/mcp" || req.url?.startsWith("/mcp?")) {
61
+ try {
62
+ const body = req.method === "POST" ? await readBody(req) : null;
63
+ await handleMcpRequest(req, res, body, { baseUrl: BASE_URL });
64
+ }
65
+ catch (err) {
66
+ if (!res.headersSent) {
67
+ res.statusCode = 500;
68
+ res.setHeader("content-type", "application/json");
69
+ res.end(JSON.stringify({
70
+ error: "internal_error",
71
+ message: err instanceof Error ? err.message : String(err),
72
+ }));
73
+ }
74
+ }
75
+ return;
76
+ }
77
+ // 404
78
+ res.statusCode = 404;
79
+ res.setHeader("content-type", "application/json");
80
+ res.end(JSON.stringify({ error: "not_found" }));
81
+ });
82
+ server.listen(PORT, () => {
83
+ process.stderr.write(`[atehra-mcp-http] escutando em http://localhost:${PORT}/mcp\n`);
84
+ });
package/dist/index.d.ts CHANGED
@@ -1,17 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * @atehra/mcp — Servidor MCP oficial da Atehra
3
+ * @atehra/mcp — Servidor MCP oficial da Atehra (transporte stdio)
4
4
  *
5
- * Sobe um servidor MCP via stdio e registra as 16 ferramentas da V1:
5
+ * Pra uso local via npx ou config do Claude Desktop. Lê a chave da API
6
+ * da variável ATEHRA_API_KEY e roda como subprocess.
6
7
  *
7
- * Saldo (5): get_balance, list_anticipatable_payments, quote_anticipation,
8
- * request_anticipation, request_withdrawal
9
- *
10
- * Cobrar (6): create_customer, list_customers, create_plan,
11
- * create_checkout_session, create_checkout_link, list_recent_orders
12
- *
13
- * Números (3): get_metrics_overview, list_subscriptions, list_invoices
14
- *
15
- * Régua (2): get_dunning_config, update_dunning_config
8
+ * Pra uso remoto (via UI dos Connectors do Claude), veja src/index-http.ts.
16
9
  */
17
10
  export {};
package/dist/index.js CHANGED
@@ -1,47 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * @atehra/mcp — Servidor MCP oficial da Atehra
3
+ * @atehra/mcp — Servidor MCP oficial da Atehra (transporte stdio)
4
4
  *
5
- * Sobe um servidor MCP via stdio e registra as 16 ferramentas da V1:
5
+ * Pra uso local via npx ou config do Claude Desktop. Lê a chave da API
6
+ * da variável ATEHRA_API_KEY e roda como subprocess.
6
7
  *
7
- * Saldo (5): get_balance, list_anticipatable_payments, quote_anticipation,
8
- * request_anticipation, request_withdrawal
9
- *
10
- * Cobrar (6): create_customer, list_customers, create_plan,
11
- * create_checkout_session, create_checkout_link, list_recent_orders
12
- *
13
- * Números (3): get_metrics_overview, list_subscriptions, list_invoices
14
- *
15
- * Régua (2): get_dunning_config, update_dunning_config
8
+ * Pra uso remoto (via UI dos Connectors do Claude), veja src/index-http.ts.
16
9
  */
17
10
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
18
11
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
19
12
  import { loadEnv } from "./env.js";
20
13
  import { AtehraClient } from "./client.js";
21
- import { registerBalanceTools } from "./tools/balance.js";
22
- import { registerCustomerTools } from "./tools/customers.js";
23
- import { registerPlanTools } from "./tools/plans.js";
24
- import { registerChargeTools } from "./tools/charges.js";
25
- import { registerMetricsTools } from "./tools/metrics.js";
26
- import { registerBillingTools } from "./tools/billing.js";
27
- import { registerDunningTools } from "./tools/dunning.js";
14
+ import { registerAllTools } from "./register-tools.js";
28
15
  async function main() {
29
16
  const env = loadEnv();
30
17
  const client = new AtehraClient(env);
31
18
  const server = new McpServer({
32
19
  name: "atehra",
33
- version: "0.1.0",
20
+ version: "0.2.0",
34
21
  });
35
- // Registra todas as 16 ferramentas
36
- registerBalanceTools(server, client, env);
37
- registerCustomerTools(server, client);
38
- registerPlanTools(server, client);
39
- registerChargeTools(server, client);
40
- registerMetricsTools(server, client);
41
- registerBillingTools(server, client);
42
- registerDunningTools(server, client);
43
- // Log de boot vai pra stderr (stdout é reservado pro protocolo MCP)
44
- process.stderr.write(`[atehra-mcp] iniciado. Modo: ${env.mode}. Base URL: ${env.baseUrl}\n`);
22
+ registerAllTools(server, client, env);
23
+ process.stderr.write(`[atehra-mcp] iniciado (stdio). Modo: ${env.mode}. Base URL: ${env.baseUrl}\n`);
45
24
  const transport = new StdioServerTransport();
46
25
  await server.connect(transport);
47
26
  }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Registro centralizado das 16 ferramentas Atehra.
3
+ *
4
+ * Compartilhado entre o transporte stdio (uso local via npx) e o transporte
5
+ * HTTP/SSE (uso remoto via UI Connectors do Claude). Mantém a lista de tools
6
+ * em um único lugar — toda nova feature aparece nos dois transportes ao mesmo tempo.
7
+ */
8
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
+ import type { AtehraClient } from "./client.js";
10
+ import type { Env } from "./env.js";
11
+ export declare function registerAllTools(server: McpServer, client: AtehraClient, env: Env): void;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Registro centralizado das 16 ferramentas Atehra.
3
+ *
4
+ * Compartilhado entre o transporte stdio (uso local via npx) e o transporte
5
+ * HTTP/SSE (uso remoto via UI Connectors do Claude). Mantém a lista de tools
6
+ * em um único lugar — toda nova feature aparece nos dois transportes ao mesmo tempo.
7
+ */
8
+ import { registerBalanceTools } from "./tools/balance.js";
9
+ import { registerCustomerTools } from "./tools/customers.js";
10
+ import { registerPlanTools } from "./tools/plans.js";
11
+ import { registerChargeTools } from "./tools/charges.js";
12
+ import { registerMetricsTools } from "./tools/metrics.js";
13
+ import { registerBillingTools } from "./tools/billing.js";
14
+ import { registerDunningTools } from "./tools/dunning.js";
15
+ export function registerAllTools(server, client, env) {
16
+ registerBalanceTools(server, client, env);
17
+ registerCustomerTools(server, client);
18
+ registerPlanTools(server, client);
19
+ registerChargeTools(server, client);
20
+ registerMetricsTools(server, client);
21
+ registerBillingTools(server, client);
22
+ registerDunningTools(server, client);
23
+ }
package/package.json CHANGED
@@ -1,20 +1,22 @@
1
1
  {
2
2
  "name": "@atehra/mcp",
3
- "version": "0.1.0",
4
- "description": "Servidor MCP oficial da Atehra — opere a infraestrutura financeira BR via Claude, Cursor, ChatGPT e qualquer cliente MCP",
3
+ "version": "0.2.0",
4
+ "description": "Servidor MCP oficial da Atehra — opere a infraestrutura financeira BR via Claude, Cursor, ChatGPT e qualquer cliente MCP. Suporta stdio (local) e HTTP/SSE (remoto, via Connectors UI do Claude).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
8
- "atehra-mcp": "dist/index.js"
8
+ "atehra-mcp": "dist/index.js",
9
+ "atehra-mcp-http": "dist/index-http.js"
9
10
  },
10
11
  "files": [
11
12
  "dist",
12
13
  "README.md"
13
14
  ],
14
15
  "scripts": {
15
- "build": "tsc",
16
+ "build": "tsc && chmod +x dist/index.js dist/index-http.js",
16
17
  "dev": "tsc --watch",
17
- "start": "node dist/index.js",
18
+ "start": "node dist/index-http.js",
19
+ "start:stdio": "node dist/index.js",
18
20
  "smoke": "node --test dist/tests/smoke.test.js",
19
21
  "prepublishOnly": "npm run build"
20
22
  },