fmes-contact-mcp 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 (38) hide show
  1. package/.env +3 -0
  2. package/.env.example +6 -0
  3. package/README.md +90 -0
  4. package/dist/client/crm-client.d.ts +13 -0
  5. package/dist/client/crm-client.js +126 -0
  6. package/dist/client/crm-client.js.map +1 -0
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.js +42 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/tools/get-promoter-contracts.d.ts +30 -0
  11. package/dist/tools/get-promoter-contracts.js +54 -0
  12. package/dist/tools/get-promoter-contracts.js.map +1 -0
  13. package/dist/tools/get-promoter-detail.d.ts +22 -0
  14. package/dist/tools/get-promoter-detail.js +40 -0
  15. package/dist/tools/get-promoter-detail.js.map +1 -0
  16. package/dist/tools/search-contracts.d.ts +31 -0
  17. package/dist/tools/search-contracts.js +50 -0
  18. package/dist/tools/search-contracts.js.map +1 -0
  19. package/dist/tools/search-promoters.d.ts +61 -0
  20. package/dist/tools/search-promoters.js +58 -0
  21. package/dist/tools/search-promoters.js.map +1 -0
  22. package/dist/tools/stats-promoters.d.ts +16 -0
  23. package/dist/tools/stats-promoters.js +19 -0
  24. package/dist/tools/stats-promoters.js.map +1 -0
  25. package/dist/types/index.d.ts +87 -0
  26. package/dist/types/index.js +3 -0
  27. package/dist/types/index.js.map +1 -0
  28. package/package.json +25 -0
  29. package/src/client/crm-client.ts +135 -0
  30. package/src/index.ts +88 -0
  31. package/src/tools/get-promoter-contracts.ts +69 -0
  32. package/src/tools/get-promoter-detail.ts +48 -0
  33. package/src/tools/search-contracts.ts +62 -0
  34. package/src/tools/search-promoters.ts +68 -0
  35. package/src/tools/stats-promoters.ts +25 -0
  36. package/src/types/index.ts +98 -0
  37. package/test/test-client.ts +68 -0
  38. package/tsconfig.json +18 -0
