├── src ├── index.d.ts ├── index.d.ts.map ├── index.js.map ├── index.js └── index.ts ├── .env.example ├── tsconfig.json ├── package.json ├── .gitignore └── README.md /src/index.d.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | export {}; 3 | //# sourceMappingURL=index.d.ts.map -------------------------------------------------------------------------------- /src/index.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # MindReset MCP Server Environment Variables 2 | # Copy this file to .env and fill in your actual values 3 | 4 | # Required: Your MindReset device serial number 5 | # Get this from the MindReset App 6 | MINDRESET_DEVICE_ID=your_device_serial_number_here 7 | 8 | # Required: Your MindReset device secret/API key 9 | # Get this from the MindReset App 10 | MINDRESET_DEVICE_SECRET=your_device_secret_here 11 | 12 | # Optional: Base URL for MindReset API (default: https://dot.mindreset.tech/api) 13 | # MINDRESET_BASE_URL=https://dot.mindreset.tech/api -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "CommonJS", 5 | "lib": ["ES2022"], 6 | "outDir": "./dist", 7 | "rootDir": "./src", 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "declaration": true, 13 | "declarationMap": true, 14 | "sourceMap": true, 15 | "types": ["node"], 16 | "moduleResolution": "node", 17 | "allowSyntheticDefaultImports": true, 18 | "resolveJsonModule": true 19 | }, 20 | "include": ["src/**/*"], 21 | "exclude": ["node_modules", "dist"] 22 | } 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mindreset-mcp-server", 3 | "version": "1.0.0", 4 | "description": "MCP server for MindReset devices providing text and image API tools", 5 | "main": "dist/index.js", 6 | "bin": { 7 | "mindreset-mcp": "dist/index.js" 8 | }, 9 | "scripts": { 10 | "build": "tsc", 11 | "dev": "tsx src/index.ts", 12 | "start": "node dist/index.js", 13 | "test": "echo \"Error: no test specified\" && exit 1" 14 | }, 15 | "keywords": [ 16 | "mcp", 17 | "mindreset", 18 | "model-context-protocol", 19 | "ai", 20 | "server" 21 | ], 22 | "author": "", 23 | "license": "ISC", 24 | "dependencies": { 25 | "@modelcontextprotocol/sdk": "^1.17.2", 26 | "undici": "^6.19.8", 27 | "zod": "^3.25.76" 28 | }, 29 | "devDependencies": { 30 | "@types/node": "^24.2.1", 31 | "tsx": "^4.20.3", 32 | "typescript": "^5.9.2" 33 | }, 34 | "engines": { 35 | "node": ">=18" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | 7 | # Build output 8 | dist/ 9 | build/ 10 | 11 | # Environment variables 12 | .env 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | # Runtime data 19 | pids 20 | *.pid 21 | *.seed 22 | *.pid.lock 23 | 24 | # Coverage directory used by tools like istanbul 25 | coverage/ 26 | *.lcov 27 | 28 | # nyc test coverage 29 | .nyc_output 30 | 31 | # Dependency directories 32 | jspm_packages/ 33 | 34 | # Optional npm cache directory 35 | .npm 36 | 37 | # Optional eslint cache 38 | .eslintcache 39 | 40 | # Optional REPL history 41 | .node_repl_history 42 | 43 | # Output of 'npm pack' 44 | *.tgz 45 | 46 | # Yarn Integrity file 47 | .yarn-integrity 48 | 49 | # dotenv environment variables file 50 | .env.test 51 | 52 | # parcel-bundler cache (https://parceljs.org/) 53 | .cache 54 | .parcel-cache 55 | 56 | # next.js build output 57 | .next 58 | 59 | # nuxt.js build output 60 | .nuxt 61 | 62 | # vuepress build output 63 | .vuepress/dist 64 | 65 | # Serverless directories 66 | .serverless/ 67 | 68 | # FuseBox cache 69 | .fusebox/ 70 | 71 | # DynamoDB Local files 72 | .dynamodb/ 73 | 74 | # TernJS port file 75 | .tern-port 76 | 77 | # Stores VSCode versions used for testing VSCode extensions 78 | .vscode-test 79 | 80 | # IDE 81 | .vscode/ 82 | .idea/ 83 | *.swp 84 | *.swo 85 | *~ 86 | 87 | # OS 88 | .DS_Store 89 | .DS_Store? 90 | ._* 91 | .Spotlight-V100 92 | .Trashes 93 | ehthumbs.db 94 | Thumbs.db -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MindReset MCP Server 2 | 3 | A Model Context Protocol (MCP) server that provides tools for interacting with MindReset devices. This server implements the official MindReset API endpoints for displaying text and images on MindReset device screens. 4 | 5 | ## Features 6 | 7 | - **Simple Text API**: Display basic text content on MindReset devices 8 | - **Complex Text API**: Display text with signatures and formatting 9 | - **Text with Icons**: Display text content with custom icons and tap-to-jump links 10 | - **Image API**: Display PNG images with customizable borders 11 | 12 | ## Prerequisites 13 | 14 | - Node.js >= 18 15 | - A MindReset device with API access 16 | - Device ID and Secret from MindReset App 17 | 18 | ## Installation 19 | 20 | 1. Clone or download this repository 21 | 2. Install dependencies: 22 | ```bash 23 | npm install 24 | ``` 25 | 26 | 3. Build the project: 27 | ```bash 28 | npm run build 29 | ``` 30 | 31 | ## Configuration 32 | 33 | Set the required environment variables: 34 | 35 | ```bash 36 | export MINDRESET_DEVICE_ID="your_device_serial_number" 37 | export MINDRESET_DEVICE_SECRET="your_device_secret" 38 | ``` 39 | 40 | You can obtain these credentials from the MindReset App by following the official MindReset API documentation. 41 | 42 | ## Usage 43 | 44 | ### Development Mode 45 | 46 | ```bash 47 | npm run dev 48 | ``` 49 | 50 | ### Production Mode 51 | 52 | ```bash 53 | npm start 54 | ``` 55 | 56 | ## Available Tools 57 | 58 | ### mindreset_simple_text 59 | 60 | Send simple text content to a MindReset device screen. 61 | 62 | **Parameters:** 63 | - `deviceId` (required): Device serial number 64 | - `title` (optional): Text title 65 | - `message` (required): Text content to display 66 | 67 | **Example:** 68 | ```json 69 | { 70 | "deviceId": "ABCD1234ABCD", 71 | "title": "Hello World", 72 | "message": "I can swallow glass without harming myself" 73 | } 74 | ``` 75 | 76 | ### mindreset_complex_text 77 | 78 | Send complex text content with signature to a MindReset device screen. 79 | 80 | **Parameters:** 81 | - `deviceId` (required): Device serial number 82 | - `title` (optional): Text title 83 | - `message` (required): Text content to display 84 | - `signature` (optional): Text signature/footer 85 | 86 | **Example:** 87 | ```json 88 | { 89 | "deviceId": "ABCD1234ABCD", 90 | "title": "Verification Code Helper", 91 | "message": "A verification code from 'Shao Pai'\n205112", 92 | "signature": "August 4, 2025 19:58" 93 | } 94 | ``` 95 | 96 | ### mindreset_text_with_icon 97 | 98 | Send text content with icon and optional link to a MindReset device screen. 99 | 100 | **Parameters:** 101 | - `deviceId` (required): Device serial number 102 | - `title` (optional): Text title 103 | - `message` (required): Text content to display 104 | - `signature` (optional): Text signature/footer 105 | - `icon` (optional): Base64 encoded PNG icon data (40px*40px) 106 | - `link` (optional): HTTP/HTTPS link or Scheme URL for tap-to-jump 107 | 108 | **Example:** 109 | ```json 110 | { 111 | "deviceId": "ABCD1234ABCD", 112 | "title": "Daily Health", 113 | "message": "Calories burned: 702 kcal\nSteps today: 4183\nStanding time: 62 minutes", 114 | "signature": "August 4, 2025 20:16", 115 | "icon": "iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAA...", 116 | "link": "x-apple-health://" 117 | } 118 | ``` 119 | 120 | ### mindreset_image 121 | 122 | Display PNG image on a MindReset device screen. 123 | 124 | **Parameters:** 125 | - `deviceId` (required): Device serial number 126 | - `image` (required): Base64 encoded PNG image data (296px*152px) 127 | - `border` (optional): "0" for white border, "1" for black border 128 | - `link` (optional): HTTP/HTTPS link or Scheme URL for tap-to-jump 129 | 130 | **Example:** 131 | ```json 132 | { 133 | "deviceId": "ABCD1234ABCD", 134 | "image": "iVBORw0KGgoAAAANSUhEUgAAASgAAACYCAYAAABXunTYAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAA...", 135 | "border": "0", 136 | "link": "https://dot.mindreset.tech" 137 | } 138 | ``` 139 | 140 | ## Integration with Claude Desktop 141 | 142 | To use this MCP server with Claude Desktop, add the following configuration to your Claude Desktop settings: 143 | 144 | ```json 145 | { 146 | "mcpServers": { 147 | "mindreset": { 148 | "command": "node", 149 | "args": ["/path/to/your/project/dist/index.js"], 150 | "env": { 151 | "MINDRESET_DEVICE_ID": "your_device_id", 152 | "MINDRESET_DEVICE_SECRET": "your_device_secret" 153 | } 154 | } 155 | } 156 | } 157 | ``` 158 | 159 | ## Error Handling 160 | 161 | The server provides comprehensive error handling for: 162 | - Missing or invalid authentication credentials 163 | - Network connectivity issues 164 | - API response errors 165 | - Input validation errors 166 | 167 | ## API Documentation 168 | 169 | For detailed information about the MindReset API, refer to the official documentation: 170 | - [Text API Documentation](https://dot.mindreset.tech/docs/server/template/api/text_api) 171 | - [Image API Documentation](https://dot.mindreset.tech/docs/server/template/api/image_api) 172 | 173 | ## License 174 | 175 | ISC -------------------------------------------------------------------------------- /src/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js"); 5 | const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js"); 6 | const types_js_1 = require("@modelcontextprotocol/sdk/types.js"); 7 | const zod_1 = require("zod"); 8 | // Schema definitions for MindReset API requests 9 | const SimpleTextSchema = zod_1.z.object({ 10 | deviceId: zod_1.z.string().describe("Device serial number"), 11 | title: zod_1.z.string().optional().describe("Text title"), 12 | message: zod_1.z.string().describe("Text content"), 13 | }); 14 | const ComplexTextSchema = zod_1.z.object({ 15 | deviceId: zod_1.z.string().describe("Device serial number"), 16 | title: zod_1.z.string().optional().describe("Text title"), 17 | message: zod_1.z.string().describe("Text content"), 18 | signature: zod_1.z.string().optional().describe("Text signature"), 19 | }); 20 | const TextWithIconSchema = zod_1.z.object({ 21 | deviceId: zod_1.z.string().describe("Device serial number"), 22 | title: zod_1.z.string().optional().describe("Text title"), 23 | message: zod_1.z.string().describe("Text content"), 24 | signature: zod_1.z.string().optional().describe("Text signature"), 25 | icon: zod_1.z.string().optional().describe("Base64 encoded PNG icon data (40px*40px)"), 26 | link: zod_1.z.string().optional().describe("HTTP/HTTPS link or Scheme URL for tap-to-jump"), 27 | }); 28 | const ImageSchema = zod_1.z.object({ 29 | deviceId: zod_1.z.string().describe("Device serial number"), 30 | image: zod_1.z.string().describe("Base64 encoded PNG image data (296px*152px)"), 31 | border: zod_1.z.enum(["0", "1"]).optional().describe("0 for white border, 1 for black border"), 32 | link: zod_1.z.string().optional().describe("HTTP/HTTPS link or Scheme URL for tap-to-jump"), 33 | }); 34 | // Response schema 35 | const ApiResponseSchema = zod_1.z.object({ 36 | code: zod_1.z.number(), 37 | message: zod_1.z.string(), 38 | result: zod_1.z.any().optional(), 39 | }); 40 | class MindResetMcpServer { 41 | server; 42 | baseUrl = "https://dot.mindreset.tech/api"; 43 | constructor() { 44 | this.server = new index_js_1.Server({ 45 | name: "mindreset-mcp-server", 46 | version: "1.0.0", 47 | }, { 48 | capabilities: { 49 | tools: {}, 50 | }, 51 | }); 52 | this.setupToolHandlers(); 53 | this.setupErrorHandling(); 54 | } 55 | setupErrorHandling() { 56 | this.server.onerror = (error) => console.error("[MCP Error]", error); 57 | process.on("SIGINT", async () => { 58 | await this.server.close(); 59 | process.exit(0); 60 | }); 61 | } 62 | getAuthHeaders() { 63 | const deviceId = process.env.MINDRESET_DEVICE_ID; 64 | const deviceSecret = process.env.MINDRESET_DEVICE_SECRET; 65 | if (!deviceId || !deviceSecret) { 66 | throw new Error("Missing authentication. Please set MINDRESET_DEVICE_ID and MINDRESET_DEVICE_SECRET environment variables."); 67 | } 68 | return { 69 | "Authorization": `Bearer ${deviceSecret}`, 70 | "Content-Type": "application/json", 71 | "User-Agent": "mindreset-mcp-server/1.0.0", 72 | "X-Device-ID": deviceId, 73 | "X-Device-Secret": deviceSecret, 74 | }; 75 | } 76 | async makeApiRequest(endpoint, data) { 77 | try { 78 | const headers = this.getAuthHeaders(); 79 | const response = await fetch(`${this.baseUrl}${endpoint}`, { 80 | method: "POST", 81 | headers, 82 | body: JSON.stringify(data), 83 | }); 84 | if (!response.ok) { 85 | const errorText = await response.text(); 86 | return { 87 | content: [ 88 | { 89 | type: "text", 90 | text: `API request failed: ${response.status} ${response.statusText} - ${errorText}`, 91 | }, 92 | ], 93 | isError: true, 94 | }; 95 | } 96 | const result = await response.json(); 97 | const validatedResult = ApiResponseSchema.parse(result); 98 | return { 99 | content: [ 100 | { 101 | type: "text", 102 | text: JSON.stringify(validatedResult, null, 2), 103 | }, 104 | ], 105 | }; 106 | } 107 | catch (error) { 108 | return { 109 | content: [ 110 | { 111 | type: "text", 112 | text: `Request failed: ${error instanceof Error ? error.message : String(error)}`, 113 | }, 114 | ], 115 | isError: true, 116 | }; 117 | } 118 | } 119 | setupToolHandlers() { 120 | this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => { 121 | const tools = [ 122 | { 123 | name: "mindreset_simple_text", 124 | description: "Send simple text content to MindReset device screen", 125 | inputSchema: types_js_1.ToolSchema.shape.inputSchema.parse(SimpleTextSchema), 126 | }, 127 | { 128 | name: "mindreset_complex_text", 129 | description: "Send complex text content with signature to MindReset device screen", 130 | inputSchema: types_js_1.ToolSchema.shape.inputSchema.parse(ComplexTextSchema), 131 | }, 132 | { 133 | name: "mindreset_text_with_icon", 134 | description: "Send text content with icon and optional link to MindReset device screen", 135 | inputSchema: types_js_1.ToolSchema.shape.inputSchema.parse(TextWithIconSchema), 136 | }, 137 | { 138 | name: "mindreset_image", 139 | description: "Display PNG image on MindReset device screen", 140 | inputSchema: types_js_1.ToolSchema.shape.inputSchema.parse(ImageSchema), 141 | }, 142 | ]; 143 | return { tools }; 144 | }); 145 | this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => { 146 | const { name, arguments: args } = request.params; 147 | try { 148 | switch (name) { 149 | case "mindreset_simple_text": { 150 | const data = SimpleTextSchema.parse(args); 151 | return await this.makeApiRequest("/open/text", data); 152 | } 153 | case "mindreset_complex_text": { 154 | const data = ComplexTextSchema.parse(args); 155 | return await this.makeApiRequest("/open/text", data); 156 | } 157 | case "mindreset_text_with_icon": { 158 | const data = TextWithIconSchema.parse(args); 159 | return await this.makeApiRequest("/open/text", data); 160 | } 161 | case "mindreset_image": { 162 | const data = ImageSchema.parse(args); 163 | return await this.makeApiRequest("/open/image", data); 164 | } 165 | default: 166 | return { 167 | content: [ 168 | { 169 | type: "text", 170 | text: `Unknown tool: ${name}`, 171 | }, 172 | ], 173 | isError: true, 174 | }; 175 | } 176 | } 177 | catch (error) { 178 | return { 179 | content: [ 180 | { 181 | type: "text", 182 | text: `Validation error: ${error instanceof Error ? error.message : String(error)}`, 183 | }, 184 | ], 185 | isError: true, 186 | }; 187 | } 188 | }); 189 | } 190 | async run() { 191 | const transport = new stdio_js_1.StdioServerTransport(); 192 | await this.server.connect(transport); 193 | console.error("MindReset MCP server started on stdio"); 194 | } 195 | } 196 | // Main execution 197 | async function main() { 198 | const server = new MindResetMcpServer(); 199 | await server.run(); 200 | } 201 | main().catch((error) => { 202 | console.error("Server failed to start:", error); 203 | process.exit(1); 204 | }); 205 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { Server } from "@modelcontextprotocol/sdk/server/index.js"; 4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 5 | import { fetch } from "undici"; 6 | import { 7 | CallToolRequestSchema, 8 | CallToolResult, 9 | ListToolsRequestSchema, 10 | Tool, 11 | ToolSchema, 12 | } from "@modelcontextprotocol/sdk/types.js"; 13 | import { z } from "zod"; 14 | 15 | // Schema definitions for MindReset API requests 16 | const SimpleTextSchema = z.object({ 17 | deviceId: z.string().describe("Device serial number"), 18 | title: z.string().optional().describe("Text title"), 19 | message: z.string().describe("Text content"), 20 | }); 21 | 22 | const ComplexTextSchema = z.object({ 23 | deviceId: z.string().describe("Device serial number"), 24 | title: z.string().optional().describe("Text title"), 25 | message: z.string().describe("Text content"), 26 | signature: z.string().optional().describe("Text signature"), 27 | }); 28 | 29 | const TextWithIconSchema = z.object({ 30 | deviceId: z.string().describe("Device serial number"), 31 | title: z.string().optional().describe("Text title"), 32 | message: z.string().describe("Text content"), 33 | signature: z.string().optional().describe("Text signature"), 34 | icon: z.string().optional().describe("Base64 encoded PNG icon data (40px*40px)"), 35 | link: z.string().optional().describe("HTTP/HTTPS link or Scheme URL for tap-to-jump"), 36 | }); 37 | 38 | const ImageSchema = z.object({ 39 | deviceId: z.string().describe("Device serial number"), 40 | image: z.string().describe("Base64 encoded PNG image data (296px*152px)"), 41 | border: z.enum(["0", "1"]).optional().describe("0 for white border, 1 for black border"), 42 | link: z.string().optional().describe("HTTP/HTTPS link or Scheme URL for tap-to-jump"), 43 | }); 44 | 45 | // Note: Do not strictly validate API responses; return raw payload to client. 46 | 47 | class MindResetMcpServer { 48 | private server: Server; 49 | private baseUrl: string = "https://dot.mindreset.tech/api"; 50 | 51 | constructor() { 52 | this.server = new Server( 53 | { 54 | name: "mindreset-mcp-server", 55 | version: "1.0.0", 56 | }, 57 | { 58 | capabilities: { 59 | tools: {}, 60 | }, 61 | } 62 | ); 63 | 64 | this.setupToolHandlers(); 65 | this.setupErrorHandling(); 66 | } 67 | 68 | private setupErrorHandling(): void { 69 | this.server.onerror = (error) => console.error("[MCP Error]", error); 70 | process.on("SIGINT", async () => { 71 | await this.server.close(); 72 | process.exit(0); 73 | }); 74 | } 75 | 76 | private getAuthHeaders(): Record { 77 | const deviceId = process.env.MINDRESET_DEVICE_ID; 78 | const deviceSecret = process.env.MINDRESET_DEVICE_SECRET; 79 | 80 | if (!deviceId || !deviceSecret) { 81 | throw new Error( 82 | "Missing authentication. Please set MINDRESET_DEVICE_ID and MINDRESET_DEVICE_SECRET environment variables." 83 | ); 84 | } 85 | 86 | return { 87 | "Authorization": `Bearer ${deviceSecret}`, 88 | "Content-Type": "application/json", 89 | "User-Agent": "mindreset-mcp-server/1.0.0", 90 | "X-Device-ID": deviceId, 91 | "X-Device-Secret": deviceSecret, 92 | }; 93 | } 94 | 95 | private async makeApiRequest(endpoint: string, data: any): Promise { 96 | try { 97 | const headers = this.getAuthHeaders(); 98 | const response = await fetch(`${this.baseUrl}${endpoint}`, { 99 | method: "POST", 100 | headers, 101 | body: JSON.stringify(data), 102 | }); 103 | 104 | if (!response.ok) { 105 | const errorText = await response.text(); 106 | return { 107 | content: [ 108 | { 109 | type: "text", 110 | text: `API request failed: ${response.status} ${response.statusText} - ${errorText}`, 111 | }, 112 | ], 113 | isError: true, 114 | }; 115 | } 116 | 117 | // Try to return JSON if available; fall back to text/empty 118 | const contentType = response.headers.get("content-type") || ""; 119 | if (contentType.includes("application/json")) { 120 | const json = await response.json(); 121 | return { 122 | content: [ 123 | { 124 | type: "text", 125 | text: JSON.stringify(json, null, 2), 126 | }, 127 | ], 128 | }; 129 | } else { 130 | const text = await response.text(); 131 | return { 132 | content: [ 133 | { 134 | type: "text", 135 | text: text || "", 136 | }, 137 | ], 138 | }; 139 | } 140 | } catch (error) { 141 | return { 142 | content: [ 143 | { 144 | type: "text", 145 | text: `Request failed: ${error instanceof Error ? error.message : String(error)}`, 146 | }, 147 | ], 148 | isError: true, 149 | }; 150 | } 151 | } 152 | 153 | private setupToolHandlers(): void { 154 | this.server.setRequestHandler(ListToolsRequestSchema, async () => { 155 | // JSON Schemas for tool inputs (as required by MCP ToolSchema) 156 | const SimpleTextJson = ToolSchema.shape.inputSchema.parse({ 157 | type: "object", 158 | properties: { 159 | deviceId: { type: "string", description: "Device serial number" }, 160 | title: { type: "string", description: "Text title" }, 161 | message: { type: "string", description: "Text content" }, 162 | }, 163 | required: ["deviceId", "message"], 164 | additionalProperties: false, 165 | }); 166 | 167 | const ComplexTextJson = ToolSchema.shape.inputSchema.parse({ 168 | type: "object", 169 | properties: { 170 | deviceId: { type: "string", description: "Device serial number" }, 171 | title: { type: "string", description: "Text title" }, 172 | message: { type: "string", description: "Text content" }, 173 | signature: { type: "string", description: "Text signature" }, 174 | }, 175 | required: ["deviceId", "message"], 176 | additionalProperties: false, 177 | }); 178 | 179 | const TextWithIconJson = ToolSchema.shape.inputSchema.parse({ 180 | type: "object", 181 | properties: { 182 | deviceId: { type: "string", description: "Device serial number" }, 183 | title: { type: "string", description: "Text title" }, 184 | message: { type: "string", description: "Text content" }, 185 | signature: { type: "string", description: "Text signature" }, 186 | icon: { type: "string", description: "Base64 encoded PNG icon data (40px*40px)" }, 187 | link: { type: "string", description: "HTTP/HTTPS link or Scheme URL for tap-to-jump" }, 188 | }, 189 | required: ["deviceId", "message"], 190 | additionalProperties: false, 191 | }); 192 | 193 | const ImageJson = ToolSchema.shape.inputSchema.parse({ 194 | type: "object", 195 | properties: { 196 | deviceId: { type: "string", description: "Device serial number" }, 197 | image: { type: "string", description: "Base64 encoded PNG image data (296px*152px)" }, 198 | border: { type: "string", enum: ["0", "1"], description: "0 for white border, 1 for black border" }, 199 | link: { type: "string", description: "HTTP/HTTPS link or Scheme URL for tap-to-jump" }, 200 | }, 201 | required: ["deviceId", "image"], 202 | additionalProperties: false, 203 | }); 204 | 205 | const tools: Tool[] = [ 206 | { 207 | name: "mindreset_simple_text", 208 | description: "Send simple text content to MindReset device screen", 209 | inputSchema: SimpleTextJson, 210 | }, 211 | { 212 | name: "mindreset_complex_text", 213 | description: "Send complex text content with signature to MindReset device screen", 214 | inputSchema: ComplexTextJson, 215 | }, 216 | { 217 | name: "mindreset_text_with_icon", 218 | description: "Send text content with icon and optional link to MindReset device screen", 219 | inputSchema: TextWithIconJson, 220 | }, 221 | { 222 | name: "mindreset_image", 223 | description: "Display PNG image on a MindReset device screen", 224 | inputSchema: ImageJson, 225 | }, 226 | ]; 227 | 228 | return { tools }; 229 | }); 230 | 231 | this.server.setRequestHandler(CallToolRequestSchema, async (request) => { 232 | const { name, arguments: args } = request.params; 233 | 234 | try { 235 | switch (name) { 236 | case "mindreset_simple_text": { 237 | const data = SimpleTextSchema.parse(args); 238 | return await this.makeApiRequest("/open/text", data); 239 | } 240 | 241 | case "mindreset_complex_text": { 242 | const data = ComplexTextSchema.parse(args); 243 | return await this.makeApiRequest("/open/text", data); 244 | } 245 | 246 | case "mindreset_text_with_icon": { 247 | const data = TextWithIconSchema.parse(args); 248 | return await this.makeApiRequest("/open/text", data); 249 | } 250 | 251 | case "mindreset_image": { 252 | const data = ImageSchema.parse(args); 253 | return await this.makeApiRequest("/open/image", data); 254 | } 255 | 256 | default: 257 | return { 258 | content: [ 259 | { 260 | type: "text", 261 | text: `Unknown tool: ${name}`, 262 | }, 263 | ], 264 | isError: true, 265 | }; 266 | } 267 | } catch (error) { 268 | return { 269 | content: [ 270 | { 271 | type: "text", 272 | text: `Validation error: ${error instanceof Error ? error.message : String(error)}`, 273 | }, 274 | ], 275 | isError: true, 276 | }; 277 | } 278 | }); 279 | } 280 | 281 | async run(): Promise { 282 | const transport = new StdioServerTransport(); 283 | await this.server.connect(transport); 284 | if (process.env.DEBUG) { 285 | console.error("MindReset MCP server started on stdio"); 286 | } 287 | } 288 | } 289 | 290 | // Main execution 291 | async function main() { 292 | const server = new MindResetMcpServer(); 293 | await server.run(); 294 | } 295 | 296 | main().catch((error) => { 297 | console.error("Server failed to start:", error); 298 | process.exit(1); 299 | }); --------------------------------------------------------------------------------