gm-mcp 1.0.10 → 1.1.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.
@@ -0,0 +1,5 @@
1
+ import { LarkRecord } from "./types/lark.type";
2
+ export declare function fetchReportRecords(p: {
3
+ view_id: string;
4
+ field_names: string[];
5
+ }): Promise<LarkRecord[]>;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.fetchReportRecords = fetchReportRecords;
16
+ const axios_1 = __importDefault(require("axios"));
17
+ const lark_service_1 = require("./lark.service");
18
+ function fetchReportRecords(p) {
19
+ return __awaiter(this, void 0, void 0, function* () {
20
+ const access_token = yield (0, lark_service_1.getLarkTenantAccessToken)();
21
+ const appToken = "V5akbIXqfaBvXqs8twfl6rALgLd";
22
+ const tableId = "tblQGKjGd8MnK5qs";
23
+ const url = `https://open.larksuite.com/open-apis/bitable/v1/apps/${appToken}/tables/${tableId}/records/search`;
24
+ return axios_1.default
25
+ .post(url, p, {
26
+ params: { page_size: 500 },
27
+ headers: {
28
+ Authorization: "Bearer " + access_token,
29
+ },
30
+ })
31
+ .then((res) => res.data.data.items);
32
+ });
33
+ }
@@ -1,3 +1,4 @@
1
+ export declare function getLarkTenantAccessToken(): Promise<string>;
1
2
  export declare function sendStatisticX3Message(data: {
2
3
  date: string;
3
4
  content: string;
@@ -34,6 +35,42 @@ export declare function sendStatisticX3Message(data: {
34
35
  upper_message_id?: string | undefined;
35
36
  } | undefined;
36
37
  }>;
38
+ export declare function sendReportOpsGroup(data: {
39
+ title: string;
40
+ message_content: string;
41
+ }): Promise<{
42
+ code?: number | undefined;
43
+ msg?: string | undefined;
44
+ data?: {
45
+ message_id?: string | undefined;
46
+ root_id?: string | undefined;
47
+ parent_id?: string | undefined;
48
+ thread_id?: string | undefined;
49
+ msg_type?: string | undefined;
50
+ create_time?: string | undefined;
51
+ update_time?: string | undefined;
52
+ deleted?: boolean | undefined;
53
+ updated?: boolean | undefined;
54
+ chat_id?: string | undefined;
55
+ sender?: {
56
+ id: string;
57
+ id_type: string;
58
+ sender_type: string;
59
+ tenant_key?: string | undefined;
60
+ } | undefined;
61
+ body?: {
62
+ content: string;
63
+ } | undefined;
64
+ mentions?: {
65
+ key: string;
66
+ id: string;
67
+ id_type: string;
68
+ name: string;
69
+ tenant_key?: string | undefined;
70
+ }[] | undefined;
71
+ upper_message_id?: string | undefined;
72
+ } | undefined;
73
+ }>;
37
74
  export declare function sendLarkTextMessage(p: {
38
75
  message_content: {
39
76
  text: string;
@@ -45,7 +45,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
45
45
  return (mod && mod.__esModule) ? mod : { "default": mod };
46
46
  };
47
47
  Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.getLarkTenantAccessToken = getLarkTenantAccessToken;
48
49
  exports.sendStatisticX3Message = sendStatisticX3Message;
50
+ exports.sendReportOpsGroup = sendReportOpsGroup;
49
51
  exports.sendLarkTextMessage = sendLarkTextMessage;
50
52
  exports.sendMessageToX3Group = sendMessageToX3Group;
51
53
  const axios_1 = __importDefault(require("axios"));
@@ -56,12 +58,13 @@ const env_1 = require("../env");
56
58
  // group test_bot
57
59
  // const LARK_X3_GROUP_ID = 'oc_bb4e5b8dd8ada38127c790fa368c02b9';
58
60
  // Group dan choi front end
61
+ const DEBT_COLLECTION_CARD_ID = "ctp_AAXr3Fr7xcjr";
59
62
  const larkClient = new lark.Client({
60
63
  appId: (0, env_1.getEnv)().LARK_APP_ID,
61
64
  appSecret: (0, env_1.getEnv)().LARK_APP_SECRET,
62
65
  disableTokenCache: true,
63
66
  });
64
- function getAccessToken() {
67
+ function getLarkTenantAccessToken() {
65
68
  return __awaiter(this, void 0, void 0, function* () {
66
69
  const url = "https://open.larksuite.com/open-apis/auth/v3/tenant_access_token/internal";
67
70
  return axios_1.default
@@ -111,7 +114,7 @@ function getAccessToken() {
111
114
  // Split later
112
115
  function sendStatisticX3Message(data) {
113
116
  return __awaiter(this, void 0, void 0, function* () {
114
- const access_token = yield getAccessToken();
117
+ const access_token = yield getLarkTenantAccessToken();
115
118
  const { date, content } = data;
116
119
  const messageContent = {
117
120
  type: "template",
@@ -136,10 +139,37 @@ function sendStatisticX3Message(data) {
136
139
  }, lark.withTenantToken(access_token));
137
140
  });
138
141
  }
142
+ function sendReportOpsGroup(data) {
143
+ return __awaiter(this, void 0, void 0, function* () {
144
+ const access_token = yield getLarkTenantAccessToken();
145
+ const { title, message_content } = data;
146
+ const messageContent = {
147
+ type: "template",
148
+ data: {
149
+ template_id: (0, env_1.getEnv)().LARK_X3_TEMPLATE_ID,
150
+ // template_id: 'ctp_AAdI5Sp48YUT',
151
+ template_variable: {
152
+ title,
153
+ message_content,
154
+ },
155
+ },
156
+ };
157
+ return larkClient.im.message.create({
158
+ params: {
159
+ receive_id_type: "chat_id",
160
+ },
161
+ data: {
162
+ receive_id: (0, env_1.getEnv)().LARK_X3_GROUP_ID,
163
+ msg_type: "interactive",
164
+ content: JSON.stringify(messageContent),
165
+ },
166
+ }, lark.withTenantToken(access_token));
167
+ });
168
+ }
139
169
  function sendLarkTextMessage(p) {
140
170
  return __awaiter(this, void 0, void 0, function* () {
141
171
  const { message_content, receive_id } = p;
142
- const access_token = yield getAccessToken();
172
+ const access_token = yield getLarkTenantAccessToken();
143
173
  return larkClient.im.message.create({
144
174
  params: {
145
175
  receive_id_type: "chat_id",
@@ -0,0 +1,4 @@
1
+ export interface LarkRecord {
2
+ fields: Record<string, any>;
3
+ record_id: string;
4
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/test.js CHANGED
@@ -10,10 +10,39 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const env_1 = require("./env");
13
+ const lark_tool_1 = require("./tools/lark-tool");
13
14
  function setupTest() {
14
15
  return __awaiter(this, void 0, void 0, function* () {
15
16
  (0, env_1.ensureEnvVariables)();
16
- const sendMessageToLark = (0, env_1.getEnv)().SEND_MESSAGE_TO_LARK;
17
+ const compnay = "BGG";
18
+ const month = "12/2025";
19
+ const res = yield (0, lark_tool_1.debtCollectionRateTool)({ sendMessageToLark: false, companyName: compnay, monthDate: month });
20
+ console.log(res);
21
+ // const fieldNames = REPORT_COLUMS.map((item) => item.field_name);
22
+ // try {
23
+ // const records = await fetchReportRecords({ view_id: "vewI23hNnx", field_names: fieldNames });
24
+ // // console.log(records);
25
+ // // Find by name first
26
+ // const companyNames = records.map((item) => (item.fields["CÔNG TY"] || "") as string);
27
+ // // console.log(companyNames);
28
+ // const searchTems = "DENKO";
29
+ // const monthTerm = "12";
30
+ // const foundName = searchLike(searchTems, companyNames)[0];
31
+ // const companiesMatchName = records.filter((item) => item.fields["CÔNG TY"] === foundName);
32
+ // // console.log(companiesMatchName);
33
+ // // Find by pay period
34
+ // const foundMonth = searchLike(
35
+ // monthTerm,
36
+ // companiesMatchName.map((item) => item.fields["KỲ LƯƠNG"])
37
+ // )[0];
38
+ // console.log(foundMonth);
39
+ // const finalCompnay = companiesMatchName.find((item) => item.fields["KỲ LƯƠNG"] === foundMonth);
40
+ // console.log(finalCompnay);
41
+ // // console.log(companyCandidates);
42
+ // } catch (e) {
43
+ // console.log(e);
44
+ // }
45
+ // const sendMessageToLark = getEnv().SEND_MESSAGE_TO_LARK;
17
46
  // const reuslt = await statisticX3TodayTool({ sendMessageToLark: false });
18
47
  // console.log(reuslt);
19
48
  // const reuslt = await statisticX3Tool({ sendMessageToLark: false, date: "11/06/2025" });
@@ -0,0 +1,6 @@
1
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types";
2
+ export declare function debtCollectionRateTool(options: {
3
+ sendMessageToLark: boolean;
4
+ companyName: string;
5
+ monthDate: string;
6
+ }): Promise<CallToolResult>;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.debtCollectionRateTool = debtCollectionRateTool;
13
+ const helpers_1 = require("../helpers");
14
+ const search_like_1 = require("../helpers/search-like");
15
+ const report_columns_1 = require("../services/constants/report-columns");
16
+ const lark_base_service_1 = require("../services/lark-base.service");
17
+ const lark_service_1 = require("../services/lark.service");
18
+ const companyKey = "CÔNG TY";
19
+ const preiodKey = "KỲ LƯƠNG";
20
+ const debtCollectionRateKey = "TỈ LỆ THU NỢ";
21
+ function debtCollectionRateTool(options) {
22
+ return __awaiter(this, void 0, void 0, function* () {
23
+ const { sendMessageToLark, companyName, monthDate } = options;
24
+ const monthParts = (0, helpers_1.extractMonth)(monthDate);
25
+ if (monthParts === null) {
26
+ return { content: [{ type: "text", text: `${monthDate} is invalid` }] };
27
+ }
28
+ const { month, year } = monthParts;
29
+ const fieldNames = report_columns_1.REPORT_COLUMS.map((item) => item.field_name);
30
+ try {
31
+ const records = yield (0, lark_base_service_1.fetchReportRecords)({ view_id: "vewI23hNnx", field_names: fieldNames });
32
+ // console.log(records);
33
+ // Find by name first
34
+ const companyNames = records.map((item) => (item.fields["CÔNG TY"] || ""));
35
+ // console.log(companyNames);
36
+ // const searchTems = "DENKO";
37
+ // const monthTerm = "12";
38
+ const foundName = (0, search_like_1.searchLike)(companyName, companyNames)[0];
39
+ const companiesMatchName = records.filter((item) => item.fields["CÔNG TY"] === foundName);
40
+ // console.log(companiesMatchName);
41
+ // Find by pay period
42
+ const foundMonth = (0, search_like_1.searchLike)(month.toString(), companiesMatchName.map((item) => item.fields["KỲ LƯƠNG"]))[0];
43
+ console.log(foundMonth);
44
+ const finalCompnay = companiesMatchName.find((item) => item.fields["KỲ LƯƠNG"] === foundMonth);
45
+ if (sendMessageToLark) {
46
+ yield (0, lark_service_1.sendReportOpsGroup)({
47
+ title: "Tỉ lệ thu nợ",
48
+ message_content: [
49
+ `Công ty: ${(finalCompnay === null || finalCompnay === void 0 ? void 0 : finalCompnay.fields[companyKey]) || "N/A"}`,
50
+ `Kỳ lương: ${(finalCompnay === null || finalCompnay === void 0 ? void 0 : finalCompnay.fields[preiodKey]) || "N/A"}`,
51
+ `Tỷ lệ thu nợ: ${Number(finalCompnay === null || finalCompnay === void 0 ? void 0 : finalCompnay.fields[debtCollectionRateKey]) * 100}%`,
52
+ ].join("<hr>"),
53
+ });
54
+ }
55
+ return { content: [{ type: "text", text: JSON.stringify(finalCompnay) }] };
56
+ // console.log(finalCompnay);
57
+ }
58
+ catch (e) {
59
+ return { content: [{ type: "text", text: JSON.stringify(e) }] };
60
+ }
61
+ });
62
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gm-mcp",
3
- "version": "1.0.10",
3
+ "version": "1.1.1",
4
4
  "description": "Mcp server for Gm",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -0,0 +1,39 @@
1
+ export function searchLike(query: string, list: string[]) {
2
+ if (!query) return list;
3
+
4
+ const searchTerm = query.toLowerCase().trim();
5
+
6
+ return list
7
+ .map((company) => {
8
+ const companyLower = company.toLowerCase();
9
+ let score = 0;
10
+
11
+ // Exact match gets highest score
12
+ if (companyLower === searchTerm) {
13
+ score = 1000;
14
+ }
15
+ // Starts with search term
16
+ else if (companyLower.startsWith(searchTerm)) {
17
+ score = 500;
18
+ }
19
+ // Contains search term
20
+ else if (companyLower.includes(searchTerm)) {
21
+ score = 250;
22
+ }
23
+ // Check if all characters exist in order
24
+ else {
25
+ let pos = 0;
26
+ for (let char of searchTerm) {
27
+ pos = companyLower.indexOf(char, pos);
28
+ if (pos === -1) break;
29
+ score += 10;
30
+ pos++;
31
+ }
32
+ }
33
+
34
+ return { company, score };
35
+ })
36
+ .filter((item) => item.score > 0)
37
+ .sort((a, b) => b.score - a.score)
38
+ .map((item) => item.company);
39
+ }
package/src/index.ts CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  } from "./tools";
13
13
  import z from "zod";
14
14
  import { ensureEnvVariables, getEnv } from "./env";
15
+ import { debtCollectionRateTool } from "./tools/lark-tool";
15
16
 
16
17
  export const server = new McpServer(
17
18
  {
@@ -88,6 +89,23 @@ server.tool(
88
89
  }
89
90
  );
90
91
 
92
+ server.tool(
93
+ "debt_collection_by_month",
94
+ "The company's debt collection rate by month",
95
+ {
96
+ month: z.string({ description: "Month to get data, month must be represented with format MM/yyyy" }),
97
+ company_name: z.string({ description: "Company name to get data" }),
98
+ },
99
+ async (args) => {
100
+ const { month, company_name } = args;
101
+ return debtCollectionRateTool({
102
+ sendMessageToLark: getEnv().SEND_MESSAGE_TO_LARK,
103
+ monthDate: month,
104
+ companyName: company_name,
105
+ });
106
+ }
107
+ );
108
+
91
109
  function bootstap() {
92
110
  ensureEnvVariables();
93
111
  const transport: StdioServerTransport = new StdioServerTransport();