@echomate/mcp-server 0.0.0-stage → 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Valerian Huber
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,62 @@
1
- # Temporary Holding Version
1
+ # @echomate/mcp-server
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ MCP server for [echomate](https://echomate.shop) — the AI-powered Shopify app for QR codes, dynamic links, analytics, store health, and link-in-bio.
4
+
5
+ **20 tools** for full zero-click store management via Claude, ChatGPT, or any MCP client.
6
+
7
+ ## Quick Start
8
+
9
+ ### Claude Desktop / Claude Code
10
+
11
+ Add to your MCP config:
12
+
13
+ ```json
14
+ {
15
+ "mcpServers": {
16
+ "echomate": {
17
+ "command": "npx",
18
+ "args": ["-y", "@echomate/mcp-server"],
19
+ "env": {
20
+ "ECHOMATE_API_KEY": "em_live_your-key-here"
21
+ }
22
+ }
23
+ }
24
+ }
25
+ ```
26
+
27
+ ### Claude.ai (Remote MCP)
28
+
29
+ URL: `https://api.echomate.shop/mcp`
30
+ Auth: API Key header `X-API-Key: em_live_...`
31
+
32
+ ## Getting Your API Key
33
+
34
+ 1. Open echomate in your Shopify admin
35
+ 2. Go to **Settings** > **API Key**
36
+ 3. Click **Generate API Key**
37
+ 4. Copy the key (shown only once!)
38
+
39
+ Which plan includes API access is shown in the plan comparison inside echomate — prices are not repeated here, they change.
40
+
41
+ ## Tools
42
+
43
+ Your MCP client lists every tool with what it does and its parameters. An AI gets
44
+ the same list, together with the usage rules, by calling `get_setup_guide`.
45
+
46
+ ## Example Prompts
47
+
48
+ > "Show me my top performing QR codes this month"
49
+
50
+ > "Create 5 QR codes for my products with UTM tracking"
51
+
52
+ > "How fast is my store? What apps are slowing it down?"
53
+
54
+ > "Set up my Link-in-Bio page with my top 5 links"
55
+
56
+ ## License
57
+
58
+ MIT, see [LICENSE](./LICENSE). Using the echomate API requires an echomate plan that includes API access.
59
+
60
+ Contributions are accepted under the same MIT license (inbound = outbound), with a DCO sign-off (`git commit -s`).
61
+
62
+ To report a vulnerability, see [SECURITY.md](./SECURITY.md).
package/SECURITY.md ADDED
@@ -0,0 +1,21 @@
1
+ # Security Policy
2
+
3
+ ## Reporting a vulnerability
4
+
5
+ Please report security issues **privately** to **security@alpin-code.de**. Do not open a public GitHub issue.
6
+
7
+ Include, if you can: the affected package and version, a description of the issue, steps to reproduce, and whether you know
8
+ of active exploitation.
9
+
10
+ We acknowledge reports within 2 business days. If a vulnerability in this package is actively exploited, we notify the
11
+ competent authorities under Article 14 of the EU Cyber Resilience Act (Regulation (EU) 2024/2847) and inform affected users,
12
+ including the steps they should take.
13
+
14
+ ## Supported versions
15
+
16
+ Only the latest published version receives security fixes. Releases are published to npm by the maintainer from our own CI.
17
+
18
+ ## Your API key
19
+
20
+ This package only sends your API key to `https://api.echomate.shop`, or to the URL you set yourself in `ECHOMATE_API_URL`. If you suspect your key was exposed, rotate it in the app
21
+ settings immediately.
@@ -0,0 +1,83 @@
1
+ import type { QRCode, ShortLink, PaginatedResponse, AnalyticsResponse, HealthScore, LinkDetail, TopPerformers, ShopSettings, BioConfig, ShopIdentity, ScanResult } from "./types.js";
2
+ export declare function sanitizeForAI(text: string): string;
3
+ export declare const ZUGANG_KOPF = "x-echomate-zugang";
4
+ export declare const ZUGANG_MCP = "mcp";
5
+ export declare class EchomateClient {
6
+ private apiKey;
7
+ private baseUrl;
8
+ private art;
9
+ constructor(apiKey: string, baseUrl: string, art?: "schluessel" | "bearer");
10
+ private request;
11
+ listQRCodes(page?: number, pageSize?: number): Promise<PaginatedResponse<QRCode>>;
12
+ createQRCode(name: string, targetUrl: string): Promise<{
13
+ data: QRCode;
14
+ }>;
15
+ getQRImageUrl(id: string, format?: "png" | "svg"): string;
16
+ getQRImageSvg(id: string): Promise<string>;
17
+ listLinks(page?: number, pageSize?: number): Promise<PaginatedResponse<ShortLink>>;
18
+ createLink(params: {
19
+ targetUrl: string;
20
+ customSlug?: string;
21
+ utmSource?: string;
22
+ utmMedium?: string;
23
+ utmCampaign?: string;
24
+ }): Promise<{
25
+ data: ShortLink;
26
+ }>;
27
+ getAnalytics(days?: number): Promise<AnalyticsResponse>;
28
+ getMe(): Promise<ShopIdentity>;
29
+ getQRCode(id: string): Promise<{
30
+ data: QRCode;
31
+ }>;
32
+ updateQRCode(id: string, input: {
33
+ name?: string;
34
+ targetUrl?: string;
35
+ isActive?: boolean;
36
+ }): Promise<{
37
+ data: QRCode;
38
+ }>;
39
+ deleteQRCode(id: string): Promise<{
40
+ deleted: boolean;
41
+ }>;
42
+ getLinkDetails(id: string): Promise<{
43
+ data: LinkDetail;
44
+ }>;
45
+ updateLink(id: string, input: {
46
+ targetUrl?: string;
47
+ utmSource?: string;
48
+ utmMedium?: string;
49
+ utmCampaign?: string;
50
+ isActive?: boolean;
51
+ }): Promise<{
52
+ data: ShortLink;
53
+ }>;
54
+ deleteLink(id: string): Promise<{
55
+ deleted: boolean;
56
+ }>;
57
+ getSettings(): Promise<{
58
+ data: ShopSettings;
59
+ }>;
60
+ updateSettings(input: Partial<ShopSettings>): Promise<{
61
+ data: ShopSettings;
62
+ }>;
63
+ getHealthScores(): Promise<{
64
+ data: HealthScore;
65
+ }>;
66
+ triggerHealthScan(): Promise<{
67
+ data: ScanResult;
68
+ }>;
69
+ getTopPerformers(days?: number): Promise<{
70
+ data: TopPerformers;
71
+ }>;
72
+ getBio(): Promise<{
73
+ data: BioConfig;
74
+ }>;
75
+ updateBio(input: Partial<BioConfig>): Promise<{
76
+ updated: boolean;
77
+ }>;
78
+ healthCheck(): Promise<{
79
+ status: "healthy" | "degraded" | "unhealthy";
80
+ db?: string;
81
+ }>;
82
+ getBaseUrl(): string;
83
+ }
package/dist/client.js ADDED
@@ -0,0 +1,142 @@
1
+ export function sanitizeForAI(text) {
2
+ if (!text)
3
+ return "";
4
+ return text
5
+ .replace(/<[^>]*>/g, "")
6
+ .replace(/</g, "&lt;")
7
+ .replace(/>/g, "&gt;")
8
+ .slice(0, 500);
9
+ }
10
+ export const ZUGANG_KOPF = "x-echomate-zugang";
11
+ export const ZUGANG_MCP = "mcp";
12
+ export class EchomateClient {
13
+ apiKey;
14
+ baseUrl;
15
+ art;
16
+ constructor(apiKey, baseUrl, art = "schluessel") {
17
+ this.apiKey = apiKey;
18
+ this.art = art;
19
+ this.baseUrl = baseUrl.replace(/\/+$/, "");
20
+ }
21
+ async request(path, options = {}) {
22
+ const url = `${this.baseUrl}${path}`;
23
+ const internalSecret = process.env.MCP_INTERNAL_SECRET;
24
+ const response = await fetch(url, {
25
+ ...options,
26
+ headers: {
27
+ "Content-Type": "application/json",
28
+ ...(this.art === "bearer" ? { Authorization: `Bearer ${this.apiKey}` } : { "X-API-Key": this.apiKey }),
29
+ ...(internalSecret ? { "X-Echomate-Internal": internalSecret } : {}),
30
+ ...options.headers,
31
+ [ZUGANG_KOPF]: ZUGANG_MCP,
32
+ },
33
+ });
34
+ if (!response.ok) {
35
+ const body = await response.text();
36
+ throw new Error(`echomate API error ${response.status}: ${body}`);
37
+ }
38
+ return response.json();
39
+ }
40
+ async listQRCodes(page = 1, pageSize = 25) {
41
+ return this.request(`/api/v1/qr?page=${page}&pageSize=${pageSize}`);
42
+ }
43
+ async createQRCode(name, targetUrl) {
44
+ return this.request("/api/v1/qr", {
45
+ method: "POST",
46
+ body: JSON.stringify({ name, targetUrl }),
47
+ });
48
+ }
49
+ getQRImageUrl(id, format = "svg") {
50
+ return `${this.baseUrl}/api/v1/qr/${id}/image?format=${format}`;
51
+ }
52
+ async getQRImageSvg(id) {
53
+ const internalSecret = process.env.MCP_INTERNAL_SECRET;
54
+ const response = await fetch(`${this.baseUrl}/api/v1/qr/${encodeURIComponent(id)}/image?format=svg&size=320`, {
55
+ headers: {
56
+ ...(this.art === "bearer" ? { Authorization: `Bearer ${this.apiKey}` } : { "X-API-Key": this.apiKey }),
57
+ ...(internalSecret ? { "X-Echomate-Internal": internalSecret } : {}),
58
+ [ZUGANG_KOPF]: ZUGANG_MCP,
59
+ },
60
+ });
61
+ if (!response.ok) {
62
+ const body = await response.text();
63
+ throw new Error(`echomate API error ${response.status}: ${body}`);
64
+ }
65
+ return response.text();
66
+ }
67
+ async listLinks(page = 1, pageSize = 25) {
68
+ return this.request(`/api/v1/links?page=${page}&pageSize=${pageSize}`);
69
+ }
70
+ async createLink(params) {
71
+ return this.request("/api/v1/links", {
72
+ method: "POST",
73
+ body: JSON.stringify(params),
74
+ });
75
+ }
76
+ async getAnalytics(days = 7) {
77
+ return this.request(`/api/v1/analytics?days=${days}`);
78
+ }
79
+ async getMe() {
80
+ return this.request("/api/v1/me");
81
+ }
82
+ async getQRCode(id) {
83
+ return this.request(`/api/v1/qr/${id}`);
84
+ }
85
+ async updateQRCode(id, input) {
86
+ return this.request(`/api/v1/qr/${id}`, {
87
+ method: "PATCH",
88
+ body: JSON.stringify(input),
89
+ });
90
+ }
91
+ async deleteQRCode(id) {
92
+ return this.request(`/api/v1/qr/${id}`, { method: "DELETE" });
93
+ }
94
+ async getLinkDetails(id) {
95
+ return this.request(`/api/v1/links/${id}`);
96
+ }
97
+ async updateLink(id, input) {
98
+ return this.request(`/api/v1/links/${id}`, {
99
+ method: "PATCH",
100
+ body: JSON.stringify(input),
101
+ });
102
+ }
103
+ async deleteLink(id) {
104
+ return this.request(`/api/v1/links/${id}`, { method: "DELETE" });
105
+ }
106
+ async getSettings() {
107
+ return this.request("/api/v1/settings");
108
+ }
109
+ async updateSettings(input) {
110
+ return this.request("/api/v1/settings", {
111
+ method: "PATCH",
112
+ body: JSON.stringify(input),
113
+ });
114
+ }
115
+ async getHealthScores() {
116
+ return this.request("/api/v1/health-scores");
117
+ }
118
+ async triggerHealthScan() {
119
+ return this.request("/api/v1/health-scan", { method: "POST" });
120
+ }
121
+ async getTopPerformers(days = 30) {
122
+ return this.request(`/api/v1/top-performers?days=${days}`);
123
+ }
124
+ async getBio() {
125
+ return this.request("/api/v1/spotlight");
126
+ }
127
+ async updateBio(input) {
128
+ return this.request("/api/v1/spotlight", {
129
+ method: "PATCH",
130
+ body: JSON.stringify(input),
131
+ });
132
+ }
133
+ async healthCheck() {
134
+ const url = `${this.baseUrl}/health`;
135
+ const res = await fetch(url);
136
+ return res.json();
137
+ }
138
+ getBaseUrl() {
139
+ return this.baseUrl;
140
+ }
141
+ }
142
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare const ECHOMATE_GUIDE = "# echomate MCP Server \u2014 Setup Guide\n\n## What is echomate?\nechomate is a Shopify app for QR codes, dynamic links, analytics, store health monitoring, and link-in-bio pages.\n\n## Tools\nYour MCP client lists every available tool with its description \u2014 this guide\ndoes not repeat them. Some installations are read-only, and deleting is switched\noff unless ECHOMATE_MCP_ALLOW_DELETE=true is set, so the list can be shorter.\n\n## Example Workflows\n\n### \"How are my QR codes performing?\"\n1. get_analytics (days=30)\n2. get_top_performers (days=30)\n3. AI summarizes: top performers, trends, recommendations\n\n### \"Create a campaign with tracking\"\n1. create_link with UTM params (source, medium, campaign)\n2. create_qr_code pointing to the short link\n3. get_qr_image to download the QR code\n\n### \"Is my store fast enough?\"\n1. get_store_health \u2014 check current scores\n2. If score < 80: check appImpacts for slow apps\n3. trigger_health_scan \u2014 get fresh data after changes\n\n## Setup\n1. In echomate app: Settings \u2192 API Key \u2192 Generate\n2. Copy the key (shown once!)\n3. Add to your MCP client config with env var ECHOMATE_API_KEY\n";
package/dist/guide.js ADDED
@@ -0,0 +1,33 @@
1
+ export const ECHOMATE_GUIDE = `# echomate MCP Server — Setup Guide
2
+
3
+ ## What is echomate?
4
+ echomate is a Shopify app for QR codes, dynamic links, analytics, store health monitoring, and link-in-bio pages.
5
+
6
+ ## Tools
7
+ Your MCP client lists every available tool with its description — this guide
8
+ does not repeat them. Some installations are read-only, and deleting is switched
9
+ off unless ECHOMATE_MCP_ALLOW_DELETE=true is set, so the list can be shorter.
10
+
11
+ ## Example Workflows
12
+
13
+ ### "How are my QR codes performing?"
14
+ 1. get_analytics (days=30)
15
+ 2. get_top_performers (days=30)
16
+ 3. AI summarizes: top performers, trends, recommendations
17
+
18
+ ### "Create a campaign with tracking"
19
+ 1. create_link with UTM params (source, medium, campaign)
20
+ 2. create_qr_code pointing to the short link
21
+ 3. get_qr_image to download the QR code
22
+
23
+ ### "Is my store fast enough?"
24
+ 1. get_store_health — check current scores
25
+ 2. If score < 80: check appImpacts for slow apps
26
+ 3. trigger_health_scan — get fresh data after changes
27
+
28
+ ## Setup
29
+ 1. In echomate app: Settings → API Key → Generate
30
+ 2. Copy the key (shown once!)
31
+ 3. Add to your MCP client config with env var ECHOMATE_API_KEY
32
+ `;
33
+ //# sourceMappingURL=guide.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guide.js","sourceRoot":"","sources":["../src/guide.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+B7B,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,115 @@
1
+ import http from "node:http";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
4
+ import { EchomateClient } from "./client.js";
5
+ import { registerTools } from "./tools.js";
6
+ import { SERVER_ANLEITUNG } from "./instructions.js";
7
+ import { SERVER_INFO } from "./server-info.js";
8
+ import { leseZugang, oauthAn, wwwAuthenticate } from "./zugang.js";
9
+ const MCP_PORT = parseInt(process.env.MCP_PORT || "3847", 10);
10
+ const ECHOMATE_API_URL = process.env.ECHOMATE_API_URL || "https://api.echomate.shop";
11
+ const CORS_HEADERS = {
12
+ "Access-Control-Allow-Origin": "*",
13
+ "Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
14
+ "Access-Control-Allow-Headers": "Content-Type, X-API-Key, Authorization, Mcp-Protocol-Version, Mcp-Session-Id",
15
+ "Access-Control-Expose-Headers": "WWW-Authenticate, Mcp-Session-Id",
16
+ "Access-Control-Max-Age": "86400",
17
+ };
18
+ function antworte(res, status, koerper, zusatz = {}) {
19
+ res.writeHead(status, { ...CORS_HEADERS, "Content-Type": "application/json", ...zusatz });
20
+ res.end(JSON.stringify(koerper));
21
+ }
22
+ function anmeldungNoetig(res, koerper, fehler) {
23
+ const zusatz = oauthAn(process.env.MCP_OAUTH_LAEDEN)
24
+ ? { "WWW-Authenticate": wwwAuthenticate(fehler) }
25
+ : {};
26
+ antworte(res, 401, koerper, zusatz);
27
+ }
28
+ const server = http.createServer(async (req, res) => {
29
+ const url = (req.url || "/").replace(/^\/mcp/, "") || "/";
30
+ if (req.method === "OPTIONS") {
31
+ res.writeHead(204, CORS_HEADERS);
32
+ res.end();
33
+ return;
34
+ }
35
+ if (req.method === "GET" && url === "/" && String(req.headers.accept ?? "").includes("text/event-stream")) {
36
+ antworte(res, 405, { error: "This MCP server is stateless; use POST." }, { Allow: "POST" });
37
+ return;
38
+ }
39
+ if (req.method === "GET" && (url === "/" || url === "/health")) {
40
+ antworte(res, 200, { status: "ok", service: "echomate-mcp", version: "1.0.1" });
41
+ return;
42
+ }
43
+ if (req.method === "DELETE" && url === "/") {
44
+ antworte(res, 405, { error: "This MCP server is stateless; there is no session to end." }, { Allow: "POST" });
45
+ return;
46
+ }
47
+ if (req.method === "POST") {
48
+ const zugang = leseZugang(req.headers);
49
+ if (zugang.art === "mehrdeutig") {
50
+ antworte(res, 400, { error: zugang.grund });
51
+ return;
52
+ }
53
+ if (zugang.art === "fehlt") {
54
+ anmeldungNoetig(res, { error: "Authentication required. Connect echomate via OAuth or send an X-API-Key header." });
55
+ return;
56
+ }
57
+ if (zugang.art === "ungueltig") {
58
+ anmeldungNoetig(res, { error: zugang.grund }, "invalid_token");
59
+ return;
60
+ }
61
+ const internalSecret = process.env.MCP_INTERNAL_SECRET;
62
+ try {
63
+ const meRes = await fetch(`${ECHOMATE_API_URL}/api/v1/me`, {
64
+ headers: {
65
+ ...(zugang.art === "bearer" ? { Authorization: `Bearer ${zugang.wert}` } : { "X-API-Key": zugang.wert }),
66
+ ...(internalSecret ? { "X-Echomate-Internal": internalSecret } : {}),
67
+ ...(req.headers["x-forwarded-for"] ? { "X-Forwarded-For": String(req.headers["x-forwarded-for"]) } : {}),
68
+ },
69
+ });
70
+ if (!meRes.ok) {
71
+ if (zugang.art === "bearer" && meRes.status === 401) {
72
+ anmeldungNoetig(res, { error: "The access token is invalid or expired." }, "invalid_token");
73
+ return;
74
+ }
75
+ let abgelaufen = null;
76
+ try {
77
+ const koerper = (await meRes.json());
78
+ if (koerper?.code === "API_KEY_EXPIRED" && typeof koerper.error === "string") {
79
+ abgelaufen = { error: koerper.error, code: "API_KEY_EXPIRED" };
80
+ }
81
+ }
82
+ catch {
83
+ }
84
+ antworte(res, meRes.status, abgelaufen ?? { error: "API key validation failed" });
85
+ return;
86
+ }
87
+ }
88
+ catch {
89
+ antworte(res, 502, { error: "Failed to connect to echomate API" });
90
+ return;
91
+ }
92
+ const mcpServer = new McpServer(SERVER_INFO, { instructions: SERVER_ANLEITUNG });
93
+ const client = new EchomateClient(zugang.wert, ECHOMATE_API_URL, zugang.art);
94
+ registerTools(mcpServer, { client });
95
+ const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
96
+ for (const [key, val] of Object.entries(CORS_HEADERS)) {
97
+ res.setHeader(key, val);
98
+ }
99
+ try {
100
+ await mcpServer.connect(transport);
101
+ await transport.handleRequest(req, res);
102
+ }
103
+ catch {
104
+ if (!res.headersSent) {
105
+ antworte(res, 500, { error: "Internal MCP server error" });
106
+ }
107
+ }
108
+ return;
109
+ }
110
+ antworte(res, 404, { error: "Not found" });
111
+ });
112
+ server.listen(MCP_PORT, () => {
113
+ console.log(`echomate MCP HTTP server listening on port ${MCP_PORT}`);
114
+ });
115
+ //# sourceMappingURL=http-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-server.js","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
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 { EchomateClient } from "./client.js";
5
+ import { registerTools } from "./tools.js";
6
+ import { SERVER_ANLEITUNG } from "./instructions.js";
7
+ import { SERVER_INFO } from "./server-info.js";
8
+ const ECHOMATE_API_KEY = process.env.ECHOMATE_API_KEY;
9
+ const ECHOMATE_API_URL = process.env.ECHOMATE_API_URL || "https://api.echomate.shop";
10
+ if (!ECHOMATE_API_KEY) {
11
+ console.error("Error: ECHOMATE_API_KEY environment variable is required.\n" +
12
+ "Set it in your MCP client config (e.g. Claude Desktop or .mcp.json).");
13
+ process.exit(1);
14
+ }
15
+ const client = new EchomateClient(ECHOMATE_API_KEY, ECHOMATE_API_URL);
16
+ const server = new McpServer(SERVER_INFO, { instructions: SERVER_ANLEITUNG });
17
+ registerTools(server, { client });
18
+ const transport = new StdioServerTransport();
19
+ server.connect(transport).catch((err) => {
20
+ console.error("Failed to start MCP server:", err);
21
+ process.exit(1);
22
+ });
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAM/C,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;AACtD,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,2BAA2B,CAAC;AAErF,IAAI,CAAC,gBAAgB,EAAE,CAAC;IACtB,OAAO,CAAC,KAAK,CACX,6DAA6D;QAC3D,sEAAsE,CACzE,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAMD,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAC;AAKtE,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,WAAW,EAAE,EAAE,YAAY,EAAE,gBAAgB,EAAE,CAAC,CAAC;AAG9E,aAAa,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;AAGlC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC/C,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;IAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export declare const SERVER_ANLEITUNG = "You are helping a Shopify merchant run **echomate** \u2014 trackable QR codes and short links, analytics on what they bring in, a store speed check, a link page and a loyalty stamp card.\n\n## Vocabulary \u2014 use the words the merchant sees in the echomate admin\n\n- **QR code** \u2014 a printable code. Every QR code points at a short link underneath; the QR code is the picture, the short link is what gets redirected and counted.\n- **Short link** \u2014 the redirect itself (`/r/<slug>`). Not every short link has a QR code. Changing a short link's target changes where every printed copy of its QR code sends people.\n- **Scan** and **click** \u2014 two separate counters. A scan is a visit through a short link that belongs to a QR code; a click is a visit through a short link without one. Neither is an order.\n- **Conversion** \u2014 an order attributed to a scan or click. Conversions are counted separately and are never part of the scan or click numbers.\n- **Placement** \u2014 where a QR code is used: a medium (Flyer, Hangtag, Packaging, Receipt \u2026) plus an optional description of the spot. It is how analytics compares printed pieces.\n- **Spotlight** \u2014 the merchant's public link page at `/s/<slug>`. The tool `manage_bio` manages it; \"bio\" is only the internal name \u2014 say \"Spotlight page\" to the merchant.\n- **Stamp Card** \u2014 the loyalty card customers collect stamps on through their orders.\n\n## Where a visitor actually lands \u2014 the order matters\n\nWhen someone opens a short link, echomate decides in this order, and the first rule that applies ends it:\n\n1. Switched off \u2192 the visitor sees a page saying the link is inactive. A link is switched off only by the merchant. echomate never pauses a link for lack of visits, on any plan; a link that has reached its own end date or click limit is handled in step 3.\n2. Not started yet \u2192 a \"not live yet\" page.\n3. Expired, or its click limit reached \u2192 the expiry page or the configured fallback URL.\n4. Routing rules (country, device, language, time) are checked top to bottom; the **first** matching rule sets the target. A later rule that is fully covered by an earlier one never fires. No match \u2192 the short link's own target.\n5. A/B tests belong to a row. A rule can run its own test: then only the visitors that rule matches are split between its target and its variant B. The link's own A/B test covers **only visitors no rule matches** \u2014 it never overrides a rule.\n\nSo \"where does a visitor from Germany land?\" depends on all five steps, not only on the rules.\n\n## Writes take effect immediately \u2014 except one case\n\nEverything below changes the live store the moment it runs, with a single exception: **a new destination for a QR code that exists on printed material**. There echomate prepares the change and the merchant confirms it in the admin; the tool answers with applied: false and the reason \"printed\". Say that you prepared it and where to confirm it \u2014 do not claim it is live, and do not retry.\n\nThere is no confirmation step for anything else. `create_qr_code`, `create_link`, `update_qr_code`, `update_link`, `update_settings`, `manage_bio` and `trigger_health_scan` change the live store the moment they run. Say what you changed, not what you \"prepared\".\n\n- Changing a target changes where **every printed copy** points, from the next scan on. That is why a printed code takes the confirmation route: the read tool for a single QR code reports \"printed\": true for those, so you can say in advance that the merchant will have to confirm.\n- **Deleting is switched off by default.** `delete_qr_code` and `delete_link` only exist when the installation sets `ECHOMATE_MCP_ALLOW_DELETE=true`. If they are available: deleting a QR code also deletes its short link and its entire visit history, and every printed copy stops working. This cannot be undone \u2014 only delete on the merchant's explicit request for that specific item.\n- Some installations are read-only and offer no write tools at all. If a tool is not in your list, it is not available \u2014 do not tell the merchant it failed.\n\n## Plans and limits\n\nFeatures and limits depend on the merchant's plan. Call `diagnose_setup` for the current plan and limits instead of stating any from memory. If something is not included, say so and name the plan that includes it \u2014 do not guess prices.\n\n## Reading the data honestly\n\n- `0` means genuinely none \u2014 a new store has 0 scans. Never infer a malfunction from an empty result; if a tool fails, it says so.\n- Some numbers are exact, some are a sample. `breakdownsTruncated: true` means the device, country and browser breakdowns cover only the most recent events. `overviewAccuracy` (and `accuracy` on a single link) says per number whether it is `exact` or `sample` \u2014 pass that on instead of presenting a sample as a total.\n- API days are UTC calendar days (`period.timezone` says so). The analytics page in the echomate admin counts in the store's time zone, so an order near midnight can land on a different day or outside the window. If the merchant compares totals with the page, explain that before suspecting an error.\n- **Other** and **Unknown** are collective categories, not browsers or devices: \"Other\" means detection found nothing it knows, \"Unknown\" means the value was missing.\n- Breakdowns count visits (scans and clicks). Orders carry no device, browser, country or referrer and are not part of them.\n- Revenue is the Shopify order total, including shipping and taxes; refunds are not deducted. If the merchant compares it with a net-sales figure, explain that difference first.\n- Never invent field values. If something is not in a response, it is not available.\n\n## Showing things to the merchant\n\nWhen the merchant wants to **see** a QR code, call `show_qr_code` \u2014 in chats that support it, it appears as a card with the code image, total scans and a button into the echomate admin. For \"which codes do best\", call `show_top_qr_codes`. The list and analytics tools are for looking things up; use them to find the ID first, not to show. `update_qr_code` shows a receipt card of what changed. Where no card appears, the same tools still answer in text \u2014 describe the result as usual.\n\n## What you cannot see or do \u2014 that is deliberate\n\n- Customer personal data (names, addresses, order contents) is not exposed.\n- You never see API keys and cannot create or revoke them; the merchant does that in the echomate settings.\n- Links that lead to a customer's order page never appear on the public Spotlight page, even if selected.";
@@ -0,0 +1,58 @@
1
+ export const SERVER_ANLEITUNG = `You are helping a Shopify merchant run **echomate** — trackable QR codes and short links, analytics on what they bring in, a store speed check, a link page and a loyalty stamp card.
2
+
3
+ ## Vocabulary — use the words the merchant sees in the echomate admin
4
+
5
+ - **QR code** — a printable code. Every QR code points at a short link underneath; the QR code is the picture, the short link is what gets redirected and counted.
6
+ - **Short link** — the redirect itself (\`/r/<slug>\`). Not every short link has a QR code. Changing a short link's target changes where every printed copy of its QR code sends people.
7
+ - **Scan** and **click** — two separate counters. A scan is a visit through a short link that belongs to a QR code; a click is a visit through a short link without one. Neither is an order.
8
+ - **Conversion** — an order attributed to a scan or click. Conversions are counted separately and are never part of the scan or click numbers.
9
+ - **Placement** — where a QR code is used: a medium (Flyer, Hangtag, Packaging, Receipt …) plus an optional description of the spot. It is how analytics compares printed pieces.
10
+ - **Spotlight** — the merchant's public link page at \`/s/<slug>\`. The tool \`manage_bio\` manages it; "bio" is only the internal name — say "Spotlight page" to the merchant.
11
+ - **Stamp Card** — the loyalty card customers collect stamps on through their orders.
12
+
13
+ ## Where a visitor actually lands — the order matters
14
+
15
+ When someone opens a short link, echomate decides in this order, and the first rule that applies ends it:
16
+
17
+ 1. Switched off → the visitor sees a page saying the link is inactive. A link is switched off only by the merchant. echomate never pauses a link for lack of visits, on any plan; a link that has reached its own end date or click limit is handled in step 3.
18
+ 2. Not started yet → a "not live yet" page.
19
+ 3. Expired, or its click limit reached → the expiry page or the configured fallback URL.
20
+ 4. Routing rules (country, device, language, time) are checked top to bottom; the **first** matching rule sets the target. A later rule that is fully covered by an earlier one never fires. No match → the short link's own target.
21
+ 5. A/B tests belong to a row. A rule can run its own test: then only the visitors that rule matches are split between its target and its variant B. The link's own A/B test covers **only visitors no rule matches** — it never overrides a rule.
22
+
23
+ So "where does a visitor from Germany land?" depends on all five steps, not only on the rules.
24
+
25
+ ## Writes take effect immediately — except one case
26
+
27
+ Everything below changes the live store the moment it runs, with a single exception: **a new destination for a QR code that exists on printed material**. There echomate prepares the change and the merchant confirms it in the admin; the tool answers with applied: false and the reason "printed". Say that you prepared it and where to confirm it — do not claim it is live, and do not retry.
28
+
29
+ There is no confirmation step for anything else. \`create_qr_code\`, \`create_link\`, \`update_qr_code\`, \`update_link\`, \`update_settings\`, \`manage_bio\` and \`trigger_health_scan\` change the live store the moment they run. Say what you changed, not what you "prepared".
30
+
31
+ - Changing a target changes where **every printed copy** points, from the next scan on. That is why a printed code takes the confirmation route: the read tool for a single QR code reports "printed": true for those, so you can say in advance that the merchant will have to confirm.
32
+ - **Deleting is switched off by default.** \`delete_qr_code\` and \`delete_link\` only exist when the installation sets \`ECHOMATE_MCP_ALLOW_DELETE=true\`. If they are available: deleting a QR code also deletes its short link and its entire visit history, and every printed copy stops working. This cannot be undone — only delete on the merchant's explicit request for that specific item.
33
+ - Some installations are read-only and offer no write tools at all. If a tool is not in your list, it is not available — do not tell the merchant it failed.
34
+
35
+ ## Plans and limits
36
+
37
+ Features and limits depend on the merchant's plan. Call \`diagnose_setup\` for the current plan and limits instead of stating any from memory. If something is not included, say so and name the plan that includes it — do not guess prices.
38
+
39
+ ## Reading the data honestly
40
+
41
+ - \`0\` means genuinely none — a new store has 0 scans. Never infer a malfunction from an empty result; if a tool fails, it says so.
42
+ - Some numbers are exact, some are a sample. \`breakdownsTruncated: true\` means the device, country and browser breakdowns cover only the most recent events. \`overviewAccuracy\` (and \`accuracy\` on a single link) says per number whether it is \`exact\` or \`sample\` — pass that on instead of presenting a sample as a total.
43
+ - API days are UTC calendar days (\`period.timezone\` says so). The analytics page in the echomate admin counts in the store's time zone, so an order near midnight can land on a different day or outside the window. If the merchant compares totals with the page, explain that before suspecting an error.
44
+ - **Other** and **Unknown** are collective categories, not browsers or devices: "Other" means detection found nothing it knows, "Unknown" means the value was missing.
45
+ - Breakdowns count visits (scans and clicks). Orders carry no device, browser, country or referrer and are not part of them.
46
+ - Revenue is the Shopify order total, including shipping and taxes; refunds are not deducted. If the merchant compares it with a net-sales figure, explain that difference first.
47
+ - Never invent field values. If something is not in a response, it is not available.
48
+
49
+ ## Showing things to the merchant
50
+
51
+ When the merchant wants to **see** a QR code, call \`show_qr_code\` — in chats that support it, it appears as a card with the code image, total scans and a button into the echomate admin. For "which codes do best", call \`show_top_qr_codes\`. The list and analytics tools are for looking things up; use them to find the ID first, not to show. \`update_qr_code\` shows a receipt card of what changed. Where no card appears, the same tools still answer in text — describe the result as usual.
52
+
53
+ ## What you cannot see or do — that is deliberate
54
+
55
+ - Customer personal data (names, addresses, order contents) is not exposed.
56
+ - You never see API keys and cannot create or revoke them; the merchant does that in the echomate settings.
57
+ - Links that lead to a customer's order page never appear on the public Spotlight page, even if selected.`;
58
+ //# sourceMappingURL=instructions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instructions.js","sourceRoot":"","sources":["../src/instructions.ts"],"names":[],"mappings":"AAgCA,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0GAwD0E,CAAC"}