#
tokens: 2659/50000 7/7 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .gitignore
├── dist
│   └── server.js
├── LICENSE.txt
├── package-lock.json
├── package.json
├── README.md
├── src
│   ├── index.ts
│   └── server.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
1 | node_modules/
2 | dist/
3 | *.log
4 | 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
 1 | # mcp-time
 2 | 
 3 | A simple MCP server for getting current time in various formats. This server is designed to work with Claude desktop app on macOS.
 4 | 
 5 | ## Features
 6 | 
 7 | - Get current time in YYYYMMDDHHMMSS format
 8 | - Returns time in JST (Japan Standard Time)
 9 | - Utilizes macOS system date command
10 | 
11 | ## Installation
12 | 
13 | ```bash
14 | npm install mcp-time
15 | ```
16 | 
17 | ## Usage
18 | 
19 | Start the server:
20 | 
21 | ```bash
22 | npm start
23 | ```
24 | 
25 | The server will start on port 3200 by default.
26 | 
27 | ## Development
28 | 
29 | 1. Clone the repository
30 | 2. Install dependencies
31 |    ```bash
32 |    npm install
33 |    ```
34 | 3. Start development server
35 |    ```bash
36 |    npm run dev
37 |    ```
38 | 
39 | ## License
40 | 
41 | MIT License - see [LICENSE](LICENSE.txt) for details
42 | 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2020",
 4 |     "module": "ES2020",
 5 |     "moduleResolution": "node",
 6 |     "outDir": "./dist",
 7 |     "rootDir": "./src",
 8 |     "strict": true,
 9 |     "esModuleInterop": true,
10 |     "skipLibCheck": true,
11 |     "forceConsistentCasingInFileNames": true
12 |   },
13 |   "include": ["src/**/*"],
14 |   "exclude": ["node_modules", "**/*.test.ts"]
15 | }
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "mcp-time",
 3 |   "version": "1.0.0",
 4 |   "description": "MCP server for getting current time",
 5 |   "main": "dist/server.js",
 6 |   "type": "module",
 7 |   "scripts": {
 8 |     "build": "tsc && chmod +x dist/server.js",
 9 |     "prepare": "npm run build"
10 |   },
11 |   "keywords": [
12 |     "mcp",
13 |     "time",
14 |     "server"
15 |   ],
16 |   "author": "zepar",
17 |   "license": "MIT",
18 |   "dependencies": {
19 |     "@modelcontextprotocol/sdk": "1.0.1",
20 |     "zod-to-json-schema": "^3.23.5"
21 |   },
22 |   "devDependencies": {
23 |     "@types/node": "^20.11.0",
24 |     "ts-node": "^10.9.2",
25 |     "typescript": "^5.7.3"
26 |   },
27 |   "bin": {
28 |     "mcp-time": "dist/server.js"
29 |   }
30 | }
```

--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------

```
 1 | MIT License
 2 | 
 3 | Copyright (c) 2024 zepar
 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.
