@crmforall/connector 0.1.3 → 0.1.5

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.
@@ -17,6 +17,7 @@ export declare const MCP_TOOL: {
17
17
  readonly REGISTER_ENTRY_POINT: "register_entry_point";
18
18
  readonly TRACK_ENGAGEMENT: "track_engagement";
19
19
  readonly ONBOARDING_STATUS: "onboarding_status";
20
+ readonly SYNC_ALL: "sync_all";
20
21
  };
21
22
  export type McpToolName = (typeof MCP_TOOL)[keyof typeof MCP_TOOL];
22
23
  export declare const APPROVAL_SCOPE: {
package/dist/contracts.js CHANGED
@@ -20,6 +20,7 @@ exports.MCP_TOOL = {
20
20
  REGISTER_ENTRY_POINT: "register_entry_point",
21
21
  TRACK_ENGAGEMENT: "track_engagement",
22
22
  ONBOARDING_STATUS: "onboarding_status",
23
+ SYNC_ALL: "sync_all",
23
24
  };
24
25
  exports.APPROVAL_SCOPE = {
25
26
  TARGET_SNAPSHOT_CREATE: "target_snapshot:create",
@@ -43,6 +44,7 @@ exports.TOOL_APPROVAL_REQUIREMENT = {
43
44
  [exports.MCP_TOOL.REGISTER_ENTRY_POINT]: exports.APPROVAL_SCOPE.ENTRY_POINT_REGISTER,
44
45
  [exports.MCP_TOOL.TRACK_ENGAGEMENT]: null,
45
46
  [exports.MCP_TOOL.ONBOARDING_STATUS]: null,
47
+ [exports.MCP_TOOL.SYNC_ALL]: null,
46
48
  };
47
49
  /** 승인 토큰이 필요한 도구 — 토큰 없이 호출되면 즉시 거부한다. */
48
50
  exports.APPROVAL_REQUIRED_TOOLS = new Set(Object.entries(exports.TOOL_APPROVAL_REQUIREMENT)
package/dist/index.d.ts CHANGED
@@ -6,6 +6,9 @@ export * from "./mapping";
6
6
  export * from "./platform";
7
7
  export * from "./targets";
8
8
  export * from "./tools";
9
+ export * from "./sync";
10
+ export { PostgresAdapter } from "./db/postgres";
11
+ export type { DbAdapter } from "./db/adapter";
9
12
  /**
10
13
  * crmforall MCP 커넥터 라이브러리 진입점.
11
14
  * 서버 기동은 server.ts(bin)에서 — CLI가 이 모듈을 import해도 서버가 뜨지 않는다.
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.PostgresAdapter = void 0;
17
18
  exports.buildAdapter = buildAdapter;
18
19
  const postgres_1 = require("./db/postgres");
19
20
  __exportStar(require("./config"), exports);
@@ -22,6 +23,9 @@ __exportStar(require("./mapping"), exports);
22
23
  __exportStar(require("./platform"), exports);
23
24
  __exportStar(require("./targets"), exports);
24
25
  __exportStar(require("./tools"), exports);
26
+ __exportStar(require("./sync"), exports);
27
+ var postgres_2 = require("./db/postgres");
28
+ Object.defineProperty(exports, "PostgresAdapter", { enumerable: true, get: function () { return postgres_2.PostgresAdapter; } });
25
29
  /**
26
30
  * crmforall MCP 커넥터 라이브러리 진입점.
27
31
  * 서버 기동은 server.ts(bin)에서 — CLI가 이 모듈을 import해도 서버가 뜨지 않는다.
package/dist/sync.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ import type { DbAdapter } from "./db/adapter";
2
+ import type { PlatformClient } from "./platform";
3
+ /**
4
+ * 발송 결과 동기화 — 플랫폼이 가진 발송 작업 결과를 고객사 CRM 스키마로 반영한다.
5
+ * 데이터 방향: 플랫폼 → 고객사 DB(crm_send_jobs / crm_send_results). 승인 불필요(읽기성).
6
+ * CLI `connect sync`(주기 폴링)와 MCP `sync_send_result`·`sync_all`이 공유한다.
7
+ */
8
+ export interface SyncedJob {
9
+ sendJobId: string;
10
+ status: string;
11
+ counts: Record<string, number>;
12
+ }
13
+ /** 한 발송 작업의 결과를 플랫폼에서 가져와 CRM 스키마에 upsert */
14
+ export declare function syncSendJob(db: DbAdapter, platform: PlatformClient, crmSchema: string, sendJobId: string): Promise<SyncedJob>;
15
+ export interface SyncResult {
16
+ synced: number;
17
+ /** 아직 진행 중(non-terminal)으로 남은 작업 수 — 다음 주기에 다시 시도 */
18
+ stillPending: number;
19
+ jobs: SyncedJob[];
20
+ }
21
+ /**
22
+ * 진행 중인(완료/실패가 아닌) 발송 작업을 모두 동기화한다 (주기 폴링의 1회분).
23
+ * 데이터 방향은 단방향(플랫폼→로컬)이라 원본 운영 테이블은 절대 건드리지 않는다.
24
+ */
25
+ export declare function syncPendingSendJobs(opts: {
26
+ db: DbAdapter;
27
+ platform: PlatformClient;
28
+ crmSchema: string;
29
+ limit?: number;
30
+ }): Promise<SyncResult>;
package/dist/sync.js ADDED
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.syncSendJob = syncSendJob;
4
+ exports.syncPendingSendJobs = syncPendingSendJobs;
5
+ const TERMINAL = new Set(["done", "failed"]);
6
+ /** 한 발송 작업의 결과를 플랫폼에서 가져와 CRM 스키마에 upsert */
7
+ async function syncSendJob(db, platform, crmSchema, sendJobId) {
8
+ const job = await platform.getSendJob(sendJobId);
9
+ for (const r of job.results) {
10
+ await db.execute(`INSERT INTO ${crmSchema}.crm_send_results
11
+ (send_job_id, receiver_ref, status, provider_code, internal_code)
12
+ VALUES ($1, $2, $3, $4, $5)
13
+ ON CONFLICT (send_job_id, receiver_ref) DO UPDATE
14
+ SET status = EXCLUDED.status,
15
+ provider_code = EXCLUDED.provider_code,
16
+ internal_code = EXCLUDED.internal_code,
17
+ synced_at = now()`, [sendJobId, r.receiverRef, r.status, r.providerCode, r.internalCode]);
18
+ }
19
+ await db.execute(`UPDATE ${crmSchema}.crm_send_jobs SET status = $2 WHERE send_job_id = $1`, [sendJobId, job.status]);
20
+ const counts = job.results.reduce((acc, r) => {
21
+ acc[r.status] = (acc[r.status] ?? 0) + 1;
22
+ return acc;
23
+ }, {});
24
+ return { sendJobId, status: job.status, counts };
25
+ }
26
+ /**
27
+ * 진행 중인(완료/실패가 아닌) 발송 작업을 모두 동기화한다 (주기 폴링의 1회분).
28
+ * 데이터 방향은 단방향(플랫폼→로컬)이라 원본 운영 테이블은 절대 건드리지 않는다.
29
+ */
30
+ async function syncPendingSendJobs(opts) {
31
+ const { db, platform, crmSchema, limit = 200 } = opts;
32
+ const rows = await db.selectRows(`SELECT send_job_id FROM ${crmSchema}.crm_send_jobs
33
+ WHERE status NOT IN ('done','failed')
34
+ ORDER BY send_job_id
35
+ LIMIT $1`, [limit]);
36
+ const jobs = [];
37
+ let stillPending = 0;
38
+ for (const row of rows) {
39
+ const id = String(row.send_job_id);
40
+ const synced = await syncSendJob(db, platform, crmSchema, id);
41
+ jobs.push(synced);
42
+ if (!TERMINAL.has(synced.status))
43
+ stillPending++;
44
+ }
45
+ return { synced: jobs.length, stillPending, jobs };
46
+ }
package/dist/tools.js CHANGED
@@ -7,6 +7,7 @@ const contracts_1 = require("./contracts");
7
7
  const config_1 = require("./config");
8
8
  const mapping_1 = require("./mapping");
9
9
  const targets_1 = require("./targets");
10
+ const sync_1 = require("./sync");
10
11
  function ok(data) {
11
12
  return { content: [{ type: "text", text: JSON.stringify(data) }] };
12
13
  }
@@ -358,24 +359,19 @@ function registerTools(server, rt) {
358
359
  });
359
360
  server.tool(contracts_1.MCP_TOOL.SYNC_SEND_RESULT, "발송 작업 결과를 플랫폼에서 가져와 CRM 스키마에 동기화한다 (승인 불필요)", { sendJobId: zod_1.z.string() }, async ({ sendJobId }) => {
360
361
  try {
361
- const job = await rt.platform.getSendJob(sendJobId);
362
362
  const db = await rt.getDb();
363
- for (const r of job.results) {
364
- await db.execute(`INSERT INTO ${s}.crm_send_results
365
- (send_job_id, receiver_ref, status, provider_code, internal_code)
366
- VALUES ($1, $2, $3, $4, $5)
367
- ON CONFLICT (send_job_id, receiver_ref) DO UPDATE
368
- SET status = EXCLUDED.status,
369
- provider_code = EXCLUDED.provider_code,
370
- internal_code = EXCLUDED.internal_code,
371
- synced_at = now()`, [sendJobId, r.receiverRef, r.status, r.providerCode, r.internalCode]);
372
- }
373
- await db.execute(`UPDATE ${s}.crm_send_jobs SET status = $2 WHERE send_job_id = $1`, [sendJobId, job.status]);
374
- const counts = job.results.reduce((acc, r) => {
375
- acc[r.status] = (acc[r.status] ?? 0) + 1;
376
- return acc;
377
- }, {});
378
- return ok({ sendJobId, status: job.status, resultCounts: counts });
363
+ const r = await (0, sync_1.syncSendJob)(db, rt.platform, s, sendJobId);
364
+ return ok({ sendJobId: r.sendJobId, status: r.status, resultCounts: r.counts });
365
+ }
366
+ catch (err) {
367
+ return unexpected(err);
368
+ }
369
+ });
370
+ server.tool(contracts_1.MCP_TOOL.SYNC_ALL, "진행 중인 발송 작업 전체를 한 번에 동기화한다 (주기 폴링 1회분, 승인 불필요)", { limit: zod_1.z.number().int().positive().max(500).optional() }, async ({ limit }) => {
371
+ try {
372
+ const db = await rt.getDb();
373
+ const result = await (0, sync_1.syncPendingSendJobs)({ db, platform: rt.platform, crmSchema: s, limit });
374
+ return ok(result);
379
375
  }
380
376
  catch (err) {
381
377
  return unexpected(err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crmforall/connector",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "crmforall MCP 커넥터 — 운영 DB 읽기, CRM 스키마 쓰기(승인 토큰 필수), 대상 산출",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",