@@ -0,0 +1,68 @@
1
+ import { z } from "zod";
2
+ import { crmClient } from "../client/crm-client.js";
3
+
4
+ export const searchPromotersSchema = z.object({
5
+ name: z.string().optional().describe("触点姓名"),
6
+ idcard: z.string().optional().describe("身份证号"),
7
+ tel: z.string().optional().describe("手机号"),
8
+ empPk: z.string().optional().describe("促销员ID(米咖ID或普洛特ID)"),
9
+ province: z.string().optional().describe("省份名称,如:北京、上海"),
10
+ city: z.string().optional().describe("城市名称,如:北京、上海、广州"),
11
+ area: z.string().optional().describe("区县名称"),
12
+ sex: z.number().int().min(0).max(1).optional().describe("性别(0=女, 1=男)"),
13
+ ageMin: z.number().int().min(18).max(70).optional().describe("最小年龄"),
14
+ ageMax: z.number().int().min(18).max(70).optional().describe("最大年龄"),
15
+ workMonthMin: z.number().int().min(0).optional().describe("最少服务月数"),
16
+ projectNumMin: z.number().int().min(0).optional().describe("最少项目数"),
17
+ status: z
18
+ .enum(["项目中", "在岗", "待岗中"])
19
+ .optional()
20
+ .describe("项目状态"),
21
+ limit: z.number().int().min(1).max(50).default(10).describe("返回数量上限"),
22
+ });
23
+
24
+ export type SearchPromotersInput = z.infer<typeof searchPromotersSchema>;
25
+
26
+ export async function searchPromoters(args: SearchPromotersInput, _extra?: unknown) {
27
+ const result = await crmClient.searchPromoters({
28
+ name: args.name,
29
+ idcard: args.idcard,
30
+ tel: args.tel,
31
+ empPk: args.empPk,
32
+ province: args.province,
33
+ city: args.city,
34
+ area: args.area,
35
+ sex: args.sex,
36
+ ageMin: args.ageMin,
37
+ ageMax: args.ageMax,
38
+ workMonthMin: args.workMonthMin,
39
+ projectNumMin: args.projectNumMin,
40
+ status: args.status,
41
+ limit: args.limit,
42
+ });
43
+
44
+ if (result.list.length === 0) {
45
+ return {
46
+ content: [{ type: "text" as const, text: "未找到符合条件的触点。" }],
47
+ };
48
+ }
49
+
50
+ const summary =
51
+ `找到 ${result.total} 个触点,以下是前 ${result.list.length} 个:\n\n` +
52
+ result.list
53
+ .map(
54
+ (p, i) =>
55
+ `${i + 1}. ${p.name} | 米咖ID: ${p.empPk} | 性别: ${p.sex === 1 ? "男" : "女"} | 年龄: ${p.age} | ` +
56
+ `地区: ${p.province || ""}${p.city || ""}${p.area || ""} | 状态: ${p.status || "未知"} | ` +
57
+ `服务月数: ${p.workMonth || 0} | 项目数: ${p.projectNum || 0}`
58
+ )
59
+ .join("\n") +
60
+ (result.total > result.list.length
61
+ ? `\n\n(共 ${result.total} 条,已截取前 ${result.list.length} 条,可缩小筛选条件或调整 limit)`
62
+ : "");
63
+
64
+ return {
65
+ content: [{ type: "text" as const, text: summary }],
66
+ structuredContent: result,
67
+ };
68
+ }
@@ -0,0 +1,25 @@
1
+ import { z } from "zod";
2
+ import { crmClient } from "../client/crm-client.js";
3
+
4
+ export const statsPromotersSchema = z.object({
5
+ city: z.string().optional().describe("城市名称"),
6
+ });
7
+
8
+ export type StatsPromotersInput = z.infer<typeof statsPromotersSchema>;
9
+
10
+ export async function statsPromoters(_args: StatsPromotersInput, _extra?: unknown) {
11
+ const result = await crmClient.statsPromoters();
12
+
13
+ const text =
14
+ `触点统计概览(共 ${result.total} 人)\n\n` +
15
+ `按项目状态分布:\n` +
16
+ result.groups
17
+ .map((g) => ` ${g.key}: ${g.count}人(${g.percentage})`)
18
+ .join("\n") +
19
+ `\n\n提示:可按地区进一步筛选,使用 search_promoters 工具指定 city 参数。`;
20
+
21
+ return {
22
+ content: [{ type: "text" as const, text }],
23
+ structuredContent: result,
24
+ };
25
+ }
@@ -0,0 +1,98 @@
1
+ // ========== 请求参数类型 ==========
2
+
3
+ export interface SearchPromotersParams {
4
+ name?: string;
5
+ idcard?: string;
6
+ tel?: string;
7
+ empPk?: string;
8
+ province?: string;
9
+ city?: string;
10
+ area?: string;
11
+ sex?: number;
12
+ ageMin?: number;
13
+ ageMax?: number;
14
+ workMonthMin?: number;
15
+ projectNumMin?: number;
16
+ status?: string;
17
+ limit: number;
18
+ }
19
+
20
+ export interface SearchContractsParams {
21
+ idcard?: string;
22
+ empPk?: string;
23
+ contractType?: "1" | "2";
24
+ limit: number;
25
+ }
26
+
27
+ export interface StatsPromotersParams {
28
+ province?: string;
29
+ city?: string;
30
+ groupBy: "age" | "region" | "status";
31
+ }
32
+
33
+ // ========== 响应数据类型 ==========
34
+
35
+ export interface PromoterInfo {
36
+ [key: string]: unknown;
37
+ name: string;
38
+ sex: number;
39
+ age: number;
40
+ tel: string;
41
+ idcard: string;
42
+ empPk: string;
43
+ province: string;
44
+ city: string;
45
+ area: string;
46
+ homeAddress: string;
47
+ bank: string;
48
+ bankNum: string;
49
+ projectNum: number;
50
+ workMonth: number;
51
+ status: string;
52
+ registerTime: string;
53
+ isAuthentication: number;
54
+ }
55
+
56
+ export interface McpContractInfo {
57
+ [key: string]: unknown;
58
+ contractType: string;
59
+ source: string;
60
+ customer: string;
61
+ serviceSupplier: string;
62
+ merchantName: string;
63
+ signedTime: string;
64
+ startTime: string;
65
+ endTime: string;
66
+ taskName: string;
67
+ clientId: string;
68
+ contractTypeId: string;
69
+ startDate: string;
70
+ endDate: string;
71
+ contractNumber: string;
72
+ contractStatus: string;
73
+ }
74
+
75
+ export interface SearchPromotersResult {
76
+ [key: string]: unknown;
77
+ total: number;
78
+ list: PromoterInfo[];
79
+ query: Partial<SearchPromotersParams>;
80
+ }
81
+
82
+ export interface SearchContractsResult {
83
+ [key: string]: unknown;
84
+ total: number;
85
+ list: McpContractInfo[];
86
+ query: Partial<SearchContractsParams>;
87
+ }
88
+
89
+ export interface StatsResult {
90
+ [key: string]: unknown;
91
+ query: Partial<StatsPromotersParams>;
92
+ total: number;
93
+ groups: Array<{
94
+ key: string;
95
+ count: number;
96
+ percentage: string;
97
+ }>;
98
+ }
@@ -0,0 +1,68 @@
1
+ import { fileURLToPath } from "node:url";
2
+ import path from "node:path";
3
+
4
+ import {
5
+ Client,
6
+ } from "@modelcontextprotocol/sdk/client/index.js";
7
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
8
+
9
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
+ const projectRoot = path.resolve(__dirname, "..");
11
+
12
+ async function main() {
13
+ const transport = new StdioClientTransport({
14
+ command: "node",
15
+ args: ["dist/index.js"],
16
+ cwd: projectRoot,
17
+ });
18
+
19
+ const client = new Client(
20
+ { name: "test-client", version: "1.0.0" },
21
+ { capabilities: {} }
22
+ );
23
+
24
+ await client.connect(transport);
25
+
26
+ const tools = await client.listTools();
27
+ console.log(`\n✅ 服务启动成功,发现 ${tools.tools.length} 个工具:\n`);
28
+ for (const tool of tools.tools) {
29
+ console.log(` [${tool.name}] ${tool.description}`);
30
+ }
31
+
32
+ console.log("\n--- 测试 search_promoters ---\n");
33
+ try {
34
+ const result = await client.callTool({
35
+ name: "search_promoters",
36
+ arguments: { limit: 3 },
37
+ });
38
+ const content = result.content
39
+ .filter((c) => c.type === "text")
40
+ .map((c) => (c as { type: "text"; text: string }).text)
41
+ .join("\n");
42
+ console.log("返回结果:", content);
43
+ } catch (e) {
44
+ console.log("⚠️ 调用失败(可能 CRM 后端未启动):", (e as Error).message);
45
+ }
46
+
47
+ console.log("\n--- 测试 stats_promoters ---\n");
48
+ try {
49
+ const result = await client.callTool({
50
+ name: "stats_promoters",
51
+ arguments: {},
52
+ });
53
+ const content = result.content
54
+ .filter((c) => c.type === "text")
55
+ .map((c) => (c as { type: "text"; text: string }).text)
56
+ .join("\n");
57
+ console.log("返回结果:", content);
58
+ } catch (e) {
59
+ console.log("⚠️ 调用失败(可能 CRM 后端未启动):", (e as Error).message);
60
+ }
61
+
62
+ await client.close();
63
+ }
64
+
65
+ main().catch((err) => {
66
+ console.error("❌ 测试失败:", err.message);
67
+ process.exit(1);
68
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "Node16",
5
+ "moduleResolution": "Node16",
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "resolveJsonModule": true,
13
+ "declaration": true,
14
+ "sourceMap": true
15
+ },
16
+ "include": ["src/**/*"],
17
+ "exclude": ["node_modules", "dist"]
18
+ }