22 | 
```

--------------------------------------------------------------------------------
/src/server.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env node
 2 | import { Server } from "@modelcontextprotocol/sdk/server/index.js";
 3 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
 4 | import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
 5 | import { z } from "zod";
 6 | import { zodToJsonSchema } from "zod-to-json-schema";
 7 | 
 8 | // Schema definition for getCurrentTime
 9 | const GetTimeArgsSchema = z.object({});
10 | 
11 | const server = new Server({
12 |     name: "time-server",
13 |     version: "0.1.0",
14 | }, {
15 |     capabilities: {
16 |         tools: {},
17 |     },
18 | });
19 | 
20 | // Tool handlers
21 | server.setRequestHandler(ListToolsRequestSchema, async () => {
22 |     return {
23 |         tools: [
24 |             {
25 |                 name: "getCurrentTime",
26 |                 description: "Get current time in UTC",
27 |                 inputSchema: zodToJsonSchema(GetTimeArgsSchema),
28 |             },
29 |         ],
30 |     };
31 | });
32 | 
33 | server.setRequestHandler(CallToolRequestSchema, async (request) => {
34 |     try {
35 |         const { name } = request.params;
36 |         
37 |         if (name === "getCurrentTime") {
38 |             const time = new Date().toISOString();
39 |             return {
40 |                 content: [{ 
41 |                     type: "text", 
42 |                     text: time
43 |                 }],
44 |                 isError: false,
45 |             };
46 |         }
47 | 
48 |         throw new Error(`Unknown tool: ${name}`);
49 |     } catch (error) {
50 |         const errorMessage = error instanceof Error ? error.message : String(error);
51 |         return {
52 |             content: [{ type: "text", text: `Error: ${errorMessage}` }],
53 |             isError: true,
54 |         };
55 |     }
56 | });
57 | 
58 | // Start server
59 | async function runServer() {
60 |     const transport = new StdioServerTransport();
61 |     await server.connect(transport);
62 |     console.error("Time server running on stdio");
63 | }
64 | 
65 | runServer().catch((error) => {
66 |     console.error("Fatal error running server:", error);
67 |     process.exit(1);
68 | });
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | #!/usr/bin/env node
  2 | 
  3 | import { exec } from 'child_process';
  4 | import { promisify } from 'util';
  5 | 
  6 | const execAsync = promisify(exec);
  7 | 
  8 | interface JsonRpcRequest {
  9 |   jsonrpc: '2.0';
 10 |   id: number | string | null;
 11 |   method: string;
 12 |   params?: any;
 13 | }
 14 | 
 15 | interface JsonRpcResponse {
 16 |   jsonrpc: '2.0';
 17 |   id: number | string | null;
 18 |   result?: any;
 19 |   error?: {
 20 |     code: number;
 21 |     message: string;
 22 |     data?: any;
 23 |   };
 24 | }
 25 | 
 26 | interface TimeResponse {
 27 |   time: string;
 28 |   success: boolean;
 29 | }
 30 | 
 31 | async function handleJsonRpcMessage(message: JsonRpcRequest): Promise<JsonRpcResponse> {
 32 |   if (message.method === 'getCurrentTime') {
 33 |     try {
 34 |       const { stdout } = await execAsync('date "+%Y-%m-%d %H:%M:%S"');
 35 |       return {
 36 |         jsonrpc: '2.0',
 37 |         id: message.id,
 38 |         result: {
 39 |           time: stdout.trim(),
 40 |           success: true
 41 |         }
 42 |       };
 43 |     } catch (error) {
 44 |       return {
 45 |         jsonrpc: '2.0',
 46 |         id: message.id,
 47 |         error: {
 48 |           code: -32603,
 49 |           message: 'Failed to get current time',
 50 |           data: error instanceof Error ? error.message : String(error)
 51 |         }
 52 |       };
 53 |     }
 54 |   }
 55 | 
 56 |   return {
 57 |     jsonrpc: '2.0',
 58 |     id: message.id,
 59 |     error: {
 60 |       code: -32601,
 61 |       message: 'Method not found'
 62 |     }
 63 |   };
 64 | }
 65 | 
 66 | // メインのサーバー処理
 67 | process.stdin.setEncoding('utf-8');
 68 | let buffer = '';
 69 | 
 70 | process.stdin.on('data', async (chunk: string) => {
 71 |   buffer += chunk;
 72 |   
 73 |   const messages = buffer.split('\n');
 74 |   buffer = messages.pop() || '';
 75 | 
 76 |   for (const message of messages) {
 77 |     try {
 78 |       const request: JsonRpcRequest = JSON.parse(message);
 79 |       const response = await handleJsonRpcMessage(request);
 80 |       console.log(JSON.stringify(response));
 81 |     } catch (error) {
 82 |       console.log(JSON.stringify({
 83 |         jsonrpc: '2.0',
 84 |         id: null,
 85 |         error: {
 86 |           code: -32700,
 87 |           message: 'Parse error',
 88 |           data: error instanceof Error ? error.message : String(error)
 89 |         }
 90 |       }));
 91 |     }
 92 |   }
 93 | });
 94 | 
 95 | process.stdin.on('end', () => {
 96 |   process.exit(0);
 97 | });
 98 | 
 99 | // エラーハンドリング
100 | process.on('uncaughtException', (error) => {
101 |   console.error('Uncaught Exception:', error);
102 |   process.exit(1);
103 | });
104 | 
105 | process.on('unhandledRejection', (reason, promise) => {
106 |   console.error('Unhandled Rejection at:', promise, 'reason:', reason);
107 |   process.exit(1);
108 | });
109 | 
```