@framedash/mcp-server 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Crane Valley LLC
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 ADDED
@@ -0,0 +1,98 @@
1
+ # @framedash/mcp-server
2
+
3
+ MCP (Model Context Protocol) server for the Framedash game telemetry platform. Provides 12 read-only tools and 4 resources for querying analytics data from LLM-powered tools.
4
+
5
+ ## Setup
6
+
7
+ ### Claude Desktop
8
+
9
+ Add to your `claude_desktop_config.json`:
10
+
11
+ ```json
12
+ {
13
+ "mcpServers": {
14
+ "framedash": {
15
+ "command": "npx",
16
+ "args": ["@framedash/mcp-server"],
17
+ "env": {
18
+ "FRAMEDASH_API_KEY": "fd_admin_xxx",
19
+ "FRAMEDASH_PROJECT_ID": "your-project-uuid"
20
+ }
21
+ }
22
+ }
23
+ }
24
+ ```
25
+
26
+ ### VS Code (Claude Extension)
27
+
28
+ Add to your VS Code settings:
29
+
30
+ ```json
31
+ {
32
+ "claude.mcpServers": {
33
+ "framedash": {
34
+ "command": "npx",
35
+ "args": ["@framedash/mcp-server"],
36
+ "env": {
37
+ "FRAMEDASH_API_KEY": "fd_admin_xxx",
38
+ "FRAMEDASH_PROJECT_ID": "your-project-uuid"
39
+ }
40
+ }
41
+ }
42
+ }
43
+ ```
44
+
45
+ ## Environment Variables
46
+
47
+ | Variable | Required | Description |
48
+ |----------|----------|-------------|
49
+ | `FRAMEDASH_API_KEY` | Yes | Admin API key (`fd_admin_` prefix) |
50
+ | `FRAMEDASH_PROJECT_ID` | No | Default project UUID for project-scoped tools |
51
+ | `FRAMEDASH_BASE_URL` | No | API base URL (default: `https://app.framedash.dev`) |
52
+
53
+ If `FRAMEDASH_PROJECT_ID` is omitted, project-scoped tools still work but
54
+ require an explicit `project_id` argument per call.
55
+
56
+ ## Tools
57
+
58
+ | Tool | Description |
59
+ |------|-------------|
60
+ | `query` | Execute SQL analytics query (SELECT only, limit default 100) |
61
+ | `get_dashboard` | Project KPI summary (DAU, MAU, sessions, events) |
62
+ | `get_retention` | Cohort retention data |
63
+ | `get_funnel` | Funnel analysis for event steps |
64
+ | `get_insights` | Event insights with grouping |
65
+ | `get_heatmap` | Heatmap grid data for a map |
66
+ | `list_projects` | Show the project bound to the API key |
67
+ | `get_project_status` | Project health overview |
68
+ | `list_maps` | List maps in a project |
69
+ | `list_content` | List content registry entries |
70
+ | `list_alerts` | List alert rules |
71
+ | `get_alert_history` | Alert trigger history |
72
+
73
+ ## Resources
74
+
75
+ | URI | Description |
76
+ |-----|-------------|
77
+ | `framedash://projects` | Project bound to the API key |
78
+ | `framedash://projects/{projectId}/maps` | Map list with bounds |
79
+ | `framedash://projects/{projectId}/content` | Content registry |
80
+ | `framedash://projects/{projectId}/status` | Project health status |
81
+
82
+ ## Development
83
+
84
+ Run from the repository root:
85
+
86
+ ```bash
87
+ pnpm install
88
+ pnpm --filter @framedash/mcp-server... build
89
+ pnpm --filter @framedash/mcp-server test
90
+ ```
91
+
92
+ With pnpm 9, the trailing `...` selects the MCP package plus its workspace
93
+ dependencies, including `@framedash/api-client`, before compiling from a clean
94
+ checkout.
95
+
96
+ ## License
97
+
98
+ MIT
@@ -0,0 +1,21 @@
1
+ import type { ApiClient } from "@framedash/api-client";
2
+ /** Truncate large responses to protect LLM context windows. */
3
+ export declare function truncate(data: unknown): string;
4
+ export declare function textResult(data: unknown): {
5
+ content: {
6
+ type: "text";
7
+ text: string;
8
+ }[];
9
+ };
10
+ /** Extract a human-readable message from an unknown error value. */
11
+ export declare function getErrorMessage(err: unknown): string;
12
+ export declare function errorResult(err: unknown): {
13
+ content: {
14
+ type: "text";
15
+ text: string;
16
+ }[];
17
+ isError: true;
18
+ };
19
+ /** Create an ApiClient with an optional project_id override. */
20
+ export declare function projectClient(base: ApiClient, overrideProjectId?: string): ApiClient;
21
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAIvD,+DAA+D;AAC/D,wBAAgB,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAe9C;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO;;;;;EAEvC;AAED,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAEpD;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO;;;;;;EAKvC;AAID,gEAAgE;AAChE,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAMpF"}
@@ -0,0 +1,42 @@
1
+ const MAX_RESPONSE_SIZE = 50 * 1024; // 50 KB
2
+ /** Truncate large responses to protect LLM context windows. */
3
+ export function truncate(data) {
4
+ const text = JSON.stringify(data, null, 2) ?? "null";
5
+ if (text.length <= MAX_RESPONSE_SIZE)
6
+ return text;
7
+ if (Array.isArray(data)) {
8
+ const total = data.length;
9
+ for (let keep = Math.min(total, 50); keep > 0; keep--) {
10
+ const slice = JSON.stringify(data.slice(0, keep), null, 2);
11
+ const footer = `\n\n[truncated: showing first ${keep} rows of ${total} total]`;
12
+ if (slice.length + footer.length <= MAX_RESPONSE_SIZE) {
13
+ return slice + footer;
14
+ }
15
+ }
16
+ }
17
+ return `${text.slice(0, MAX_RESPONSE_SIZE)}\n\n[truncated]`;
18
+ }
19
+ export function textResult(data) {
20
+ return { content: [{ type: "text", text: truncate(data) }] };
21
+ }
22
+ /** Extract a human-readable message from an unknown error value. */
23
+ export function getErrorMessage(err) {
24
+ return err instanceof Error ? err.message : String(err);
25
+ }
26
+ export function errorResult(err) {
27
+ return {
28
+ content: [{ type: "text", text: getErrorMessage(err) }],
29
+ isError: true,
30
+ };
31
+ }
32
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
33
+ /** Create an ApiClient with an optional project_id override. */
34
+ export function projectClient(base, overrideProjectId) {
35
+ if (!overrideProjectId)
36
+ return base;
37
+ if (!UUID_RE.test(overrideProjectId)) {
38
+ throw new Error("Invalid project_id: must be a valid UUID");
39
+ }
40
+ return base.withProject(overrideProjectId);
41
+ }
42
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAEA,MAAM,iBAAiB,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,QAAQ;AAE7C,+DAA+D;AAC/D,MAAM,UAAU,QAAQ,CAAC,IAAa;IACrC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC;IACrD,IAAI,IAAI,CAAC,MAAM,IAAI,iBAAiB;QAAE,OAAO,IAAI,CAAC;IAElD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,KAAK,IAAI,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC;YACvD,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAC3D,MAAM,MAAM,GAAG,iCAAiC,IAAI,YAAY,KAAK,SAAS,CAAC;YAC/E,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,iBAAiB,EAAE,CAAC;gBACvD,OAAO,KAAK,GAAG,MAAM,CAAC;YACvB,CAAC;QACF,CAAC;IACF,CAAC;IACD,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,iBAAiB,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAa;IACvC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;AACvE,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,eAAe,CAAC,GAAY;IAC3C,OAAO,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAY;IACvC,OAAO;QACN,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC;QAChE,OAAO,EAAE,IAAa;KACtB,CAAC;AACH,CAAC;AAED,MAAM,OAAO,GAAG,iEAAiE,CAAC;AAElF,gEAAgE;AAChE,MAAM,UAAU,aAAa,CAAC,IAAe,EAAE,iBAA0B;IACxE,IAAI,CAAC,iBAAiB;QAAE,OAAO,IAAI,CAAC;IACpC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,IAAI,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+ import { ApiClient } from "@framedash/api-client";
3
+ import { createServer } from "./server.js";
4
+ import { StrictStdioServerTransport } from "./transport.js";
5
+ const apiKey = process.env.FRAMEDASH_API_KEY;
6
+ if (!apiKey) {
7
+ process.stderr.write("Error: FRAMEDASH_API_KEY environment variable is required\n");
8
+ process.exit(1);
9
+ }
10
+ const projectId = process.env.FRAMEDASH_PROJECT_ID ?? "";
11
+ if (!projectId) {
12
+ process.stderr.write("Warning: FRAMEDASH_PROJECT_ID is not set. Project-scoped tools will require a project_id argument.\n");
13
+ }
14
+ const baseUrl = process.env.FRAMEDASH_BASE_URL || "https://app.framedash.dev";
15
+ try {
16
+ const client = new ApiClient({
17
+ baseUrl,
18
+ apiKey,
19
+ projectId,
20
+ onError(err) {
21
+ throw err;
22
+ },
23
+ });
24
+ const server = createServer(client);
25
+ const transport = new StrictStdioServerTransport();
26
+ await server.connect(transport);
27
+ }
28
+ catch (err) {
29
+ process.stderr.write(`Failed to start MCP server: ${err}\n`);
30
+ process.exit(1);
31
+ }
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAE5D,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;AAC7C,IAAI,CAAC,MAAM,EAAE,CAAC;IACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,6DAA6D,CAAC,CAAC;IACpF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC;AAED,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,EAAE,CAAC;AACzD,IAAI,CAAC,SAAS,EAAE,CAAC;IAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CACnB,sGAAsG,CACtG,CAAC;AACH,CAAC;AACD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,2BAA2B,CAAC;AAE9E,IAAI,CAAC;IACJ,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC5B,OAAO;QACP,MAAM;QACN,SAAS;QACT,OAAO,CAAC,GAAG;YACV,MAAM,GAAG,CAAC;QACX,CAAC;KACD,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,IAAI,0BAA0B,EAAE,CAAC;IACnD,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AACjC,CAAC;AAAC,OAAO,GAAG,EAAE,CAAC;IACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,+BAA+B,GAAG,IAAI,CAAC,CAAC;IAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ApiClient } from "@framedash/api-client";
2
+ import { type McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ export declare function registerResources(server: McpServer, apiClient: ApiClient): void;
4
+ //# sourceMappingURL=resources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,KAAK,SAAS,EAAoB,MAAM,yCAAyC,CAAC;AAG3F,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI,CA+E/E"}
@@ -0,0 +1,64 @@
1
+ import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { getErrorMessage, projectClient, truncate } from "./helpers.js";
3
+ export function registerResources(server, apiClient) {
4
+ server.registerResource("projects", "framedash://projects", {
5
+ title: "Projects",
6
+ description: "Project bound to the current API key",
7
+ mimeType: "application/json",
8
+ }, async () => {
9
+ try {
10
+ const data = await apiClient.get("/api/v1/projects");
11
+ return { contents: [{ uri: "framedash://projects", text: truncate(data) }] };
12
+ }
13
+ catch (err) {
14
+ const msg = getErrorMessage(err);
15
+ return { contents: [{ uri: "framedash://projects", text: msg }] };
16
+ }
17
+ });
18
+ server.registerResource("project-maps", new ResourceTemplate("framedash://projects/{projectId}/maps", { list: undefined }), {
19
+ title: "Project Maps",
20
+ description: "Maps in a project with coordinates",
21
+ mimeType: "application/json",
22
+ }, async (uri, params) => {
23
+ try {
24
+ const client = projectClient(apiClient, String(params.projectId));
25
+ const data = await client.get(client.projectPath("maps"));
26
+ return { contents: [{ uri: uri.href, text: truncate(data) }] };
27
+ }
28
+ catch (err) {
29
+ const msg = getErrorMessage(err);
30
+ return { contents: [{ uri: uri.href, text: msg }] };
31
+ }
32
+ });
33
+ server.registerResource("project-content", new ResourceTemplate("framedash://projects/{projectId}/content", { list: undefined }), {
34
+ title: "Content Registry",
35
+ description: "Content entries (event names, display labels)",
36
+ mimeType: "application/json",
37
+ }, async (uri, params) => {
38
+ try {
39
+ const client = projectClient(apiClient, String(params.projectId));
40
+ const data = await client.get(client.projectPath("content"));
41
+ return { contents: [{ uri: uri.href, text: truncate(data) }] };
42
+ }
43
+ catch (err) {
44
+ const msg = getErrorMessage(err);
45
+ return { contents: [{ uri: uri.href, text: msg }] };
46
+ }
47
+ });
48
+ server.registerResource("project-status", new ResourceTemplate("framedash://projects/{projectId}/status", { list: undefined }), {
49
+ title: "Project Status",
50
+ description: "Project status and event statistics",
51
+ mimeType: "application/json",
52
+ }, async (uri, params) => {
53
+ try {
54
+ const client = projectClient(apiClient, String(params.projectId));
55
+ const data = await client.get(client.projectPath("status"));
56
+ return { contents: [{ uri: uri.href, text: truncate(data) }] };
57
+ }
58
+ catch (err) {
59
+ const msg = getErrorMessage(err);
60
+ return { contents: [{ uri: uri.href, text: msg }] };
61
+ }
62
+ });
63
+ }
64
+ //# sourceMappingURL=resources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { ApiClient } from "@framedash/api-client";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ export declare function createServer(apiClient: ApiClient): McpServer;
4
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOpE,wBAAgB,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,CAa5D"}
package/dist/server.js ADDED
@@ -0,0 +1,19 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { registerResources } from "./resources.js";
3
+ import { registerAlertTools } from "./tools/alerts.js";
4
+ import { registerAnalyticsTools } from "./tools/analytics.js";
5
+ import { registerProjectTools } from "./tools/project.js";
6
+ import { registerQueryTools } from "./tools/query.js";
7
+ export function createServer(apiClient) {
8
+ const server = new McpServer({
9
+ name: "framedash",
10
+ version: "0.1.0",
11
+ });
12
+ registerQueryTools(server, apiClient);
13
+ registerAnalyticsTools(server, apiClient);
14
+ registerProjectTools(server, apiClient);
15
+ registerAlertTools(server, apiClient);
16
+ registerResources(server, apiClient);
17
+ return server;
18
+ }
19
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,UAAU,YAAY,CAAC,SAAoB;IAChD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC5B,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,OAAO;KAChB,CAAC,CAAC;IAEH,kBAAkB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACtC,sBAAsB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAC1C,oBAAoB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACxC,kBAAkB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACtC,iBAAiB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAErC,OAAO,MAAM,CAAC;AACf,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ApiClient } from "@framedash/api-client";
2
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ export declare function registerAlertTools(server: McpServer, apiClient: ApiClient): void;
4
+ //# sourceMappingURL=alerts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alerts.d.ts","sourceRoot":"","sources":["../../src/tools/alerts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIzE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI,CAkDhF"}
@@ -0,0 +1,48 @@
1
+ import { z } from "zod";
2
+ import { errorResult, projectClient, textResult } from "../helpers.js";
3
+ export function registerAlertTools(server, apiClient) {
4
+ server.registerTool("list_alerts", {
5
+ title: "List Alerts",
6
+ description: "List all alert rules in the project.",
7
+ inputSchema: {
8
+ project_id: z.string().uuid().optional().describe("Override the default project ID"),
9
+ },
10
+ }, async (args) => {
11
+ try {
12
+ const client = projectClient(apiClient, args.project_id);
13
+ const data = await client.get(client.projectPath("alerts"));
14
+ return textResult(data);
15
+ }
16
+ catch (err) {
17
+ return errorResult(err);
18
+ }
19
+ });
20
+ server.registerTool("get_alert_history", {
21
+ title: "Get Alert History",
22
+ description: "Get alert event history (trigger/resolve events).",
23
+ inputSchema: {
24
+ project_id: z.string().uuid().optional().describe("Override the default project ID"),
25
+ limit: z
26
+ .number()
27
+ .int()
28
+ .min(1)
29
+ .max(100)
30
+ .optional()
31
+ .describe("Max events to return (default 50)"),
32
+ },
33
+ }, async (args) => {
34
+ try {
35
+ const client = projectClient(apiClient, args.project_id);
36
+ const params = new URLSearchParams();
37
+ if (args.limit)
38
+ params.set("limit", String(args.limit));
39
+ const qs = params.toString() ? `?${params}` : "";
40
+ const data = await client.get(client.projectPath(`alerts/history${qs}`));
41
+ return textResult(data);
42
+ }
43
+ catch (err) {
44
+ return errorResult(err);
45
+ }
46
+ });
47
+ }
48
+ //# sourceMappingURL=alerts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alerts.js","sourceRoot":"","sources":["../../src/tools/alerts.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEvE,MAAM,UAAU,kBAAkB,CAAC,MAAiB,EAAE,SAAoB;IACzE,MAAM,CAAC,YAAY,CAClB,aAAa,EACb;QACC,KAAK,EAAE,aAAa;QACpB,WAAW,EAAE,sCAAsC;QACnD,WAAW,EAAE;YACZ,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;SACpF;KACD,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACd,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;YACzD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC5D,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;IACF,CAAC,CACD,CAAC;IAEF,MAAM,CAAC,YAAY,CAClB,mBAAmB,EACnB;QACC,KAAK,EAAE,mBAAmB;QAC1B,WAAW,EAAE,mDAAmD;QAChE,WAAW,EAAE;YACZ,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;YACpF,KAAK,EAAE,CAAC;iBACN,MAAM,EAAE;iBACR,GAAG,EAAE;iBACL,GAAG,CAAC,CAAC,CAAC;iBACN,GAAG,CAAC,GAAG,CAAC;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,mCAAmC,CAAC;SAC/C;KACD,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACd,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;YACzD,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;YACrC,IAAI,IAAI,CAAC,KAAK;gBAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YACxD,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACjD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC,CAAC;YACzE,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;IACF,CAAC,CACD,CAAC;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ApiClient } from "@framedash/api-client";
2
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ export declare function registerAnalyticsTools(server: McpServer, apiClient: ApiClient): void;
4
+ //# sourceMappingURL=analytics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/tools/analytics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA+BzE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI,CAsIpF"}
@@ -0,0 +1,149 @@
1
+ import { z } from "zod";
2
+ import { errorResult, projectClient, textResult } from "../helpers.js";
3
+ // The REST API rejects values outside these sets with HTTP 400. They mirror the
4
+ // web app's constants (ALLOWED_DAYS / ALLOWED_LIMITS in lib/analytics/constants,
5
+ // VALID_DAYS / VALID_CELL_SIZES in lib/heatmap-constants). The MCP server is a
6
+ // separate package and cannot import them, so keep these in sync if the API changes.
7
+ // Constraining the input schema lets the MCP client reject invalid values up front
8
+ // instead of after a server round-trip.
9
+ const ANALYTICS_DAYS = [7, 14, 30, 90];
10
+ const INSIGHTS_LIMITS = [10, 20, 50];
11
+ const HEATMAP_DAYS = [1, 7, 14, 30];
12
+ const HEATMAP_CELL_SIZES = [5, 10, 25, 50];
13
+ /**
14
+ * Optional value constrained to an allowed set, mirroring the REST API. Built from
15
+ * z.union(z.literal(...)) rather than z.number().refine(...) so the allowed values
16
+ * serialize into the JSON Schema the MCP SDK publishes -- `refine` runs opaque JS
17
+ * and is dropped by the schema converter, which would leave the LLM unable to see
18
+ * the constraint and reject out-of-set values before a server round-trip.
19
+ */
20
+ function allowedIntEnum(values, description) {
21
+ const literals = values.map((v) => z.literal(v));
22
+ return z.union(literals).optional().describe(description);
23
+ }
24
+ export function registerAnalyticsTools(server, apiClient) {
25
+ server.registerTool("get_dashboard", {
26
+ title: "Get Dashboard",
27
+ description: "Get project dashboard metrics (KPIs, daily active users, top events).",
28
+ inputSchema: {
29
+ project_id: z.string().uuid().optional().describe("Override the default project ID"),
30
+ days: allowedIntEnum(ANALYTICS_DAYS, "Time period: 7, 14, 30, or 90 days (default 30)"),
31
+ },
32
+ }, async (args) => {
33
+ try {
34
+ const client = projectClient(apiClient, args.project_id);
35
+ const params = new URLSearchParams();
36
+ if (args.days)
37
+ params.set("days", String(args.days));
38
+ const qs = params.toString() ? `?${params}` : "";
39
+ const data = await client.get(client.projectPath(`dashboard${qs}`));
40
+ return textResult(data);
41
+ }
42
+ catch (err) {
43
+ return errorResult(err);
44
+ }
45
+ });
46
+ server.registerTool("get_retention", {
47
+ title: "Get Retention",
48
+ description: "Get player retention cohort analysis.",
49
+ inputSchema: {
50
+ project_id: z.string().uuid().optional().describe("Override the default project ID"),
51
+ days: allowedIntEnum(ANALYTICS_DAYS, "Time period: 7, 14, 30, or 90 days (default 30)"),
52
+ },
53
+ }, async (args) => {
54
+ try {
55
+ const client = projectClient(apiClient, args.project_id);
56
+ const params = new URLSearchParams();
57
+ if (args.days)
58
+ params.set("days", String(args.days));
59
+ const qs = params.toString() ? `?${params}` : "";
60
+ const data = await client.get(client.projectPath(`retention${qs}`));
61
+ return textResult(data);
62
+ }
63
+ catch (err) {
64
+ return errorResult(err);
65
+ }
66
+ });
67
+ server.registerTool("get_funnel", {
68
+ title: "Get Funnel",
69
+ description: "Analyze event funnels. Requires 2-8 event names as steps.",
70
+ inputSchema: {
71
+ project_id: z.string().uuid().optional().describe("Override the default project ID"),
72
+ steps: z.string().describe("Comma-separated event names (2-8 steps)"),
73
+ days: allowedIntEnum(ANALYTICS_DAYS, "Time period: 7, 14, 30, or 90 days (default 30)"),
74
+ },
75
+ }, async (args) => {
76
+ try {
77
+ const client = projectClient(apiClient, args.project_id);
78
+ const params = new URLSearchParams({ steps: args.steps });
79
+ if (args.days)
80
+ params.set("days", String(args.days));
81
+ const data = await client.get(client.projectPath(`funnels?${params}`));
82
+ return textResult(data);
83
+ }
84
+ catch (err) {
85
+ return errorResult(err);
86
+ }
87
+ });
88
+ server.registerTool("get_insights", {
89
+ title: "Get Insights",
90
+ description: "Get aggregated insights for a metric grouped by a dimension.",
91
+ inputSchema: {
92
+ project_id: z.string().uuid().optional().describe("Override the default project ID"),
93
+ metric: z.string().describe("Metric to aggregate: count or unique_players"),
94
+ group_by: z.string().describe("Dimension to group by (e.g. event_name, platform)"),
95
+ days: allowedIntEnum(ANALYTICS_DAYS, "Time period: 7, 14, 30, or 90 days (default 30)"),
96
+ limit: allowedIntEnum(INSIGHTS_LIMITS, "Max groups to return: 10, 20, or 50"),
97
+ event_name: z.string().optional().describe("Filter by event name"),
98
+ },
99
+ }, async (args) => {
100
+ try {
101
+ const client = projectClient(apiClient, args.project_id);
102
+ const params = new URLSearchParams({
103
+ metric: args.metric,
104
+ groupBy: args.group_by,
105
+ });
106
+ if (args.days)
107
+ params.set("days", String(args.days));
108
+ if (args.limit)
109
+ params.set("limit", String(args.limit));
110
+ if (args.event_name)
111
+ params.set("eventName", args.event_name);
112
+ const data = await client.get(client.projectPath(`insights?${params}`));
113
+ return textResult(data);
114
+ }
115
+ catch (err) {
116
+ return errorResult(err);
117
+ }
118
+ });
119
+ server.registerTool("get_heatmap", {
120
+ title: "Get Heatmap",
121
+ description: "Get heatmap grid data for a map. Returns performance metrics per cell.",
122
+ inputSchema: {
123
+ project_id: z.string().uuid().optional().describe("Override the default project ID"),
124
+ map_id: z
125
+ .string()
126
+ .describe("Map ID to query (accepts the row UUID or slug from list_maps)"),
127
+ cell_size: allowedIntEnum(HEATMAP_CELL_SIZES, "Cell size: 5, 10, 25, or 50 (default 25)"),
128
+ days: allowedIntEnum(HEATMAP_DAYS, "Time period: 1, 7, 14, or 30 days (default 7)"),
129
+ event_name: z.string().optional().describe("Filter by event name"),
130
+ },
131
+ }, async (args) => {
132
+ try {
133
+ const client = projectClient(apiClient, args.project_id);
134
+ const params = new URLSearchParams({ mapId: args.map_id });
135
+ if (args.cell_size)
136
+ params.set("cellSize", String(args.cell_size));
137
+ if (args.days)
138
+ params.set("days", String(args.days));
139
+ if (args.event_name)
140
+ params.set("eventName", args.event_name);
141
+ const data = await client.get(client.projectPath(`heatmap?${params}`));
142
+ return textResult(data);
143
+ }
144
+ catch (err) {
145
+ return errorResult(err);
146
+ }
147
+ });
148
+ }
149
+ //# sourceMappingURL=analytics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/tools/analytics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { ApiClient } from "@framedash/api-client";
2
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ export declare function registerProjectTools(server: McpServer, apiClient: ApiClient): void;
4
+ //# sourceMappingURL=project.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/tools/project.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIzE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI,CAgFlF"}
@@ -0,0 +1,70 @@
1
+ import { z } from "zod";
2
+ import { errorResult, projectClient, textResult } from "../helpers.js";
3
+ export function registerProjectTools(server, apiClient) {
4
+ server.registerTool("list_projects", {
5
+ title: "List Projects",
6
+ description: "Show the project bound to the current API key.",
7
+ }, async () => {
8
+ try {
9
+ const data = await apiClient.get("/api/v1/projects");
10
+ return textResult(data);
11
+ }
12
+ catch (err) {
13
+ return errorResult(err);
14
+ }
15
+ });
16
+ server.registerTool("get_project_status", {
17
+ title: "Get Project Status",
18
+ description: "Get the status of a project (event counts, last event time, etc.).",
19
+ inputSchema: {
20
+ project_id: z.string().uuid().optional().describe("Override the default project ID"),
21
+ },
22
+ }, async (args) => {
23
+ try {
24
+ const client = projectClient(apiClient, args.project_id);
25
+ const data = await client.get(client.projectPath("status"));
26
+ return textResult(data);
27
+ }
28
+ catch (err) {
29
+ return errorResult(err);
30
+ }
31
+ });
32
+ server.registerTool("list_maps", {
33
+ title: "List Maps",
34
+ description: "List all maps in the project.",
35
+ inputSchema: {
36
+ project_id: z.string().uuid().optional().describe("Override the default project ID"),
37
+ },
38
+ }, async (args) => {
39
+ try {
40
+ const client = projectClient(apiClient, args.project_id);
41
+ const data = await client.get(client.projectPath("maps"));
42
+ return textResult(data);
43
+ }
44
+ catch (err) {
45
+ return errorResult(err);
46
+ }
47
+ });
48
+ server.registerTool("list_content", {
49
+ title: "List Content",
50
+ description: "List content registry entries (event names, display labels, etc.).",
51
+ inputSchema: {
52
+ project_id: z.string().uuid().optional().describe("Override the default project ID"),
53
+ type: z.string().optional().describe("Filter by content type"),
54
+ },
55
+ }, async (args) => {
56
+ try {
57
+ const client = projectClient(apiClient, args.project_id);
58
+ const params = new URLSearchParams();
59
+ if (args.type)
60
+ params.set("type", args.type);
61
+ const qs = params.toString() ? `?${params}` : "";
62
+ const data = await client.get(`/api/v1/content${qs}`);
63
+ return textResult(data);
64
+ }
65
+ catch (err) {
66
+ return errorResult(err);
67
+ }
68
+ });
69
+ }
70
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/tools/project.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { ApiClient } from "@framedash/api-client";
2
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ export declare function registerQueryTools(server: McpServer, apiClient: ApiClient): void;
4
+ //# sourceMappingURL=query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/tools/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIzE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI,CAiChF"}
@@ -0,0 +1,33 @@
1
+ import { z } from "zod";
2
+ import { errorResult, projectClient, textResult } from "../helpers.js";
3
+ export function registerQueryTools(server, apiClient) {
4
+ server.registerTool("query", {
5
+ title: "Query Events",
6
+ description: "Execute a read-only SQL query against the ClickHouse events table. Returns up to `limit` rows (default 100, max 1000).",
7
+ inputSchema: {
8
+ sql: z.string().describe("SQL query to execute"),
9
+ project_id: z.string().uuid().optional().describe("Override the default project ID"),
10
+ limit: z
11
+ .number()
12
+ .int()
13
+ .min(1)
14
+ .max(1000)
15
+ .optional()
16
+ .describe("Max rows to return (default 100)"),
17
+ },
18
+ }, async (args) => {
19
+ try {
20
+ const client = projectClient(apiClient, args.project_id);
21
+ const data = await client.post("/api/v1/query", {
22
+ project_id: client.currentProjectId,
23
+ sql: args.sql,
24
+ limit: args.limit ?? 100,
25
+ });
26
+ return textResult(data);
27
+ }
28
+ catch (err) {
29
+ return errorResult(err);
30
+ }
31
+ });
32
+ }
33
+ //# sourceMappingURL=query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/tools/query.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEvE,MAAM,UAAU,kBAAkB,CAAC,MAAiB,EAAE,SAAoB;IACzE,MAAM,CAAC,YAAY,CAClB,OAAO,EACP;QACC,KAAK,EAAE,cAAc;QACrB,WAAW,EACV,wHAAwH;QACzH,WAAW,EAAE;YACZ,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sBAAsB,CAAC;YAChD,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;YACpF,KAAK,EAAE,CAAC;iBACN,MAAM,EAAE;iBACR,GAAG,EAAE;iBACL,GAAG,CAAC,CAAC,CAAC;iBACN,GAAG,CAAC,IAAI,CAAC;iBACT,QAAQ,EAAE;iBACV,QAAQ,CAAC,kCAAkC,CAAC;SAC9C;KACD,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACd,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;YACzD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE;gBAC/C,UAAU,EAAE,MAAM,CAAC,gBAAgB;gBACnC,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,GAAG;aACxB,CAAC,CAAC;YACH,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;IACF,CAAC,CACD,CAAC;AACH,CAAC"}
@@ -0,0 +1,43 @@
1
+ import { Buffer } from "node:buffer";
2
+ import type { Readable, Writable } from "node:stream";
3
+ import type { Transport, TransportSendOptions } from "@modelcontextprotocol/sdk/shared/transport.js";
4
+ import type { JSONRPCMessage, MessageExtraInfo } from "@modelcontextprotocol/sdk/types.js";
5
+ /**
6
+ * Stdio transport for an MCP server that responds with a JSON-RPC `Parse error`
7
+ * envelope (-32700, id null) on stdout when stdin contains a non-JSON line.
8
+ *
9
+ * The upstream `StdioServerTransport` forwards parse failures to an optional
10
+ * `onerror` hook but never replies on the wire, so a client sending a malformed
11
+ * frame never learns it was dropped. JSON-RPC 2.0 requires an error response
12
+ * with id=null for unparseable input.
13
+ */
14
+ export declare class StrictStdioServerTransport implements Transport {
15
+ private readonly _stdin;
16
+ private readonly _stdout;
17
+ private _chunks;
18
+ private _chunksLen;
19
+ private _discardingFrame;
20
+ private _stdoutSaturated;
21
+ private _drainAttached;
22
+ private _writeChain;
23
+ private _started;
24
+ private _closed;
25
+ onclose?: () => void;
26
+ onerror?: (error: Error) => void;
27
+ onmessage?: <T extends JSONRPCMessage>(message: T, extra?: MessageExtraInfo) => void;
28
+ constructor(stdin?: Readable, stdout?: Writable);
29
+ start(): Promise<void>;
30
+ send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void>;
31
+ close(): Promise<void>;
32
+ private _ondata;
33
+ private _onstdinError;
34
+ private _onstdinEnd;
35
+ private _resetChunks;
36
+ private _handleLine;
37
+ private _emitParseError;
38
+ private _markSaturated;
39
+ private _writeStdout;
40
+ /** Test helper: feed bytes through the same path as live stdin data. */
41
+ feedForTest(chunk: Buffer | string): void;
42
+ }
43
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EACX,SAAS,EACT,oBAAoB,EACpB,MAAM,+CAA+C,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAK3F;;;;;;;;GAQG;AACH,qBAAa,0BAA2B,YAAW,SAAS;IAC3D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAW;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAW;IAInC,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,UAAU,CAAK;IAIvB,OAAO,CAAC,gBAAgB,CAAS;IAKjC,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,cAAc,CAAS;IAI/B,OAAO,CAAC,WAAW,CAAoC;IACvD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IAExB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,CAAC,SAAS,cAAc,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,gBAAgB,KAAK,IAAI,CAAC;gBAEzE,KAAK,GAAE,QAAwB,EAAE,MAAM,GAAE,QAAyB;IAKxE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAYtB,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAY7E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAY5B,OAAO,CAAC,OAAO,CAmEb;IAEF,OAAO,CAAC,aAAa,CAEnB;IAEF,OAAO,CAAC,WAAW,CAEjB;IAEF,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,eAAe;IAuBvB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,YAAY;IAsCpB,wEAAwE;IACxE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;CAIzC"}
@@ -0,0 +1,237 @@
1
+ import { Buffer } from "node:buffer";
2
+ import process from "node:process";
3
+ /** Hard cap on a single JSON-RPC frame: 4 MB. Lines larger than this are rejected. */
4
+ const MAX_LINE_BYTES = 4 * 1024 * 1024;
5
+ /**
6
+ * Stdio transport for an MCP server that responds with a JSON-RPC `Parse error`
7
+ * envelope (-32700, id null) on stdout when stdin contains a non-JSON line.
8
+ *
9
+ * The upstream `StdioServerTransport` forwards parse failures to an optional
10
+ * `onerror` hook but never replies on the wire, so a client sending a malformed
11
+ * frame never learns it was dropped. JSON-RPC 2.0 requires an error response
12
+ * with id=null for unparseable input.
13
+ */
14
+ export class StrictStdioServerTransport {
15
+ _stdin;
16
+ _stdout;
17
+ // Pending bytes for the in-flight (unterminated) line. We accumulate them in an
18
+ // array and only call Buffer.concat once a newline arrives, so a peer that
19
+ // streams a big payload in many small chunks pays linear cost instead of O(N^2).
20
+ _chunks = [];
21
+ _chunksLen = 0;
22
+ // When an oversized frame is detected, we throw away the rest of that frame
23
+ // (everything up to and including the next newline) so the trailing bytes of
24
+ // a too-large line cannot be parsed as a fresh JSON-RPC message.
25
+ _discardingFrame = false;
26
+ // True while stdout has signaled backpressure (write returned false). We
27
+ // drop best-effort parse-error envelopes while saturated so a peer that
28
+ // floods malformed input without reading stdout cannot pin unbounded reply
29
+ // bytes in Node's internal buffer.
30
+ _stdoutSaturated = false;
31
+ _drainAttached = false;
32
+ // Serialize send() writes through a single promise chain so concurrent
33
+ // callers do not stack drain/close/error listeners on stdout (which would
34
+ // trip Node's MaxListenersExceededWarning at >10 in flight).
35
+ _writeChain = Promise.resolve();
36
+ _started = false;
37
+ _closed = false;
38
+ onclose;
39
+ onerror;
40
+ onmessage;
41
+ constructor(stdin = process.stdin, stdout = process.stdout) {
42
+ this._stdin = stdin;
43
+ this._stdout = stdout;
44
+ }
45
+ async start() {
46
+ if (this._started) {
47
+ throw new Error("StrictStdioServerTransport already started");
48
+ }
49
+ this._started = true;
50
+ this._stdin.on("data", this._ondata);
51
+ this._stdin.on("error", this._onstdinError);
52
+ // stdin EOF (peer disconnect) must propagate to onclose so the MCP server
53
+ // shuts down instead of lingering as a zombie process.
54
+ this._stdin.on("end", this._onstdinEnd);
55
+ }
56
+ async send(message, _options) {
57
+ const json = `${JSON.stringify(message)}\n`;
58
+ const next = this._writeChain.then(() => this._writeStdout(json), () => this._writeStdout(json));
59
+ // Swallow the rejection on the chain so a single failed write does not
60
+ // poison every subsequent send. Callers still see the original rejection.
61
+ this._writeChain = next.catch(() => undefined);
62
+ return next;
63
+ }
64
+ async close() {
65
+ if (this._closed)
66
+ return;
67
+ this._closed = true;
68
+ this._stdin.off("data", this._ondata);
69
+ this._stdin.off("error", this._onstdinError);
70
+ this._stdin.off("end", this._onstdinEnd);
71
+ this._chunks = [];
72
+ this._chunksLen = 0;
73
+ this._discardingFrame = false;
74
+ this.onclose?.();
75
+ }
76
+ _ondata = (chunk) => {
77
+ // If we are discarding the tail of an oversized frame, skip ahead to the
78
+ // next newline before resuming normal framing.
79
+ if (this._discardingFrame) {
80
+ const nl = chunk.indexOf(0x0a);
81
+ if (nl === -1)
82
+ return;
83
+ this._discardingFrame = false;
84
+ chunk = chunk.subarray(nl + 1);
85
+ if (chunk.length === 0)
86
+ return;
87
+ }
88
+ const projected = this._chunksLen + chunk.length;
89
+ const nlInChunk = chunk.indexOf(0x0a);
90
+ // No frame boundary in this chunk: just queue and bail. We cap the pending
91
+ // buffer at MAX_LINE_BYTES so a peer that never sends a newline cannot
92
+ // exhaust memory or pin the event loop.
93
+ if (nlInChunk === -1) {
94
+ if (projected > MAX_LINE_BYTES) {
95
+ this._resetChunks();
96
+ this._discardingFrame = true;
97
+ this._emitParseError(new Error(`JSON-RPC frame exceeded ${MAX_LINE_BYTES} bytes without a newline`));
98
+ return;
99
+ }
100
+ this._chunks.push(chunk);
101
+ this._chunksLen = projected;
102
+ return;
103
+ }
104
+ // Newline present — flatten once and process all complete lines. If the
105
+ // pending data + this chunk exceeds the cap and we have no terminator
106
+ // before the cap, reset and report a parse error.
107
+ this._chunks.push(chunk);
108
+ this._chunksLen = projected;
109
+ let buf = Buffer.concat(this._chunks, this._chunksLen);
110
+ this._resetChunks();
111
+ while (true) {
112
+ const idx = buf.indexOf(0x0a);
113
+ if (idx === -1)
114
+ break;
115
+ if (idx > MAX_LINE_BYTES) {
116
+ // The frame before the newline is too large; drop it and resync to
117
+ // the byte after the newline.
118
+ buf = buf.subarray(idx + 1);
119
+ this._emitParseError(new Error(`JSON-RPC frame exceeded ${MAX_LINE_BYTES} bytes without a newline`));
120
+ continue;
121
+ }
122
+ const line = buf.toString("utf8", 0, idx).replace(/\r$/, "");
123
+ buf = buf.subarray(idx + 1);
124
+ this._handleLine(line);
125
+ }
126
+ if (buf.length > 0) {
127
+ if (buf.length > MAX_LINE_BYTES) {
128
+ this._discardingFrame = true;
129
+ this._emitParseError(new Error(`JSON-RPC frame exceeded ${MAX_LINE_BYTES} bytes without a newline`));
130
+ return;
131
+ }
132
+ this._chunks.push(buf);
133
+ this._chunksLen = buf.length;
134
+ }
135
+ };
136
+ _onstdinError = (error) => {
137
+ this.onerror?.(error);
138
+ };
139
+ _onstdinEnd = () => {
140
+ void this.close();
141
+ };
142
+ _resetChunks() {
143
+ this._chunks = [];
144
+ this._chunksLen = 0;
145
+ }
146
+ _handleLine(line) {
147
+ if (line.length === 0)
148
+ return;
149
+ let parsed;
150
+ try {
151
+ parsed = JSON.parse(line);
152
+ }
153
+ catch (error) {
154
+ const detail = error instanceof Error ? error.message : String(error);
155
+ this._emitParseError(error instanceof Error ? error : new Error(detail));
156
+ return;
157
+ }
158
+ this.onmessage?.(parsed);
159
+ }
160
+ _emitParseError(error) {
161
+ // Drop the envelope when stdout is saturated or already closed. Without
162
+ // this drop a peer that floods malformed lines without reading stdout
163
+ // could pin unbounded -32700 replies in Node's internal write buffer.
164
+ // The onerror hook still surfaces every parse failure.
165
+ if (!this._closed && !this._stdoutSaturated) {
166
+ const envelope = {
167
+ jsonrpc: "2.0",
168
+ id: null,
169
+ error: { code: -32700, message: "Parse error", data: error.message },
170
+ };
171
+ try {
172
+ const ok = this._stdout.write(`${JSON.stringify(envelope)}\n`);
173
+ if (!ok) {
174
+ this._markSaturated();
175
+ }
176
+ }
177
+ catch (writeErr) {
178
+ this.onerror?.(writeErr instanceof Error ? writeErr : new Error(String(writeErr)));
179
+ }
180
+ }
181
+ this.onerror?.(error);
182
+ }
183
+ _markSaturated() {
184
+ this._stdoutSaturated = true;
185
+ if (this._drainAttached)
186
+ return;
187
+ this._drainAttached = true;
188
+ this._stdout.once("drain", () => {
189
+ this._stdoutSaturated = false;
190
+ this._drainAttached = false;
191
+ });
192
+ }
193
+ _writeStdout(json) {
194
+ return new Promise((resolve, reject) => {
195
+ let written;
196
+ try {
197
+ written = this._stdout.write(json);
198
+ }
199
+ catch (err) {
200
+ reject(err instanceof Error ? err : new Error(String(err)));
201
+ return;
202
+ }
203
+ if (written) {
204
+ resolve();
205
+ return;
206
+ }
207
+ this._stdoutSaturated = true;
208
+ const onDrain = () => {
209
+ this._stdoutSaturated = false;
210
+ cleanup();
211
+ resolve();
212
+ };
213
+ const onClose = () => {
214
+ cleanup();
215
+ reject(new Error("stdout closed before drain"));
216
+ };
217
+ const onError = (err) => {
218
+ cleanup();
219
+ reject(err);
220
+ };
221
+ const cleanup = () => {
222
+ this._stdout.off("drain", onDrain);
223
+ this._stdout.off("close", onClose);
224
+ this._stdout.off("error", onError);
225
+ };
226
+ this._stdout.once("drain", onDrain);
227
+ this._stdout.once("close", onClose);
228
+ this._stdout.once("error", onError);
229
+ });
230
+ }
231
+ /** Test helper: feed bytes through the same path as live stdin data. */
232
+ feedForTest(chunk) {
233
+ const buf = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
234
+ this._ondata(buf);
235
+ }
236
+ }
237
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@framedash/mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for the Framedash analytics platform",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "bin": {
14
+ "framedash-mcp": "./dist/index.js"
15
+ },
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "default": "./dist/index.js"
22
+ }
23
+ },
24
+ "dependencies": {
25
+ "@modelcontextprotocol/sdk": "^1.27.0",
26
+ "zod": "^3.25.0",
27
+ "@framedash/api-client": "0.1.0"
28
+ },
29
+ "engines": {
30
+ "node": ">=18.0.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^22.0.0",
34
+ "@vitest/coverage-v8": "^3.2.0",
35
+ "typescript": "^5.7.0",
36
+ "vitest": "^3.2.0"
37
+ },
38
+ "scripts": {
39
+ "build": "tsc -p tsconfig.build.json",
40
+ "type-check": "tsc --noEmit",
41
+ "test": "vitest run",
42
+ "test:coverage": "vitest run --coverage",
43
+ "clean": "rm -rf dist"
44
+ }
45
+ }