@dashtray/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/README.md CHANGED
@@ -1,3 +1,277 @@
1
- # Temporary Holding Version
1
+ # Dashtray 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
+ Model Context Protocol server for [Dashtray](https://dashtray.app) - send notifications to your phone from AI agents, build scripts, and automation tools.
4
+
5
+ ## Features
6
+
7
+ - **Single, focused tool:** `send_notification` - send alerts to your phone
8
+ - **No preset management:** Notifications are automatically logged in history
9
+ - **Auto-prefixed:** All notifications prefixed with "MCP - " for clarity
10
+ - **Rate-limited:** Per-plan limits (Starter 100/min, Pro 1000/min)
11
+ - **Secure:** SHA256-hashed API keys, bearer token auth
12
+ - **Lightweight:** Minimal dependencies, ~30KB bundle
13
+
14
+ ## Installation
15
+
16
+ ### Option 1: From npm
17
+
18
+ ```bash
19
+ npm install @dashtray/mcp-server
20
+ # or run directly:
21
+ npx @dashtray/mcp-server
22
+ ```
23
+
24
+ ## Setup
25
+
26
+ ### 1. Get Your API Key
27
+
28
+ 1. Open **Dashtray** on your phone
29
+ 2. Go to **Settings → Integrations → API Keys**
30
+ 3. Tap **Generate New Key**
31
+ 4. Give it a name (e.g., "Claude Desktop")
32
+ 5. **Copy the full key** (starts with `sk_test_`)
33
+ 6. Save it securely - you won't see it again
34
+
35
+ ### 2. Set Environment Variables
36
+
37
+ Create a `.env` file or export in your shell:
38
+
39
+ ```bash
40
+ export DASHTRAY_API_KEY="sk_test_your_key_here"
41
+ export DASHTRAY_API_ENDPOINT="https://api.dashtray.app" # Optional, uses default if omitted
42
+ ```
43
+
44
+ ### 3. Configure Your Agent
45
+
46
+ #### Claude Desktop
47
+
48
+ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
49
+
50
+ ```json
51
+ {
52
+ "mcpServers": {
53
+ "dashtray": {
54
+ "command": "npx",
55
+ "args": ["-y", "@dashtray/mcp-server"],
56
+ "env": {
57
+ "DASHTRAY_API_KEY": "sk_test_your_key_here",
58
+ "DASHTRAY_API_ENDPOINT": "https://api.dashtray.app"
59
+ }
60
+ }
61
+ }
62
+ }
63
+ ```
64
+ Then restart Claude Desktop.
65
+
66
+ #### Cline
67
+
68
+ In your project's `.cline/config.json`:
69
+
70
+ ```json
71
+ {
72
+ "mcpServers": {
73
+ "dashtray": {
74
+ "command": "npx",
75
+ "args": ["-y", "@dashtray/mcp-server"],
76
+ "env": {
77
+ "DASHTRAY_API_KEY": "sk_test_your_key_here"
78
+ }
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ #### Custom Agents
85
+
86
+ Start the server and connect via stdio:
87
+
88
+ ```javascript
89
+ import { ChildProcess, spawn } from "child_process";
90
+
91
+ const server = spawn("node", ["/path/to/dashtray/packages/mcp-server/dist/index.js"], {
92
+ env: {
93
+ ...process.env,
94
+ DASHTRAY_API_KEY: "sk_test_your_key_here",
95
+ },
96
+ });
97
+
98
+ // Connect to server via server.stdio
99
+ ```
100
+
101
+ ## Usage
102
+
103
+ ### Send a Notification
104
+
105
+ ```typescript
106
+ // The send_notification tool accepts:
107
+ {
108
+ "title": "Build Passed", // Required: notification title (max 200 chars)
109
+ "body": "Production build #42 completed in 3m 21s" // Required: details (max 500 chars)
110
+ }
111
+ ```
112
+
113
+ ### Examples
114
+
115
+ **Build Alert:**
116
+ ```
117
+ Title: Build Failed
118
+ Body: Docker image build failed. Error: unauthorized. Check credentials.
119
+ ```
120
+
121
+ **Deployment Status:**
122
+ ```
123
+ Title: Deployment Complete
124
+ Body: App v1.2.3 deployed to production. 50 instances running, health check passed.
125
+ ```
126
+
127
+ **Test Results:**
128
+ ```
129
+ Title: Tests Passed
130
+ Body: All 342 tests passed. Coverage: 87%. Duration: 2m 15s.
131
+ ```
132
+
133
+ **Long-Running Task:**
134
+ ```
135
+ Title: Analysis Complete
136
+ Body: Dataset processed. 1.2M records. Anomalies: 3. Report: https://...
137
+ ```
138
+
139
+ ## API Reference
140
+
141
+ ### Tool: `send_notification`
142
+
143
+ **Description:** Send a notification to your Dashtray account. Notifications appear on your phone with "MCP - " prefix and are logged in history.
144
+
145
+ **Inputs:**
146
+ | Field | Type | Required | Max Length | Notes |
147
+ |-------|------|----------|-----------|-------|
148
+ | `title` | string | Yes | 200 | Notification title. Auto-prefixed with "MCP - " |
149
+ | `body` | string | Yes | 500 | Notification details/body |
150
+
151
+ **Outputs:**
152
+ ```json
153
+ {
154
+ "success": true,
155
+ "message": "Notification sent successfully!\n\nTitle: Build Passed\nBody: ...\n\nIt will appear on your phone with the \"MCP - \" prefix..."
156
+ }
157
+ ```
158
+
159
+ **Errors:**
160
+ - `DASHTRAY_API_KEY not set` - Missing environment variable
161
+ - `Invalid input: title is required` - Missing or empty title
162
+ - `Invalid input: body must be 500 chars or less` - Body too long
163
+ - `API error: 401 Unauthorized` - Invalid API key
164
+ - `API error: 429 Too Many Requests` - Rate limit exceeded
165
+ - `API error: 500 Internal Server Error` - Server error
166
+
167
+ ## Configuration
168
+
169
+ ### Environment Variables
170
+
171
+ | Variable | Required | Default | Description |
172
+ |----------|----------|---------|-------------|
173
+ | `DASHTRAY_API_KEY` | Yes | - | API key from Dashtray app |
174
+ | `DASHTRAY_API_ENDPOINT` | No | https://api.dashtray.app | Dashtray API URL (self-hosted or cloud) |
175
+
176
+ ### API Key Format
177
+
178
+ All keys are generated with the `sk_test_` prefix followed by 48 hex characters, regardless of plan.
179
+
180
+ ## Rate Limiting
181
+
182
+ Notifications are rate-limited per-plan:
183
+
184
+ | Plan | Limit | Window |
185
+ |------|-------|--------|
186
+ | Starter | 100/min | Fixed 1-minute window |
187
+ | Pro | 1000/min | Fixed 1-minute window |
188
+
189
+ When rate limit is exceeded, the API returns `429 Too Many Requests`.
190
+
191
+ ## Troubleshooting
192
+
193
+ ### "DASHTRAY_API_KEY environment variable is not set"
194
+
195
+ **Solution:** Set the environment variable in your shell or config file.
196
+
197
+ ```bash
198
+ export DASHTRAY_API_KEY="sk_test_..."
199
+ ```
200
+
201
+ ### "API error: 401 Unauthorized"
202
+
203
+ **Solution:** Check your API key:
204
+ 1. Open Dashtray app
205
+ 2. Go to Settings → Integrations → API Keys
206
+ 3. Verify the key is still active (hasn't been revoked)
207
+ 4. Generate a new key if needed
208
+
209
+ ### "API error: 429 Too Many Requests"
210
+
211
+ **Solution:** You've hit your rate limit for this plan. Either:
212
+ - Wait a minute and retry
213
+ - Upgrade your plan in the app
214
+ - Batch notifications together
215
+
216
+ ### "Failed to send notification: ECONNREFUSED"
217
+
218
+ **Solution:** Check that:
219
+ 1. `DASHTRAY_API_ENDPOINT` is correct
220
+ 2. The endpoint is reachable from your network
221
+ 3. No firewall is blocking the connection
222
+
223
+ ### Tool not appearing in Claude Desktop
224
+
225
+ **Solution:**
226
+ 1. Restart Claude Desktop completely
227
+ 2. Check `claude_desktop_config.json` is valid JSON
228
+ 3. Verify Node 18+ is installed (`node --version`) so `npx` can fetch the server
229
+
230
+ ### "Invalid input: title is required"
231
+
232
+ **Solution:** The title must be a non-empty string:
233
+
234
+ ```typescript
235
+ // ❌ Wrong
236
+ send_notification({ body: "Only body" })
237
+
238
+ // ✓ Right
239
+ send_notification({ title: "Alert", body: "Details" })
240
+ ```
241
+
242
+ ## Development
243
+
244
+ ### Build
245
+
246
+ ```bash
247
+ npm run build
248
+ ```
249
+
250
+ ### Watch Mode
251
+
252
+ ```bash
253
+ npm run watch
254
+ ```
255
+
256
+ ### Manual Testing
257
+
258
+ ```bash
259
+ export DASHTRAY_API_KEY="sk_test_..."
260
+ node dist/index.js
261
+ ```
262
+
263
+ Then send a test request (requires MCP client like Claude Desktop).
264
+
265
+ ## Support
266
+
267
+ - **Dashtray App Issues:** Settings → Help & Support
268
+ - **MCP Server Issues:** support@dashtray.app
269
+ - **API Questions:** See [API Reference](https://docs.dashtray.app/api-reference/overview)
270
+
271
+ ## License
272
+
273
+ MIT
274
+
275
+ ---
276
+
277
+ **Dashtray MCP Server** - Minimal, focused, production-ready.
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Dashtray MCP Server
4
+ *
5
+ * Model Context Protocol server that provides a single tool:
6
+ * - send_notification: Send notifications to your phone via Dashtray
7
+ *
8
+ * Usage:
9
+ * - Local (stdio): node dist/index.js
10
+ * - Environment: Set DASHTRAY_API_KEY and DASHTRAY_API_ENDPOINT
11
+ *
12
+ * Configuration:
13
+ * - Get API key from: Dashtray app → Settings → Integrations → API Keys
14
+ * - Default endpoint: https://api.dashtray.app (or self-hosted)
15
+ *
16
+ * SDK notes (matches @modelcontextprotocol/sdk >= 1.x):
17
+ * - `Server` from `server/index.js`; the stdio transport lives in
18
+ * `server/stdio.js` and request schemas/types in `types.js`.
19
+ * - Handlers register against the exported request SCHEMAS (validated
20
+ * end-to-end by the SDK), not string method names.
21
+ */
22
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,223 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Dashtray MCP Server
4
+ *
5
+ * Model Context Protocol server that provides a single tool:
6
+ * - send_notification: Send notifications to your phone via Dashtray
7
+ *
8
+ * Usage:
9
+ * - Local (stdio): node dist/index.js
10
+ * - Environment: Set DASHTRAY_API_KEY and DASHTRAY_API_ENDPOINT
11
+ *
12
+ * Configuration:
13
+ * - Get API key from: Dashtray app → Settings → Integrations → API Keys
14
+ * - Default endpoint: https://api.dashtray.app (or self-hosted)
15
+ *
16
+ * SDK notes (matches @modelcontextprotocol/sdk >= 1.x):
17
+ * - `Server` from `server/index.js`; the stdio transport lives in
18
+ * `server/stdio.js` and request schemas/types in `types.js`.
19
+ * - Handlers register against the exported request SCHEMAS (validated
20
+ * end-to-end by the SDK), not string method names.
21
+ */
22
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
23
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
24
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
25
+ import { z } from "zod";
26
+ /**
27
+ * Dashtray Notification Schema
28
+ * Matches POST /api/v1/notifications/send endpoint
29
+ */
30
+ // Limits match the backend's validateNotificationInput (title 1–200,
31
+ // body 1–500) exactly - rejecting early with a clear message beats letting
32
+ // the API 400 with a generic validation error.
33
+ const NotificationSchema = z.object({
34
+ title: z
35
+ .string()
36
+ .min(1, "Title is required")
37
+ .max(200, "Title must be 200 chars or less"),
38
+ body: z
39
+ .string()
40
+ .min(1, "Body is required")
41
+ .max(500, "Body must be 500 chars or less"),
42
+ source: z
43
+ .string()
44
+ .min(1)
45
+ .max(40, "Source must be 40 chars or less")
46
+ .optional(),
47
+ });
48
+ /**
49
+ * Get API credentials from environment
50
+ */
51
+ function getApiCredentials() {
52
+ const apiKey = process.env.DASHTRAY_API_KEY;
53
+ if (!apiKey) {
54
+ throw new Error("DASHTRAY_API_KEY environment variable is not set. " +
55
+ "Get it from: Dashtray app → Settings → Integrations → API Keys");
56
+ }
57
+ const endpoint = process.env.DASHTRAY_API_ENDPOINT || "https://api.dashtray.app";
58
+ return { apiKey, endpoint };
59
+ }
60
+ /**
61
+ * Send notification to Dashtray
62
+ */
63
+ async function sendNotification(notification, apiKey, endpoint) {
64
+ const url = `${endpoint}/api/v1/notifications/send`;
65
+ try {
66
+ const response = await fetch(url, {
67
+ method: "POST",
68
+ headers: {
69
+ "Content-Type": "application/json",
70
+ Authorization: `Bearer ${apiKey}`,
71
+ },
72
+ body: JSON.stringify({
73
+ title: notification.title,
74
+ body: notification.body,
75
+ source: notification.source,
76
+ }),
77
+ });
78
+ if (!response.ok) {
79
+ const errorData = (await response.json().catch(() => ({})));
80
+ return {
81
+ ok: false,
82
+ error: `API error: ${response.status} ${response.statusText}. ${errorData.error || ""}`,
83
+ };
84
+ }
85
+ const data = (await response.json());
86
+ return {
87
+ ok: true,
88
+ data: data.data || { ...notification, id: "sent", createdAt: Date.now() },
89
+ };
90
+ }
91
+ catch (error) {
92
+ return {
93
+ ok: false,
94
+ error: `Failed to send notification: ${error instanceof Error ? error.message : String(error)}`,
95
+ };
96
+ }
97
+ }
98
+ /**
99
+ * MCP Server Main
100
+ */
101
+ async function main() {
102
+ const { apiKey, endpoint } = getApiCredentials();
103
+ const server = new Server({
104
+ name: "dashtray",
105
+ version: "1.0.0",
106
+ }, {
107
+ capabilities: {
108
+ tools: {},
109
+ },
110
+ });
111
+ // Define the send_notification tool
112
+ const sendNotificationTool = {
113
+ name: "send_notification",
114
+ description: "Send a notification to your Dashtray account. " +
115
+ "Notifications appear on your phone with 'MCP - ' prefix and are also logged in history. " +
116
+ "Perfect for build alerts, deployment updates, workflow notifications, etc.",
117
+ inputSchema: {
118
+ type: "object",
119
+ properties: {
120
+ title: {
121
+ type: "string",
122
+ description: "Short notification title (max 200 chars). Will be prefixed with 'MCP - ' in the app.",
123
+ examples: [
124
+ "Build Complete",
125
+ "Deployment Failed",
126
+ "Test Suite Passed",
127
+ ],
128
+ },
129
+ body: {
130
+ type: "string",
131
+ description: "Notification body/details (max 500 chars). Can include status, error details, links, etc.",
132
+ examples: [
133
+ "Build succeeded in 2m 15s. Ready to deploy.",
134
+ "E2E tests failed on 3 files. See logs for details.",
135
+ ],
136
+ },
137
+ source: {
138
+ type: "string",
139
+ description: "Name of the app/integration that sent this (max 40 chars, e.g. 'OpenCode', 'CI'). " +
140
+ "Notifications with the same source are grouped under one alert named 'MCP - <source>' in the app. " +
141
+ "Omit to group on the generic 'MCP & API' alert.",
142
+ },
143
+ },
144
+ required: ["title", "body"],
145
+ },
146
+ };
147
+ // Register tools/list handler (schema-based, per current SDK contract)
148
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
149
+ return {
150
+ tools: [sendNotificationTool],
151
+ };
152
+ });
153
+ // Handle tool calls
154
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
155
+ if (request.params.name !== "send_notification") {
156
+ return {
157
+ content: [
158
+ {
159
+ type: "text",
160
+ text: `Unknown tool: ${request.params.name}`,
161
+ },
162
+ ],
163
+ isError: true,
164
+ };
165
+ }
166
+ try {
167
+ // Validate input
168
+ const args = request.params.arguments;
169
+ const validated = NotificationSchema.parse({
170
+ title: args.title,
171
+ body: args.body,
172
+ });
173
+ // Send to Dashtray
174
+ const result = await sendNotification(validated, apiKey, endpoint);
175
+ if (!result.ok) {
176
+ return {
177
+ content: [
178
+ {
179
+ type: "text",
180
+ text: `Error sending notification: ${result.error}`,
181
+ },
182
+ ],
183
+ isError: true,
184
+ };
185
+ }
186
+ // Success response
187
+ return {
188
+ content: [
189
+ {
190
+ type: "text",
191
+ text: `Notification sent successfully!\n\nTitle: ${result.data?.title}\nBody: ${result.data?.body}\n\nIt will appear on your phone with the "MCP - " prefix and also be logged in your Dashtray history.`,
192
+ },
193
+ ],
194
+ };
195
+ }
196
+ catch (error) {
197
+ const message = error instanceof z.ZodError
198
+ ? `Invalid input: ${error.issues.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ")}`
199
+ : error instanceof Error
200
+ ? error.message
201
+ : String(error);
202
+ return {
203
+ content: [
204
+ {
205
+ type: "text",
206
+ text: `Error: ${message}`,
207
+ },
208
+ ],
209
+ isError: true,
210
+ };
211
+ }
212
+ });
213
+ // Start server with stdio transport
214
+ const transport = new StdioServerTransport();
215
+ await server.connect(transport);
216
+ console.error("Dashtray MCP Server started");
217
+ console.error(`Using endpoint: ${endpoint}`);
218
+ console.error("Listening on stdio...");
219
+ }
220
+ main().catch((error) => {
221
+ console.error("Fatal error:", error);
222
+ process.exit(1);
223
+ });
package/package.json CHANGED
@@ -1,6 +1,48 @@
1
1
  {
2
2
  "name": "@dashtray/mcp-server",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "1.0.1",
4
+ "description": "Dashtray MCP Server - send notifications to your phone via MCP agents",
5
+ "type": "module",
6
+ "bin": {
7
+ "dashtray-mcp": "./dist/index.js"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "main": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "files": [
18
+ "dist",
19
+ "README.md"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsc",
23
+ "dev": "node dist/index.js",
24
+ "watch": "tsc --watch",
25
+ "prepublishOnly": "npm run build"
26
+ },
27
+ "keywords": [
28
+ "mcp",
29
+ "model-context-protocol",
30
+ "notifications",
31
+ "dashtray",
32
+ "agents"
33
+ ],
34
+ "author": "Dashtray",
35
+ "license": "MIT",
36
+ "homepage": "https://dashtray.app",
37
+ "dependencies": {
38
+ "@modelcontextprotocol/sdk": "^1.0.0",
39
+ "zod": "catalog:"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^24.3.0",
43
+ "typescript": "^5.4.0"
44
+ },
45
+ "engines": {
46
+ "node": ">=18.0.0"
47
+ }
6
48
  }