ponch-mcp-server 1.0.1

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/dist/auth.d.ts ADDED
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Auth Resolver — Detecta Modo A (Cowork/service account) o Modo B (tenant/credentials)
3
+ *
4
+ * Modo A: FIREBASE_SERVICE_ACCOUNT env var apunta al service account JSON
5
+ * → Admin SDK, acceso a TODOS los tenants
6
+ * → set_context selecciona tenant/brand en cada sesion
7
+ *
8
+ * Modo B: ~/.ponch/credentials.json tiene Firebase Auth token del tenant
9
+ * → Client SDK, solo SU tenant
10
+ * → tenantId viene del token guardado
11
+ * → connect_account intercambia codigo por custom token
12
+ */
13
+ export type AuthMode = 'cowork' | 'tenant';
14
+ export interface AuthContext {
15
+ mode: AuthMode;
16
+ tenantId: string | null;
17
+ brandId: string | null;
18
+ brands: Array<{
19
+ id: string;
20
+ nombre: string;
21
+ }>;
22
+ userId: string | null;
23
+ userName: string | null;
24
+ rol: string | null;
25
+ serviceAccountPath: string | null;
26
+ credentialsPath: string | null;
27
+ }
28
+ export interface SessionContext {
29
+ tenantId: string | null;
30
+ brandId: string | null;
31
+ }
32
+ export interface StoredCredentials {
33
+ tenantId: string;
34
+ userId: string;
35
+ userName: string | null;
36
+ rol: string | null;
37
+ brands: Array<{
38
+ id: string;
39
+ nombre: string;
40
+ }>;
41
+ customToken: string;
42
+ idToken: string | null;
43
+ refreshToken: string | null;
44
+ connectedAt: string;
45
+ }
46
+ export declare const CREDENTIALS_DIR: string;
47
+ export declare const CREDENTIALS_PATH: string;
48
+ export declare const FIREBASE_CONFIG: {
49
+ apiKey: string;
50
+ authDomain: string;
51
+ projectId: string;
52
+ storageBucket: string;
53
+ messagingSenderId: string;
54
+ appId: string;
55
+ };
56
+ /**
57
+ * Resuelve el modo de auth al arrancar el MCP.
58
+ * 1. Busca FIREBASE_SERVICE_ACCOUNT en env → Modo A
59
+ * 2. Busca ~/.ponch/credentials.json → Modo B
60
+ * 3. Si ninguno → Modo B sin conectar (necesita connect_account)
61
+ */
62
+ export declare function resolveAuth(): AuthContext;
63
+ /**
64
+ * Guarda credentials en ~/.ponch/credentials.json
65
+ * Permisos 600 (solo el dueño lee)
66
+ */
67
+ export declare function saveCredentials(creds: StoredCredentials): void;
68
+ /**
69
+ * Lee credentials guardadas
70
+ */
71
+ export declare function readCredentials(): StoredCredentials | null;
72
+ /**
73
+ * Contexto de sesion mutable — se actualiza con set_context (Modo A)
74
+ * o se fija desde credentials (Modo B)
75
+ */
76
+ export declare class Session {
77
+ private context;
78
+ private _authContext;
79
+ constructor(auth: AuthContext);
80
+ get mode(): AuthMode;
81
+ get tenantId(): string | null;
82
+ get brandId(): string | null;
83
+ get serviceAccountPath(): string | null;
84
+ get brands(): Array<{
85
+ id: string;
86
+ nombre: string;
87
+ }>;
88
+ get userName(): string | null;
89
+ setContext(tenantId: string, brandId: string): void;
90
+ /**
91
+ * Actualiza el contexto despues de connect_account (Modo B)
92
+ */
93
+ setTenantContext(tenantId: string, brandId: string, brands: Array<{
94
+ id: string;
95
+ nombre: string;
96
+ }>, userId: string, userName: string | null): void;
97
+ requireTenant(): string;
98
+ requireBrand(): string;
99
+ }
100
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE3C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9C,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,MAAM,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,eAAe,QAA4B,CAAC;AACzD,eAAO,MAAM,gBAAgB,QAA4C,CAAC;AAG1E,eAAO,MAAM,eAAe;;;;;;;CAO3B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,WAAW,IAAI,WAAW,CAqDzC;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAK9D;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,iBAAiB,GAAG,IAAI,CAO1D;AAED;;;GAGG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,YAAY,CAAc;gBAEtB,IAAI,EAAE,WAAW;IAQ7B,IAAI,IAAI,IAAI,QAAQ,CAEnB;IAED,IAAI,QAAQ,IAAI,MAAM,GAAG,IAAI,CAE5B;IAED,IAAI,OAAO,IAAI,MAAM,GAAG,IAAI,CAE3B;IAED,IAAI,kBAAkB,IAAI,MAAM,GAAG,IAAI,CAEtC;IAED,IAAI,MAAM,IAAI,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAElD;IAED,IAAI,QAAQ,IAAI,MAAM,GAAG,IAAI,CAE5B;IAED,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAQnD;;OAEG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAUjJ,aAAa,IAAI,MAAM;IAWvB,YAAY,IAAI,MAAM;CAMvB"}
package/dist/auth.js ADDED
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Auth Resolver — Detecta Modo A (Cowork/service account) o Modo B (tenant/credentials)
3
+ *
4
+ * Modo A: FIREBASE_SERVICE_ACCOUNT env var apunta al service account JSON
5
+ * → Admin SDK, acceso a TODOS los tenants
6
+ * → set_context selecciona tenant/brand en cada sesion
7
+ *
8
+ * Modo B: ~/.ponch/credentials.json tiene Firebase Auth token del tenant
9
+ * → Client SDK, solo SU tenant
10
+ * → tenantId viene del token guardado
11
+ * → connect_account intercambia codigo por custom token
12
+ */
13
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
14
+ import { join } from 'path';
15
+ import { homedir } from 'os';
16
+ export const CREDENTIALS_DIR = join(homedir(), '.ponch');
17
+ export const CREDENTIALS_PATH = join(CREDENTIALS_DIR, 'credentials.json');
18
+ // Firebase project config (hardcoded — mismo proyecto para todos)
19
+ export const FIREBASE_CONFIG = {
20
+ apiKey: 'AIzaSyCe9kCNhi0nFzFEXTsEUmz9UD5AmiN-apE',
21
+ authDomain: 'atteyo-ops.firebaseapp.com',
22
+ projectId: 'atteyo-ops',
23
+ storageBucket: 'atteyo-ops.firebasestorage.app',
24
+ messagingSenderId: '481353219532',
25
+ appId: '1:481353219532:web:66f5d26a4efdb589baf169',
26
+ };
27
+ /**
28
+ * Resuelve el modo de auth al arrancar el MCP.
29
+ * 1. Busca FIREBASE_SERVICE_ACCOUNT en env → Modo A
30
+ * 2. Busca ~/.ponch/credentials.json → Modo B
31
+ * 3. Si ninguno → Modo B sin conectar (necesita connect_account)
32
+ */
33
+ export function resolveAuth() {
34
+ // Modo A: service account
35
+ const saPath = process.env.FIREBASE_SERVICE_ACCOUNT;
36
+ if (saPath) {
37
+ const resolvedPath = saPath.startsWith('/') ? saPath : join(process.cwd(), saPath);
38
+ if (existsSync(resolvedPath)) {
39
+ return {
40
+ mode: 'cowork',
41
+ tenantId: null,
42
+ brandId: null,
43
+ brands: [],
44
+ userId: null,
45
+ userName: null,
46
+ rol: 'super_admin', // service account = admin total
47
+ serviceAccountPath: resolvedPath,
48
+ credentialsPath: null,
49
+ };
50
+ }
51
+ console.error(`[auth] FIREBASE_SERVICE_ACCOUNT apunta a ${resolvedPath} pero no existe`);
52
+ }
53
+ // Modo B: credentials locales del tenant
54
+ if (existsSync(CREDENTIALS_PATH)) {
55
+ try {
56
+ const creds = JSON.parse(readFileSync(CREDENTIALS_PATH, 'utf-8'));
57
+ return {
58
+ mode: creds.rol === 'super_admin' ? 'cowork' : 'tenant',
59
+ tenantId: creds.tenantId ?? null,
60
+ brandId: creds.brands?.[0]?.id ?? null,
61
+ brands: creds.brands ?? [],
62
+ userId: creds.userId ?? null,
63
+ userName: creds.userName ?? null,
64
+ rol: creds.rol ?? null,
65
+ serviceAccountPath: null,
66
+ credentialsPath: CREDENTIALS_PATH,
67
+ };
68
+ }
69
+ catch {
70
+ console.error('[auth] Error leyendo credentials.json');
71
+ }
72
+ }
73
+ // Modo B sin conectar
74
+ return {
75
+ mode: 'tenant',
76
+ tenantId: null,
77
+ brandId: null,
78
+ brands: [],
79
+ userId: null,
80
+ userName: null,
81
+ rol: null,
82
+ serviceAccountPath: null,
83
+ credentialsPath: null,
84
+ };
85
+ }
86
+ /**
87
+ * Guarda credentials en ~/.ponch/credentials.json
88
+ * Permisos 600 (solo el dueño lee)
89
+ */
90
+ export function saveCredentials(creds) {
91
+ if (!existsSync(CREDENTIALS_DIR)) {
92
+ mkdirSync(CREDENTIALS_DIR, { recursive: true, mode: 0o700 });
93
+ }
94
+ writeFileSync(CREDENTIALS_PATH, JSON.stringify(creds, null, 2), { mode: 0o600 });
95
+ }
96
+ /**
97
+ * Lee credentials guardadas
98
+ */
99
+ export function readCredentials() {
100
+ if (!existsSync(CREDENTIALS_PATH))
101
+ return null;
102
+ try {
103
+ return JSON.parse(readFileSync(CREDENTIALS_PATH, 'utf-8'));
104
+ }
105
+ catch {
106
+ return null;
107
+ }
108
+ }
109
+ /**
110
+ * Contexto de sesion mutable — se actualiza con set_context (Modo A)
111
+ * o se fija desde credentials (Modo B)
112
+ */
113
+ export class Session {
114
+ context;
115
+ _authContext;
116
+ constructor(auth) {
117
+ this._authContext = auth;
118
+ this.context = {
119
+ tenantId: auth.tenantId,
120
+ brandId: auth.brandId,
121
+ };
122
+ }
123
+ get mode() {
124
+ return this._authContext.mode;
125
+ }
126
+ get tenantId() {
127
+ return this.context.tenantId;
128
+ }
129
+ get brandId() {
130
+ return this.context.brandId;
131
+ }
132
+ get serviceAccountPath() {
133
+ return this._authContext.serviceAccountPath;
134
+ }
135
+ get brands() {
136
+ return this._authContext.brands;
137
+ }
138
+ get userName() {
139
+ return this._authContext.userName;
140
+ }
141
+ setContext(tenantId, brandId) {
142
+ if (this._authContext.mode !== 'cowork') {
143
+ throw new Error('set_context solo disponible en Modo Cowork');
144
+ }
145
+ this.context.tenantId = tenantId;
146
+ this.context.brandId = brandId;
147
+ }
148
+ /**
149
+ * Actualiza el contexto despues de connect_account (Modo B)
150
+ */
151
+ setTenantContext(tenantId, brandId, brands, userId, userName) {
152
+ this.context.tenantId = tenantId;
153
+ this.context.brandId = brandId;
154
+ this._authContext.tenantId = tenantId;
155
+ this._authContext.brandId = brandId;
156
+ this._authContext.brands = brands;
157
+ this._authContext.userId = userId;
158
+ this._authContext.userName = userName;
159
+ }
160
+ requireTenant() {
161
+ if (!this.context.tenantId) {
162
+ throw new Error(this._authContext.mode === 'cowork'
163
+ ? 'Usa set_context para seleccionar un tenant primero'
164
+ : 'No conectado. Usa connect_account para conectar tu cuenta de Ponch');
165
+ }
166
+ return this.context.tenantId;
167
+ }
168
+ requireBrand() {
169
+ if (!this.context.brandId) {
170
+ throw new Error('No hay brand seleccionada. Especifica brandId o usa set_context');
171
+ }
172
+ return this.context.brandId;
173
+ }
174
+ }
175
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,IAAI,EAAW,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAiC7B,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;AACzD,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC,eAAe,EAAE,kBAAkB,CAAC,CAAC;AAE1E,kEAAkE;AAClE,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,MAAM,EAAE,yCAAyC;IACjD,UAAU,EAAE,4BAA4B;IACxC,SAAS,EAAE,YAAY;IACvB,aAAa,EAAE,gCAAgC;IAC/C,iBAAiB,EAAE,cAAc;IACjC,KAAK,EAAE,2CAA2C;CACnD,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,WAAW;IACzB,0BAA0B;IAC1B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC;IACpD,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,YAAY,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC;QACnF,IAAI,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YAC7B,OAAO;gBACL,IAAI,EAAE,QAAQ;gBACd,QAAQ,EAAE,IAAI;gBACd,OAAO,EAAE,IAAI;gBACb,MAAM,EAAE,EAAE;gBACV,MAAM,EAAE,IAAI;gBACZ,QAAQ,EAAE,IAAI;gBACd,GAAG,EAAE,aAAa,EAAE,gCAAgC;gBACpD,kBAAkB,EAAE,YAAY;gBAChC,eAAe,EAAE,IAAI;aACtB,CAAC;QACJ,CAAC;QACD,OAAO,CAAC,KAAK,CAAC,4CAA4C,YAAY,iBAAiB,CAAC,CAAC;IAC3F,CAAC;IAED,yCAAyC;IACzC,IAAI,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,KAAK,GAAsB,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC,CAAC;YACrF,OAAO;gBACL,IAAI,EAAE,KAAK,CAAC,GAAG,KAAK,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ;gBACvD,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,IAAI;gBAChC,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,IAAI;gBACtC,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,EAAE;gBAC1B,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI;gBAC5B,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,IAAI;gBAChC,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,IAAI;gBACtB,kBAAkB,EAAE,IAAI;gBACxB,eAAe,EAAE,gBAAgB;aAClC,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;IAED,sBAAsB;IACtB,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE,IAAI;QACb,MAAM,EAAE,EAAE;QACV,MAAM,EAAE,IAAI;QACZ,QAAQ,EAAE,IAAI;QACd,GAAG,EAAE,IAAI;QACT,kBAAkB,EAAE,IAAI;QACxB,eAAe,EAAE,IAAI;KACtB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,KAAwB;IACtD,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;QACjC,SAAS,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,aAAa,CAAC,gBAAgB,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACnF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,OAAO;IACV,OAAO,CAAiB;IACxB,YAAY,CAAc;IAElC,YAAY,IAAiB;QAC3B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG;YACb,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC;IACJ,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC;IAChC,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;IAC/B,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;IAC9B,CAAC;IAED,IAAI,kBAAkB;QACpB,OAAO,IAAI,CAAC,YAAY,CAAC,kBAAkB,CAAC;IAC9C,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;IAClC,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC;IACpC,CAAC;IAED,UAAU,CAAC,QAAgB,EAAE,OAAe;QAC1C,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAChE,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACjC,IAAI,CAAC,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC;IACjC,CAAC;IAED;;OAEG;IACH,gBAAgB,CAAC,QAAgB,EAAE,OAAe,EAAE,MAA6C,EAAE,MAAc,EAAE,QAAuB;QACxI,IAAI,CAAC,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACjC,IAAI,CAAC,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC;QAC/B,IAAI,CAAC,YAAY,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACtC,IAAI,CAAC,YAAY,CAAC,OAAO,GAAG,OAAO,CAAC;QACpC,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,MAAM,CAAC;QAClC,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,MAAM,CAAC;QAClC,IAAI,CAAC,YAAY,CAAC,QAAQ,GAAG,QAAQ,CAAC;IACxC,CAAC;IAED,aAAa;QACX,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CACb,IAAI,CAAC,YAAY,CAAC,IAAI,KAAK,QAAQ;gBACjC,CAAC,CAAC,oDAAoD;gBACtD,CAAC,CAAC,oEAAoE,CACzE,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;IAC/B,CAAC;IAED,YAAY;QACV,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;QACrF,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;IAC9B,CAAC;CACF"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Firebase connection + helpers
3
+ *
4
+ * Modo A (Cowork): firebase-admin con service account → acceso total
5
+ * Modo B (Tenant): firebase client SDK con auth token → solo su data (security rules)
6
+ *
7
+ * La interfaz de helpers es IDENTICA para ambos modos.
8
+ * El modo se detecta al inicializar.
9
+ */
10
+ import type { Session } from './auth.js';
11
+ type FirestoreMode = 'admin' | 'client' | 'none';
12
+ /**
13
+ * Inicializa Firebase Admin SDK (Modo A).
14
+ */
15
+ export declare function initFirebaseAdmin(serviceAccountPath: string): void;
16
+ /**
17
+ * Inicializa Firebase Client SDK (Modo B).
18
+ * Autentica con custom token de credentials.json.
19
+ */
20
+ export declare function initFirebaseClient(): Promise<boolean>;
21
+ /**
22
+ * Re-autenticar despues de connect_account (sin reiniciar el MCP).
23
+ */
24
+ export declare function reauthClient(customToken: string): Promise<boolean>;
25
+ export declare function getMode(): FirestoreMode;
26
+ interface QueryFilter {
27
+ field: string;
28
+ op: '<' | '<=' | '==' | '!=' | '>=' | '>' | 'array-contains' | 'in' | 'not-in' | 'array-contains-any';
29
+ value: unknown;
30
+ }
31
+ interface QueryOptions {
32
+ orderBy?: {
33
+ field: string;
34
+ direction?: 'asc' | 'desc';
35
+ };
36
+ limit?: number;
37
+ }
38
+ /**
39
+ * Query con tenantId obligatorio.
40
+ * Funciona identico en Modo A (admin) y Modo B (client).
41
+ */
42
+ export declare function queryByTenant(session: Session, collectionName: string, filters?: QueryFilter[], options?: QueryOptions): Promise<Record<string, unknown>[]>;
43
+ /**
44
+ * Lee un documento por ID.
45
+ */
46
+ export declare function readDoc(collectionName: string, docId: string): Promise<Record<string, unknown> | null>;
47
+ /**
48
+ * Escribe (set merge) un documento.
49
+ */
50
+ export declare function writeDoc(collectionName: string, docId: string, data: Record<string, unknown>): Promise<void>;
51
+ /**
52
+ * Actualiza campos de un documento existente.
53
+ */
54
+ export declare function updateDoc(collectionName: string, docId: string, data: Record<string, unknown>): Promise<void>;
55
+ /**
56
+ * Genera un ID unico para un documento nuevo.
57
+ */
58
+ export declare function generateId(collectionName: string): string;
59
+ /**
60
+ * Server timestamp para escrituras. Funciona en ambos modos.
61
+ */
62
+ export declare function serverTimestamp(): unknown;
63
+ export {};
64
+ //# sourceMappingURL=firestore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"firestore.d.ts","sourceRoot":"","sources":["../src/firestore.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAuBH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKzC,KAAK,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAOjD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAalE;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,OAAO,CAAC,CAwB3D;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAqBxE;AAED,wBAAgB,OAAO,IAAI,aAAa,CAEvC;AAID,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,GAAG,gBAAgB,GAAG,IAAI,GAAG,QAAQ,GAAG,oBAAoB,CAAC;IACtG,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,UAAU,YAAY;IACpB,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE,CAAC;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAID;;;GAGG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE,WAAW,EAAO,EAC3B,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CASpC;AAkDD;;GAEG;AACH,wBAAsB,OAAO,CAC3B,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAYzC;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;GAEG;AACH,wBAAsB,SAAS,CAC7B,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAOzD;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAMzC"}
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Firebase connection + helpers
3
+ *
4
+ * Modo A (Cowork): firebase-admin con service account → acceso total
5
+ * Modo B (Tenant): firebase client SDK con auth token → solo su data (security rules)
6
+ *
7
+ * La interfaz de helpers es IDENTICA para ambos modos.
8
+ * El modo se detecta al inicializar.
9
+ */
10
+ import admin from 'firebase-admin';
11
+ import { initializeApp as initClientApp } from 'firebase/app';
12
+ import { getFirestore as getClientFirestore, collection as clientCollection, doc as clientDoc, getDoc as clientGetDoc, getDocs as clientGetDocs, setDoc as clientSetDoc, updateDoc as clientUpdateDoc, query as clientQuery, where as clientWhere, orderBy as clientOrderBy, limit as clientLimit, serverTimestamp as clientServerTimestamp, } from 'firebase/firestore';
13
+ import { getAuth as getClientAuth, signInWithCustomToken, } from 'firebase/auth';
14
+ import { readFileSync } from 'fs';
15
+ import { FIREBASE_CONFIG, readCredentials } from './auth.js';
16
+ let mode = 'none';
17
+ let adminDb;
18
+ let clientDb;
19
+ // ─── Inicializacion ───
20
+ /**
21
+ * Inicializa Firebase Admin SDK (Modo A).
22
+ */
23
+ export function initFirebaseAdmin(serviceAccountPath) {
24
+ if (mode !== 'none')
25
+ return;
26
+ const serviceAccount = JSON.parse(readFileSync(serviceAccountPath, 'utf-8'));
27
+ admin.initializeApp({
28
+ credential: admin.credential.cert(serviceAccount),
29
+ });
30
+ adminDb = admin.firestore();
31
+ mode = 'admin';
32
+ }
33
+ /**
34
+ * Inicializa Firebase Client SDK (Modo B).
35
+ * Autentica con custom token de credentials.json.
36
+ */
37
+ export async function initFirebaseClient() {
38
+ if (mode !== 'none')
39
+ return true;
40
+ const creds = readCredentials();
41
+ if (!creds?.customToken) {
42
+ console.error('[firestore] No hay custom token en credentials.json');
43
+ return false;
44
+ }
45
+ try {
46
+ const app = initClientApp(FIREBASE_CONFIG);
47
+ const auth = getClientAuth(app);
48
+ // Autenticar con custom token
49
+ await signInWithCustomToken(auth, creds.customToken);
50
+ clientDb = getClientFirestore(app);
51
+ mode = 'client';
52
+ console.error('[firestore] Firebase Client SDK autenticado');
53
+ return true;
54
+ }
55
+ catch (err) {
56
+ console.error('[firestore] Error autenticando con Client SDK:', err);
57
+ return false;
58
+ }
59
+ }
60
+ /**
61
+ * Re-autenticar despues de connect_account (sin reiniciar el MCP).
62
+ */
63
+ export async function reauthClient(customToken) {
64
+ try {
65
+ let app;
66
+ if (mode === 'client') {
67
+ // Ya inicializado, solo re-autenticar
68
+ const { getApp } = await import('firebase/app');
69
+ app = getApp();
70
+ }
71
+ else {
72
+ app = initClientApp(FIREBASE_CONFIG);
73
+ }
74
+ const auth = getClientAuth(app);
75
+ await signInWithCustomToken(auth, customToken);
76
+ clientDb = getClientFirestore(app);
77
+ mode = 'client';
78
+ return true;
79
+ }
80
+ catch (err) {
81
+ console.error('[firestore] Error re-autenticando:', err);
82
+ return false;
83
+ }
84
+ }
85
+ export function getMode() {
86
+ return mode;
87
+ }
88
+ // ─── Helpers unificados ───
89
+ /**
90
+ * Query con tenantId obligatorio.
91
+ * Funciona identico en Modo A (admin) y Modo B (client).
92
+ */
93
+ export async function queryByTenant(session, collectionName, filters = [], options = {}) {
94
+ const tenantId = session.requireTenant();
95
+ if (mode === 'admin') {
96
+ return queryByTenantAdmin(tenantId, collectionName, filters, options);
97
+ }
98
+ else if (mode === 'client') {
99
+ return queryByTenantClient(tenantId, collectionName, filters, options);
100
+ }
101
+ throw new Error('Firebase no inicializado');
102
+ }
103
+ async function queryByTenantAdmin(tenantId, collectionName, filters, options) {
104
+ let query = adminDb
105
+ .collection(collectionName)
106
+ .where('tenantId', '==', tenantId);
107
+ for (const f of filters) {
108
+ query = query.where(f.field, f.op, f.value);
109
+ }
110
+ if (options.orderBy) {
111
+ query = query.orderBy(options.orderBy.field, options.orderBy.direction ?? 'asc');
112
+ }
113
+ if (options.limit) {
114
+ query = query.limit(options.limit);
115
+ }
116
+ const snapshot = await query.get();
117
+ return snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() }));
118
+ }
119
+ async function queryByTenantClient(tenantId, collectionName, filters, options) {
120
+ const whereConstraints = [clientWhere('tenantId', '==', tenantId)];
121
+ for (const f of filters) {
122
+ whereConstraints.push(clientWhere(f.field, f.op, f.value));
123
+ }
124
+ const allConstraints = [...whereConstraints];
125
+ if (options.orderBy) {
126
+ allConstraints.push(clientOrderBy(options.orderBy.field, options.orderBy.direction ?? 'asc'));
127
+ }
128
+ if (options.limit) {
129
+ allConstraints.push(clientLimit(options.limit));
130
+ }
131
+ const q = clientQuery(clientCollection(clientDb, collectionName), ...allConstraints);
132
+ const snapshot = await clientGetDocs(q);
133
+ return snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() }));
134
+ }
135
+ /**
136
+ * Lee un documento por ID.
137
+ */
138
+ export async function readDoc(collectionName, docId) {
139
+ if (mode === 'admin') {
140
+ const doc = await adminDb.collection(collectionName).doc(docId).get();
141
+ if (!doc.exists)
142
+ return null;
143
+ return { id: doc.id, ...doc.data() };
144
+ }
145
+ else if (mode === 'client') {
146
+ const ref = clientDoc(clientDb, collectionName, docId);
147
+ const doc = await clientGetDoc(ref);
148
+ if (!doc.exists())
149
+ return null;
150
+ return { id: doc.id, ...doc.data() };
151
+ }
152
+ throw new Error('Firebase no inicializado');
153
+ }
154
+ /**
155
+ * Escribe (set merge) un documento.
156
+ */
157
+ export async function writeDoc(collectionName, docId, data) {
158
+ if (mode === 'admin') {
159
+ await adminDb.collection(collectionName).doc(docId).set(data, { merge: true });
160
+ }
161
+ else if (mode === 'client') {
162
+ const ref = clientDoc(clientDb, collectionName, docId);
163
+ await clientSetDoc(ref, data, { merge: true });
164
+ }
165
+ else {
166
+ throw new Error('Firebase no inicializado');
167
+ }
168
+ }
169
+ /**
170
+ * Actualiza campos de un documento existente.
171
+ */
172
+ export async function updateDoc(collectionName, docId, data) {
173
+ if (mode === 'admin') {
174
+ await adminDb.collection(collectionName).doc(docId).update(data);
175
+ }
176
+ else if (mode === 'client') {
177
+ const ref = clientDoc(clientDb, collectionName, docId);
178
+ await clientUpdateDoc(ref, data);
179
+ }
180
+ else {
181
+ throw new Error('Firebase no inicializado');
182
+ }
183
+ }
184
+ /**
185
+ * Genera un ID unico para un documento nuevo.
186
+ */
187
+ export function generateId(collectionName) {
188
+ if (mode === 'admin') {
189
+ return adminDb.collection(collectionName).doc().id;
190
+ }
191
+ else if (mode === 'client') {
192
+ return clientDoc(clientCollection(clientDb, collectionName)).id;
193
+ }
194
+ throw new Error('Firebase no inicializado');
195
+ }
196
+ /**
197
+ * Server timestamp para escrituras. Funciona en ambos modos.
198
+ */
199
+ export function serverTimestamp() {
200
+ if (mode === 'admin') {
201
+ return admin.firestore.FieldValue.serverTimestamp();
202
+ }
203
+ else {
204
+ return clientServerTimestamp();
205
+ }
206
+ }
207
+ //# sourceMappingURL=firestore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"firestore.js","sourceRoot":"","sources":["../src/firestore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Ponch MCP Server — Entry Point
4
+ *
5
+ * Conecta Claude Desktop/Cowork con Firestore.
6
+ * Modo A (Cowork): service account admin, multi-tenant
7
+ * Modo B (Tenant): login una vez con codigo, solo su data
8
+ *
9
+ * Docs: docs/logica/LOGICA_MARKETING_MCP_PLAN.md
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG"}
package/dist/index.js ADDED
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Ponch MCP Server — Entry Point
4
+ *
5
+ * Conecta Claude Desktop/Cowork con Firestore.
6
+ * Modo A (Cowork): service account admin, multi-tenant
7
+ * Modo B (Tenant): login una vez con codigo, solo su data
8
+ *
9
+ * Docs: docs/logica/LOGICA_MARKETING_MCP_PLAN.md
10
+ */
11
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
12
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
13
+ import { resolveAuth, Session } from './auth.js';
14
+ import { initFirebaseAdmin, initFirebaseClient } from './firestore.js';
15
+ import { registerContextTools } from './tools/context.js';
16
+ import { registerCoreTools } from './tools/core.js';
17
+ import { registerMarketingTools } from './tools/marketing.js';
18
+ import { buildSystemPrompt } from './prompts/system.js';
19
+ async function main() {
20
+ // 1. Resolver auth
21
+ const authContext = resolveAuth();
22
+ const session = new Session(authContext);
23
+ console.error(`[ponch-mcp] Modo: ${authContext.mode}`);
24
+ // 2. Inicializar Firebase segun el modo
25
+ if (authContext.serviceAccountPath) {
26
+ // Modo A puro: service account → Admin SDK
27
+ initFirebaseAdmin(authContext.serviceAccountPath);
28
+ console.error('[ponch-mcp] Firebase Admin inicializado. Usa set_context para seleccionar tenant.');
29
+ }
30
+ else if (authContext.credentialsPath && authContext.tenantId) {
31
+ // Modo B (o super_admin con credentials): Client SDK
32
+ const ok = await initFirebaseClient();
33
+ if (ok) {
34
+ const rolLabel = authContext.rol === 'super_admin' ? ' (super_admin — set_context habilitado)' : '';
35
+ console.error(`[ponch-mcp] Firebase Client autenticado. Tenant: ${authContext.tenantId}${rolLabel}`);
36
+ }
37
+ else {
38
+ console.error('[ponch-mcp] Error autenticando. Usa connect_account para reconectar.');
39
+ }
40
+ }
41
+ else {
42
+ console.error('[ponch-mcp] Sin conexion. Usa connect_account para conectar tu cuenta de Ponch.');
43
+ }
44
+ // 3. Crear MCP Server
45
+ const server = new McpServer({
46
+ name: 'ponch',
47
+ version: '1.0.0',
48
+ });
49
+ // 4. Registrar system prompt como resource
50
+ server.resource('system-prompt', 'ponch://system-prompt', {
51
+ description: 'System prompt de marketing de Ponch (IP, invisible para el tenant)',
52
+ mimeType: 'text/plain',
53
+ }, async () => {
54
+ const prompt = await buildSystemPrompt(session);
55
+ return {
56
+ contents: [{
57
+ uri: 'ponch://system-prompt',
58
+ text: prompt,
59
+ mimeType: 'text/plain',
60
+ }],
61
+ };
62
+ });
63
+ // 5. Registrar tools
64
+ registerContextTools(server, session);
65
+ registerCoreTools(server, session);
66
+ registerMarketingTools(server, session);
67
+ console.error(`[ponch-mcp] ${session.mode === 'cowork' ? '13' : '12'} tools registrados.`);
68
+ // 6. Conectar via stdio
69
+ const transport = new StdioServerTransport();
70
+ await server.connect(transport);
71
+ console.error('[ponch-mcp] Servidor listo.');
72
+ }
73
+ main().catch((error) => {
74
+ console.error('[ponch-mcp] Error fatal:', error);
75
+ process.exit(1);
76
+ });
77
+ //# sourceMappingURL=index.js.map