@creditsync-pro/mcp 1.0.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 ADDED
@@ -0,0 +1,43 @@
1
+ # CreditSync MCP Server
2
+
3
+ MCP server for connecting Claude Desktop to your CreditSync account. Requires a CreditSync Pro subscription.
4
+
5
+ ## Setup
6
+
7
+ 1. In CreditSync, go to **Settings > API** and create an API key
8
+ 2. Copy the key (it's only shown once)
9
+ 3. Add to your Claude Desktop config (`claude_desktop_config.json`):
10
+
11
+ ```json
12
+ {
13
+ "mcpServers": {
14
+ "creditsync": {
15
+ "command": "npx",
16
+ "args": ["@creditsync-pro/mcp"],
17
+ "env": {
18
+ "CREDITSYNC_API_KEY": "cs_your_key_here"
19
+ }
20
+ }
21
+ }
22
+ }
23
+ ```
24
+
25
+ 4. Restart Claude Desktop
26
+
27
+ ## Tools
28
+
29
+ | Tool | Description |
30
+ |------|-------------|
31
+ | `list_benefits` | List all tracked benefits with status, amounts, expiry dates |
32
+ | `get_benefit` | Get detailed info for a specific benefit with all periods |
33
+ | `mark_benefit_used` | Mark a benefit as used, partial, or unused |
34
+ | `get_dashboard` | Dashboard metrics: expiring, captured, available, ROI |
35
+ | `list_cards` | List all cards with fees, renewal months, benefit counts |
36
+
37
+ ## Examples
38
+
39
+ Ask Claude:
40
+ - "What benefits are expiring this week?"
41
+ - "Show me my dashboard"
42
+ - "Mark my Uber Cash as used"
43
+ - "What cards do I have and when do their fees post?"
@@ -0,0 +1,8 @@
1
+ export declare class CreditSyncClient {
2
+ private baseUrl;
3
+ private apiKey;
4
+ constructor(apiKey: string, baseUrl?: string);
5
+ private request;
6
+ get(path: string, params?: Record<string, string>): Promise<unknown>;
7
+ patch(path: string, body: unknown): Promise<unknown>;
8
+ }
@@ -0,0 +1,32 @@
1
+ export class CreditSyncClient {
2
+ baseUrl;
3
+ apiKey;
4
+ constructor(apiKey, baseUrl = "https://www.creditsync.pro") {
5
+ this.apiKey = apiKey;
6
+ this.baseUrl = baseUrl.replace(/\/$/, "");
7
+ }
8
+ async request(method, path, body) {
9
+ const url = `${this.baseUrl}${path}`;
10
+ const res = await fetch(url, {
11
+ method,
12
+ headers: {
13
+ "Authorization": `Bearer ${this.apiKey}`,
14
+ "Content-Type": "application/json",
15
+ },
16
+ ...(body ? { body: JSON.stringify(body) } : {}),
17
+ });
18
+ const data = await res.json();
19
+ if (!res.ok) {
20
+ const message = data.error ?? `API error: ${res.status}`;
21
+ throw new Error(message);
22
+ }
23
+ return data;
24
+ }
25
+ async get(path, params) {
26
+ const query = params ? "?" + new URLSearchParams(params).toString() : "";
27
+ return this.request("GET", path + query);
28
+ }
29
+ async patch(path, body) {
30
+ return this.request("PATCH", path, body);
31
+ }
32
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { z } from "zod";
5
+ import { CreditSyncClient } from "./api-client.js";
6
+ const apiKey = process.env.CREDITSYNC_API_KEY;
7
+ if (!apiKey) {
8
+ console.error("CREDITSYNC_API_KEY environment variable is required");
9
+ process.exit(1);
10
+ }
11
+ const baseUrl = process.env.CREDITSYNC_API_URL ?? "https://www.creditsync.pro";
12
+ const client = new CreditSyncClient(apiKey, baseUrl);
13
+ const server = new McpServer({
14
+ name: "creditsync",
15
+ version: "1.0.0",
16
+ });
17
+ // ─── list_benefits ─────────────────────────────────
18
+ server.tool("list_benefits", "List all tracked credit card benefits with current period status, amounts, and expiry dates. Filter by status (unused/partial/used), card nickname, or expiring within N days.", {
19
+ status: z.enum(["unused", "partial", "used"]).optional().describe("Filter by usage status"),
20
+ card: z.string().optional().describe("Filter by card nickname (partial match)"),
21
+ expiring_within_days: z.number().int().positive().optional().describe("Only return benefits expiring within this many days"),
22
+ }, async ({ status, card, expiring_within_days }) => {
23
+ const params = {};
24
+ if (status)
25
+ params.status = status;
26
+ if (card)
27
+ params.card = card;
28
+ if (expiring_within_days)
29
+ params.expiring_within_days = String(expiring_within_days);
30
+ const data = await client.get("/api/v1/benefits", params);
31
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
32
+ });
33
+ // ─── get_benefit ───────────────────────────────────
34
+ server.tool("get_benefit", "Get detailed information about a specific benefit including all historical periods.", {
35
+ id: z.string().describe("The benefit ID from list_benefits"),
36
+ }, async ({ id }) => {
37
+ const data = await client.get(`/api/v1/benefits/${id}`);
38
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
39
+ });
40
+ // ─── mark_benefit_used ─────────────────────────────
41
+ server.tool("mark_benefit_used", "Mark a benefit period as used, partially used, or unused. Use the period_id from list_benefits or get_benefit.", {
42
+ period_id: z.string().describe("The period ID to update"),
43
+ status: z.enum(["unused", "partial", "used"]).describe("New usage status"),
44
+ amount_used: z.number().positive().optional().describe("Dollar amount used (required for partial status)"),
45
+ notes: z.string().optional().describe("Optional note about usage"),
46
+ }, async ({ period_id, status, amount_used, notes }) => {
47
+ const body = { usageStatus: status };
48
+ if (amount_used !== undefined)
49
+ body.usedAmount = amount_used;
50
+ if (notes !== undefined)
51
+ body.notes = notes;
52
+ const data = await client.patch(`/api/v1/benefits/${period_id}/use`, body);
53
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
54
+ });
55
+ // ─── get_dashboard ─────────────────────────────────
56
+ server.tool("get_dashboard", "Get dashboard metrics: benefits expiring in 30 days, captured this month, total available, and net ROI value (captured vs annual fees).", {}, async () => {
57
+ const data = await client.get("/api/v1/dashboard");
58
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
59
+ });
60
+ // ─── list_cards ────────────────────────────────────
61
+ server.tool("list_cards", "List all credit cards with issuer, annual fee, renewal month, owner, and benefit count.", {}, async () => {
62
+ const data = await client.get("/api/v1/cards");
63
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
64
+ });
65
+ // ─── Start server ──────────────────────────────────
66
+ async function main() {
67
+ const transport = new StdioServerTransport();
68
+ await server.connect(transport);
69
+ }
70
+ main().catch((err) => {
71
+ console.error("MCP server error:", err);
72
+ process.exit(1);
73
+ });
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@creditsync-pro/mcp",
3
+ "version": "1.0.0",
4
+ "description": "CreditSync MCP server for Claude Desktop",
5
+ "type": "module",
6
+ "bin": {
7
+ "creditsync-mcp": "dist/index.js",
8
+ "creditsync-pro-mcp": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "start": "node dist/index.js"
13
+ },
14
+ "dependencies": {
15
+ "@modelcontextprotocol/sdk": "^1.12.1",
16
+ "zod": "^3.24.4"
17
+ },
18
+ "devDependencies": {
19
+ "typescript": "^5.7.3"
20
+ }
21
+ }
@@ -0,0 +1,39 @@
1
+ export class CreditSyncClient {
2
+ private baseUrl: string;
3
+ private apiKey: string;
4
+
5
+ constructor(apiKey: string, baseUrl = "https://www.creditsync.pro") {
6
+ this.apiKey = apiKey;
7
+ this.baseUrl = baseUrl.replace(/\/$/, "");
8
+ }
9
+
10
+ private async request(method: string, path: string, body?: unknown): Promise<unknown> {
11
+ const url = `${this.baseUrl}${path}`;
12
+ const res = await fetch(url, {
13
+ method,
14
+ headers: {
15
+ "Authorization": `Bearer ${this.apiKey}`,
16
+ "Content-Type": "application/json",
17
+ },
18
+ ...(body ? { body: JSON.stringify(body) } : {}),
19
+ });
20
+
21
+ const data = await res.json();
22
+
23
+ if (!res.ok) {
24
+ const message = (data as { error?: string }).error ?? `API error: ${res.status}`;
25
+ throw new Error(message);
26
+ }
27
+
28
+ return data;
29
+ }
30
+
31
+ async get(path: string, params?: Record<string, string>): Promise<unknown> {
32
+ const query = params ? "?" + new URLSearchParams(params).toString() : "";
33
+ return this.request("GET", path + query);
34
+ }
35
+
36
+ async patch(path: string, body: unknown): Promise<unknown> {
37
+ return this.request("PATCH", path, body);
38
+ }
39
+ }
package/src/index.ts ADDED
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { z } from "zod";
6
+ import { CreditSyncClient } from "./api-client.js";
7
+
8
+ const apiKey = process.env.CREDITSYNC_API_KEY;
9
+ if (!apiKey) {
10
+ console.error("CREDITSYNC_API_KEY environment variable is required");
11
+ process.exit(1);
12
+ }
13
+
14
+ const baseUrl = process.env.CREDITSYNC_API_URL ?? "https://www.creditsync.pro";
15
+ const client = new CreditSyncClient(apiKey, baseUrl);
16
+
17
+ const server = new McpServer({
18
+ name: "creditsync",
19
+ version: "1.0.0",
20
+ });
21
+
22
+ // ─── list_benefits ─────────────────────────────────
23
+
24
+ server.tool(
25
+ "list_benefits",
26
+ "List all tracked credit card benefits with current period status, amounts, and expiry dates. Filter by status (unused/partial/used), card nickname, or expiring within N days.",
27
+ {
28
+ status: z.enum(["unused", "partial", "used"]).optional().describe("Filter by usage status"),
29
+ card: z.string().optional().describe("Filter by card nickname (partial match)"),
30
+ expiring_within_days: z.number().int().positive().optional().describe("Only return benefits expiring within this many days"),
31
+ },
32
+ async ({ status, card, expiring_within_days }) => {
33
+ const params: Record<string, string> = {};
34
+ if (status) params.status = status;
35
+ if (card) params.card = card;
36
+ if (expiring_within_days) params.expiring_within_days = String(expiring_within_days);
37
+
38
+ const data = await client.get("/api/v1/benefits", params);
39
+ return { content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }] };
40
+ },
41
+ );
42
+
43
+ // ─── get_benefit ───────────────────────────────────
44
+
45
+ server.tool(
46
+ "get_benefit",
47
+ "Get detailed information about a specific benefit including all historical periods.",
48
+ {
49
+ id: z.string().describe("The benefit ID from list_benefits"),
50
+ },
51
+ async ({ id }) => {
52
+ const data = await client.get(`/api/v1/benefits/${id}`);
53
+ return { content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }] };
54
+ },
55
+ );
56
+
57
+ // ─── mark_benefit_used ─────────────────────────────
58
+
59
+ server.tool(
60
+ "mark_benefit_used",
61
+ "Mark a benefit period as used, partially used, or unused. Use the period_id from list_benefits or get_benefit.",
62
+ {
63
+ period_id: z.string().describe("The period ID to update"),
64
+ status: z.enum(["unused", "partial", "used"]).describe("New usage status"),
65
+ amount_used: z.number().positive().optional().describe("Dollar amount used (required for partial status)"),
66
+ notes: z.string().optional().describe("Optional note about usage"),
67
+ },
68
+ async ({ period_id, status, amount_used, notes }) => {
69
+ const body: Record<string, unknown> = { usageStatus: status };
70
+ if (amount_used !== undefined) body.usedAmount = amount_used;
71
+ if (notes !== undefined) body.notes = notes;
72
+
73
+ const data = await client.patch(`/api/v1/benefits/${period_id}/use`, body);
74
+ return { content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }] };
75
+ },
76
+ );
77
+
78
+ // ─── get_dashboard ─────────────────────────────────
79
+
80
+ server.tool(
81
+ "get_dashboard",
82
+ "Get dashboard metrics: benefits expiring in 30 days, captured this month, total available, and net ROI value (captured vs annual fees).",
83
+ {},
84
+ async () => {
85
+ const data = await client.get("/api/v1/dashboard");
86
+ return { content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }] };
87
+ },
88
+ );
89
+
90
+ // ─── list_cards ────────────────────────────────────
91
+
92
+ server.tool(
93
+ "list_cards",
94
+ "List all credit cards with issuer, annual fee, renewal month, owner, and benefit count.",
95
+ {},
96
+ async () => {
97
+ const data = await client.get("/api/v1/cards");
98
+ return { content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }] };
99
+ },
100
+ );
101
+
102
+ // ─── Start server ──────────────────────────────────
103
+
104
+ async function main() {
105
+ const transport = new StdioServerTransport();
106
+ await server.connect(transport);
107
+ }
108
+
109
+ main().catch((err) => {
110
+ console.error("MCP server error:", err);
111
+ process.exit(1);
112
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
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
+ "declaration": true
12
+ },
13
+ "include": ["src"]
14
+ }