mcp-macos 2.1.5 → 4.0.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/README.md +7 -60
- package/dist/config/index.d.ts +5 -32
- package/dist/config/index.js +5 -112
- package/dist/config/index.js.map +1 -1
- package/dist/config/schema.d.ts +1 -144
- package/dist/config/schema.js +1 -34
- package/dist/config/schema.js.map +1 -1
- package/dist/index.d.ts +1 -6
- package/dist/index.js +5 -46
- package/dist/index.js.map +1 -1
- package/dist/server/handlers.js +1 -20
- package/dist/server/handlers.js.map +1 -1
- package/dist/server/server.d.ts +1 -18
- package/dist/server/server.js +1 -33
- package/dist/server/server.js.map +1 -1
- package/dist/types/index.d.ts +1 -5
- package/dist/utils/errorHandling.js +1 -1
- package/dist/utils/errorHandling.js.map +1 -1
- package/package.json +3 -9
- package/dist/server/promptAbstractions.d.ts +0 -74
- package/dist/server/promptAbstractions.js +0 -150
- package/dist/server/promptAbstractions.js.map +0 -1
- package/dist/server/prompts.d.ts +0 -8
- package/dist/server/prompts.js +0 -480
- package/dist/server/prompts.js.map +0 -1
- package/dist/server/transports/http/auth.d.ts +0 -34
- package/dist/server/transports/http/auth.js +0 -148
- package/dist/server/transports/http/auth.js.map +0 -1
- package/dist/server/transports/http/health.d.ts +0 -35
- package/dist/server/transports/http/health.js +0 -93
- package/dist/server/transports/http/health.js.map +0 -1
- package/dist/server/transports/http/index.d.ts +0 -43
- package/dist/server/transports/http/index.js +0 -147
- package/dist/server/transports/http/index.js.map +0 -1
- package/dist/server/transports/http/middleware.d.ts +0 -53
- package/dist/server/transports/http/middleware.js +0 -133
- package/dist/server/transports/http/middleware.js.map +0 -1
- package/dist/types/prompts.d.ts +0 -84
- package/dist/types/prompts.js +0 -6
- package/dist/types/prompts.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> Based on [FradSer/mcp-server-apple-events](https://github.com/FradSer/mcp-server-apple-events)
|
|
4
4
|
|
|
5
|
-
MCP server for Reminders, Calendar, Notes, Mail, Messages, and Contacts on macOS.
|
|
6
|
-
|
|
7
|
-
Requires a Mac. For always-on remote access, a Mac Mini or Mac Studio is recommended.
|
|
5
|
+
MCP server for Reminders, Calendar, Notes, Mail, Messages, and Contacts on macOS. Local stdio transport for Claude Desktop, Claude Code, and Cursor.
|
|
8
6
|
|
|
9
7
|
## Quick Start
|
|
10
8
|
|
|
@@ -47,7 +45,7 @@ Checks macOS version, Node.js, EventKit binary, Full Disk Access, and JXA automa
|
|
|
47
45
|
|
|
48
46
|
Both underscore (`reminders_tasks`) and dot (`reminders.tasks`) notation work.
|
|
49
47
|
|
|
50
|
-
##
|
|
48
|
+
## Setup
|
|
51
49
|
|
|
52
50
|
### Prerequisites
|
|
53
51
|
|
|
@@ -93,33 +91,6 @@ Messages and Mail read SQLite databases directly, so your terminal app (Terminal
|
|
|
93
91
|
|
|
94
92
|
Run `macos-mcp --check` to verify. See [Troubleshooting](#troubleshooting) if anything fails.
|
|
95
93
|
|
|
96
|
-
## Remote Setup (Claude iOS/web)
|
|
97
|
-
|
|
98
|
-
Access your Mac's apps from Claude iOS or Claude web via Cloudflare Tunnel.
|
|
99
|
-
|
|
100
|
-
```
|
|
101
|
-
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
102
|
-
│ Claude iOS │──────│ Cloudflare │──────│ Your Mac │
|
|
103
|
-
│ or Web │ HTTPS│ Edge + Access │tunnel│ macos-mcp │
|
|
104
|
-
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### What you'll set up
|
|
108
|
-
|
|
109
|
-
1. **Cloudflare Tunnel** — outbound connection from your Mac to Cloudflare's edge
|
|
110
|
-
2. **Cloudflare Access** — email OTP authentication so only you can connect
|
|
111
|
-
3. **LaunchAgents** — auto-start on boot for both the server and tunnel
|
|
112
|
-
4. **Permissions** — Automation + Full Disk Access granted to the node binary
|
|
113
|
-
|
|
114
|
-
### Prerequisites
|
|
115
|
-
|
|
116
|
-
- Cloudflare account (free tier works)
|
|
117
|
-
- Custom domain in Cloudflare (or `.cfargotunnel.com` subdomain)
|
|
118
|
-
- Always-on Mac (Mac Mini/Studio recommended)
|
|
119
|
-
- macos-mcp built and working locally (`pnpm build && macos-mcp --check`)
|
|
120
|
-
|
|
121
|
-
Full setup guide: **[`docs/CLOUDFLARE_SETUP.md`](docs/CLOUDFLARE_SETUP.md)**
|
|
122
|
-
|
|
123
94
|
## Troubleshooting
|
|
124
95
|
|
|
125
96
|
### Quick-Fix Commands
|
|
@@ -134,13 +105,10 @@ open "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles"
|
|
|
134
105
|
|
|
135
106
|
### Full Disk Access (Messages & Mail)
|
|
136
107
|
|
|
137
|
-
Messages and Mail read SQLite databases (`~/Library/Messages/chat.db` and `~/Library/Mail/V10/MailData/Envelope Index`). These require Full Disk Access.
|
|
138
|
-
|
|
139
|
-
- **stdio**: Grant FDA to your terminal app
|
|
140
|
-
- **HTTP / LaunchAgent**: Grant FDA to the **actual node binary**, not a version manager shim
|
|
108
|
+
Messages and Mail read SQLite databases (`~/Library/Messages/chat.db` and `~/Library/Mail/V10/MailData/Envelope Index`). These require Full Disk Access on your terminal app.
|
|
141
109
|
|
|
142
110
|
```bash
|
|
143
|
-
# Find your real node binary
|
|
111
|
+
# Find your real node binary (version managers use shims)
|
|
144
112
|
node -e "console.log(process.execPath)"
|
|
145
113
|
|
|
146
114
|
# Reveal it in Finder for drag-and-drop into FDA settings
|
|
@@ -164,8 +132,6 @@ System Settings may not show binaries in hidden directories — use `open -R` ab
|
|
|
164
132
|
|
|
165
133
|
On first use, macOS prompts for Automation access. Grant via **System Settings > Privacy & Security > Automation**.
|
|
166
134
|
|
|
167
|
-
> **Headless / LaunchAgent:** Automation dialogs can't appear through a LaunchAgent or SSH. Grant them once from a local Terminal session. Once granted, they persist across reboots. See [`docs/CLOUDFLARE_SETUP.md`](docs/CLOUDFLARE_SETUP.md) Step 10.
|
|
168
|
-
|
|
169
135
|
Verify permissions:
|
|
170
136
|
|
|
171
137
|
```bash
|
|
@@ -182,23 +148,14 @@ Each command should return a value. A hang means the permission dialog is trying
|
|
|
182
148
|
|
|
183
149
|
Gmail stores all messages in `[Gmail]/All Mail` and uses labels for folder membership. The server checks both the direct mailbox and labels join table. If Gmail inbox messages are missing, verify the Mail app has fully synced.
|
|
184
150
|
|
|
185
|
-
### Server Restart (LaunchAgent)
|
|
186
|
-
|
|
187
|
-
Restart **both** services — the server and the tunnel are separate:
|
|
188
|
-
|
|
189
|
-
```bash
|
|
190
|
-
launchctl kickstart -k gui/$(id -u)/com.macos-mcp.server
|
|
191
|
-
launchctl kickstart -k gui/$(id -u)/com.cloudflare.macos-mcp-tunnel
|
|
192
|
-
```
|
|
193
|
-
|
|
194
151
|
## Development
|
|
195
152
|
|
|
196
153
|
```bash
|
|
197
154
|
pnpm install # Install dependencies
|
|
198
155
|
pnpm build # Build TypeScript + Swift binary
|
|
199
|
-
pnpm test # Run full test suite
|
|
156
|
+
pnpm test # Run full test suite
|
|
200
157
|
pnpm lint # Lint and format (Biome + TypeScript)
|
|
201
|
-
pnpm dev # Run from source via tsx
|
|
158
|
+
pnpm dev # Run from source via tsx
|
|
202
159
|
```
|
|
203
160
|
|
|
204
161
|
Production entry point (`bin/run.cjs`) requires `pnpm build`. Use `pnpm dev` for local development.
|
|
@@ -211,19 +168,9 @@ Three bridges to Apple apps:
|
|
|
211
168
|
- **JXA** — Notes, Mail writes, Contacts. Scripts run via `osascript -l JavaScript`.
|
|
212
169
|
- **SQLite** — Messages reads (`~/Library/Messages/chat.db`), Mail reads (`~/Library/Mail/V10/MailData/Envelope Index`). JXA message reading is broken on Sonoma+; JXA mail reading is too slow for real inboxes.
|
|
213
170
|
|
|
214
|
-
### HTTP Transport
|
|
215
|
-
|
|
216
|
-
Set via environment variables or `macos-mcp.config.json`:
|
|
217
|
-
|
|
218
|
-
| Variable | Default | Description |
|
|
219
|
-
|----------|---------|-------------|
|
|
220
|
-
| `MCP_TRANSPORT` | `stdio` | `stdio`, `http`, or `both` |
|
|
221
|
-
| `MCP_HTTP_ENABLED` | `false` | Enable HTTP transport |
|
|
222
|
-
| `MCP_HTTP_PORT` | `3847` | HTTP server port |
|
|
223
|
-
|
|
224
171
|
### Dependencies
|
|
225
172
|
|
|
226
|
-
**Runtime:** `@modelcontextprotocol/sdk`, `
|
|
173
|
+
**Runtime:** `@modelcontextprotocol/sdk`, `zod`
|
|
227
174
|
|
|
228
175
|
**Dev:** `typescript`, `tsx`, `jest`, `@biomejs/biome`
|
|
229
176
|
|
package/dist/config/index.d.ts
CHANGED
|
@@ -1,36 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Configuration
|
|
2
|
+
* @fileoverview Configuration loader for macos-mcp server
|
|
3
3
|
* @module config
|
|
4
|
-
* @description
|
|
5
|
-
*/
|
|
6
|
-
import { type CloudflareAccessConfig, type FullServerConfig, type HttpConfig } from './schema.js';
|
|
7
|
-
/**
|
|
8
|
-
* Loads server configuration from file and environment variables
|
|
9
|
-
*
|
|
10
|
-
* Configuration is loaded in the following priority (highest to lowest):
|
|
11
|
-
* 1. Environment variables (MCP_TRANSPORT, MCP_HTTP_*, CF_ACCESS_*)
|
|
12
|
-
* 2. Configuration file (macos-mcp.config.json in project root)
|
|
13
|
-
* 3. Default values from schema
|
|
14
|
-
*
|
|
15
|
-
* Name and version are always auto-injected from package.json
|
|
16
|
-
*
|
|
17
|
-
* @returns Validated server configuration
|
|
18
|
-
* @throws Error if configuration is invalid
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* // Basic usage
|
|
22
|
-
* const config = loadConfig();
|
|
23
|
-
* console.log(config.transport); // 'stdio' (default)
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* // With environment variables
|
|
27
|
-
* // MCP_TRANSPORT=http
|
|
28
|
-
* // MCP_HTTP_ENABLED=true
|
|
29
|
-
* // MCP_HTTP_PORT=8080
|
|
30
|
-
* const config = loadConfig();
|
|
31
|
-
* console.log(config.transport); // 'http'
|
|
32
|
-
* console.log(config.http?.port); // 8080
|
|
4
|
+
* @description Auto-injects name + version from package.json
|
|
33
5
|
*/
|
|
6
|
+
import { type FullServerConfig } from './schema.js';
|
|
34
7
|
export declare function loadConfig(): FullServerConfig;
|
|
35
|
-
export type {
|
|
36
|
-
export {
|
|
8
|
+
export type { FullServerConfig };
|
|
9
|
+
export { ServerConfigSchema } from './schema.js';
|
package/dist/config/index.js
CHANGED
|
@@ -1,127 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Configuration
|
|
2
|
+
* @fileoverview Configuration loader for macos-mcp server
|
|
3
3
|
* @module config
|
|
4
|
-
* @description
|
|
4
|
+
* @description Auto-injects name + version from package.json
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { readFileSync } from 'node:fs';
|
|
7
7
|
import { join } from 'node:path';
|
|
8
8
|
import { findProjectRoot } from '../utils/projectUtils.js';
|
|
9
|
-
import { ServerConfigSchema
|
|
10
|
-
/** Configuration file name */
|
|
11
|
-
const CONFIG_FILENAME = 'macos-mcp.config.json';
|
|
12
|
-
/**
|
|
13
|
-
* Loads server configuration from file and environment variables
|
|
14
|
-
*
|
|
15
|
-
* Configuration is loaded in the following priority (highest to lowest):
|
|
16
|
-
* 1. Environment variables (MCP_TRANSPORT, MCP_HTTP_*, CF_ACCESS_*)
|
|
17
|
-
* 2. Configuration file (macos-mcp.config.json in project root)
|
|
18
|
-
* 3. Default values from schema
|
|
19
|
-
*
|
|
20
|
-
* Name and version are always auto-injected from package.json
|
|
21
|
-
*
|
|
22
|
-
* @returns Validated server configuration
|
|
23
|
-
* @throws Error if configuration is invalid
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* // Basic usage
|
|
27
|
-
* const config = loadConfig();
|
|
28
|
-
* console.log(config.transport); // 'stdio' (default)
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* // With environment variables
|
|
32
|
-
* // MCP_TRANSPORT=http
|
|
33
|
-
* // MCP_HTTP_ENABLED=true
|
|
34
|
-
* // MCP_HTTP_PORT=8080
|
|
35
|
-
* const config = loadConfig();
|
|
36
|
-
* console.log(config.transport); // 'http'
|
|
37
|
-
* console.log(config.http?.port); // 8080
|
|
38
|
-
*/
|
|
9
|
+
import { ServerConfigSchema } from './schema.js';
|
|
39
10
|
export function loadConfig() {
|
|
40
11
|
const projectRoot = findProjectRoot();
|
|
41
|
-
const configPath = join(projectRoot, CONFIG_FILENAME);
|
|
42
|
-
// Load file configuration if it exists
|
|
43
|
-
let fileConfig = {};
|
|
44
|
-
if (existsSync(configPath)) {
|
|
45
|
-
const fileContent = readFileSync(configPath, 'utf-8');
|
|
46
|
-
fileConfig = JSON.parse(fileContent);
|
|
47
|
-
}
|
|
48
|
-
// Build environment variable configuration
|
|
49
|
-
const envConfig = buildEnvConfig();
|
|
50
|
-
// Deep merge file config with env config (env takes precedence)
|
|
51
|
-
const merged = deepMerge(fileConfig, envConfig);
|
|
52
|
-
// Load package.json for name and version
|
|
53
12
|
const packageJsonPath = join(projectRoot, 'package.json');
|
|
54
13
|
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
55
|
-
// Parse and validate configuration
|
|
56
14
|
return ServerConfigSchema.parse({
|
|
57
15
|
name: packageJson.name,
|
|
58
16
|
version: packageJson.version,
|
|
59
|
-
...merged,
|
|
60
17
|
});
|
|
61
18
|
}
|
|
62
|
-
|
|
63
|
-
* Builds configuration object from environment variables
|
|
64
|
-
* @returns Partial configuration from environment variables
|
|
65
|
-
*/
|
|
66
|
-
function buildEnvConfig() {
|
|
67
|
-
const config = {};
|
|
68
|
-
// Transport mode
|
|
69
|
-
if (process.env.MCP_TRANSPORT) {
|
|
70
|
-
config.transport = process.env.MCP_TRANSPORT;
|
|
71
|
-
}
|
|
72
|
-
// HTTP configuration (only if explicitly enabled)
|
|
73
|
-
if (process.env.MCP_HTTP_ENABLED === 'true') {
|
|
74
|
-
const httpConfig = {
|
|
75
|
-
enabled: true,
|
|
76
|
-
};
|
|
77
|
-
if (process.env.MCP_HTTP_HOST) {
|
|
78
|
-
httpConfig.host = process.env.MCP_HTTP_HOST;
|
|
79
|
-
}
|
|
80
|
-
if (process.env.MCP_HTTP_PORT) {
|
|
81
|
-
const port = Number.parseInt(process.env.MCP_HTTP_PORT, 10);
|
|
82
|
-
if (!Number.isNaN(port)) {
|
|
83
|
-
httpConfig.port = port;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
// Cloudflare Access configuration
|
|
87
|
-
if (process.env.CF_ACCESS_TEAM_DOMAIN && process.env.CF_ACCESS_POLICY_AUD) {
|
|
88
|
-
const cfConfig = {
|
|
89
|
-
teamDomain: process.env.CF_ACCESS_TEAM_DOMAIN,
|
|
90
|
-
policyAUD: process.env.CF_ACCESS_POLICY_AUD,
|
|
91
|
-
};
|
|
92
|
-
if (process.env.CF_ACCESS_ALLOWED_EMAILS) {
|
|
93
|
-
cfConfig.allowedEmails = process.env.CF_ACCESS_ALLOWED_EMAILS.split(',').map((e) => e.trim());
|
|
94
|
-
}
|
|
95
|
-
httpConfig.cloudflareAccess = cfConfig;
|
|
96
|
-
}
|
|
97
|
-
config.http = httpConfig;
|
|
98
|
-
}
|
|
99
|
-
return config;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Deep merges two objects, with source taking precedence
|
|
103
|
-
* @param target - Base object
|
|
104
|
-
* @param source - Object to merge in (takes precedence)
|
|
105
|
-
* @returns Merged object
|
|
106
|
-
*/
|
|
107
|
-
function deepMerge(target, source) {
|
|
108
|
-
const result = { ...target };
|
|
109
|
-
for (const key of Object.keys(source)) {
|
|
110
|
-
const sourceValue = source[key];
|
|
111
|
-
const targetValue = result[key];
|
|
112
|
-
if (sourceValue !== null &&
|
|
113
|
-
typeof sourceValue === 'object' &&
|
|
114
|
-
!Array.isArray(sourceValue) &&
|
|
115
|
-
targetValue !== null &&
|
|
116
|
-
typeof targetValue === 'object' &&
|
|
117
|
-
!Array.isArray(targetValue)) {
|
|
118
|
-
result[key] = deepMerge(targetValue, sourceValue);
|
|
119
|
-
}
|
|
120
|
-
else if (sourceValue !== undefined) {
|
|
121
|
-
result[key] = sourceValue;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
return result;
|
|
125
|
-
}
|
|
126
|
-
export { CloudflareAccessConfigSchema, HttpConfigSchema, ServerConfigSchema, } from './schema.js';
|
|
19
|
+
export { ServerConfigSchema } from './schema.js';
|
|
127
20
|
//# sourceMappingURL=index.js.map
|
package/dist/config/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAyB,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAExE,MAAM,UAAU,UAAU;IACxB,MAAM,WAAW,GAAG,eAAe,EAAE,CAAC;IACtC,MAAM,eAAe,GAAG,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;IAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAGpE,CAAC;IAEF,OAAO,kBAAkB,CAAC,KAAK,CAAC;QAC9B,IAAI,EAAE,WAAW,CAAC,IAAI;QACtB,OAAO,EAAE,WAAW,CAAC,OAAO;KAC7B,CAAC,CAAC;AACL,CAAC;AAGD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -1,161 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Configuration schema
|
|
2
|
+
* @fileoverview Configuration schema for macos-mcp server
|
|
3
3
|
* @module config/schema
|
|
4
|
-
* @description Zod schemas for transport mode, HTTP settings, and Cloudflare Access configuration
|
|
5
4
|
*/
|
|
6
5
|
import { z } from 'zod/v3';
|
|
7
|
-
/**
|
|
8
|
-
* Cloudflare Access configuration schema for JWT verification
|
|
9
|
-
* Required when using Cloudflare Tunnel for secure remote access
|
|
10
|
-
*/
|
|
11
|
-
export declare const CloudflareAccessConfigSchema: z.ZodObject<{
|
|
12
|
-
/** Cloudflare Access team domain (e.g., "myteam.cloudflareaccess.com") */
|
|
13
|
-
teamDomain: z.ZodString;
|
|
14
|
-
/** Application Audience (AUD) tag from Cloudflare Access policy */
|
|
15
|
-
policyAUD: z.ZodString;
|
|
16
|
-
/** Optional list of allowed email addresses for additional verification */
|
|
17
|
-
allowedEmails: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
18
|
-
}, "strip", z.ZodTypeAny, {
|
|
19
|
-
teamDomain: string;
|
|
20
|
-
policyAUD: string;
|
|
21
|
-
allowedEmails?: string[] | undefined;
|
|
22
|
-
}, {
|
|
23
|
-
teamDomain: string;
|
|
24
|
-
policyAUD: string;
|
|
25
|
-
allowedEmails?: string[] | undefined;
|
|
26
|
-
}>;
|
|
27
|
-
/**
|
|
28
|
-
* HTTP transport configuration schema
|
|
29
|
-
*/
|
|
30
|
-
export declare const HttpConfigSchema: z.ZodObject<{
|
|
31
|
-
/** Whether HTTP transport is enabled */
|
|
32
|
-
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
33
|
-
/** Host to bind the HTTP server to */
|
|
34
|
-
host: z.ZodDefault<z.ZodString>;
|
|
35
|
-
/** Port for the HTTP server (1-65535) */
|
|
36
|
-
port: z.ZodDefault<z.ZodNumber>;
|
|
37
|
-
/** Cloudflare Access configuration for JWT verification */
|
|
38
|
-
cloudflareAccess: z.ZodOptional<z.ZodObject<{
|
|
39
|
-
/** Cloudflare Access team domain (e.g., "myteam.cloudflareaccess.com") */
|
|
40
|
-
teamDomain: z.ZodString;
|
|
41
|
-
/** Application Audience (AUD) tag from Cloudflare Access policy */
|
|
42
|
-
policyAUD: z.ZodString;
|
|
43
|
-
/** Optional list of allowed email addresses for additional verification */
|
|
44
|
-
allowedEmails: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
45
|
-
}, "strip", z.ZodTypeAny, {
|
|
46
|
-
teamDomain: string;
|
|
47
|
-
policyAUD: string;
|
|
48
|
-
allowedEmails?: string[] | undefined;
|
|
49
|
-
}, {
|
|
50
|
-
teamDomain: string;
|
|
51
|
-
policyAUD: string;
|
|
52
|
-
allowedEmails?: string[] | undefined;
|
|
53
|
-
}>>;
|
|
54
|
-
}, "strip", z.ZodTypeAny, {
|
|
55
|
-
enabled: boolean;
|
|
56
|
-
host: string;
|
|
57
|
-
port: number;
|
|
58
|
-
cloudflareAccess?: {
|
|
59
|
-
teamDomain: string;
|
|
60
|
-
policyAUD: string;
|
|
61
|
-
allowedEmails?: string[] | undefined;
|
|
62
|
-
} | undefined;
|
|
63
|
-
}, {
|
|
64
|
-
enabled?: boolean | undefined;
|
|
65
|
-
host?: string | undefined;
|
|
66
|
-
port?: number | undefined;
|
|
67
|
-
cloudflareAccess?: {
|
|
68
|
-
teamDomain: string;
|
|
69
|
-
policyAUD: string;
|
|
70
|
-
allowedEmails?: string[] | undefined;
|
|
71
|
-
} | undefined;
|
|
72
|
-
}>;
|
|
73
|
-
/**
|
|
74
|
-
* Full server configuration schema
|
|
75
|
-
*/
|
|
76
6
|
export declare const ServerConfigSchema: z.ZodObject<{
|
|
77
7
|
/** Server name (auto-populated from package.json) */
|
|
78
8
|
name: z.ZodString;
|
|
79
9
|
/** Server version (auto-populated from package.json) */
|
|
80
10
|
version: z.ZodString;
|
|
81
|
-
/** Transport mode: stdio (default), http, or both */
|
|
82
|
-
transport: z.ZodDefault<z.ZodEnum<["stdio", "http", "both"]>>;
|
|
83
|
-
/** HTTP transport configuration (required when transport includes http) */
|
|
84
|
-
http: z.ZodOptional<z.ZodObject<{
|
|
85
|
-
/** Whether HTTP transport is enabled */
|
|
86
|
-
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
87
|
-
/** Host to bind the HTTP server to */
|
|
88
|
-
host: z.ZodDefault<z.ZodString>;
|
|
89
|
-
/** Port for the HTTP server (1-65535) */
|
|
90
|
-
port: z.ZodDefault<z.ZodNumber>;
|
|
91
|
-
/** Cloudflare Access configuration for JWT verification */
|
|
92
|
-
cloudflareAccess: z.ZodOptional<z.ZodObject<{
|
|
93
|
-
/** Cloudflare Access team domain (e.g., "myteam.cloudflareaccess.com") */
|
|
94
|
-
teamDomain: z.ZodString;
|
|
95
|
-
/** Application Audience (AUD) tag from Cloudflare Access policy */
|
|
96
|
-
policyAUD: z.ZodString;
|
|
97
|
-
/** Optional list of allowed email addresses for additional verification */
|
|
98
|
-
allowedEmails: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
99
|
-
}, "strip", z.ZodTypeAny, {
|
|
100
|
-
teamDomain: string;
|
|
101
|
-
policyAUD: string;
|
|
102
|
-
allowedEmails?: string[] | undefined;
|
|
103
|
-
}, {
|
|
104
|
-
teamDomain: string;
|
|
105
|
-
policyAUD: string;
|
|
106
|
-
allowedEmails?: string[] | undefined;
|
|
107
|
-
}>>;
|
|
108
|
-
}, "strip", z.ZodTypeAny, {
|
|
109
|
-
enabled: boolean;
|
|
110
|
-
host: string;
|
|
111
|
-
port: number;
|
|
112
|
-
cloudflareAccess?: {
|
|
113
|
-
teamDomain: string;
|
|
114
|
-
policyAUD: string;
|
|
115
|
-
allowedEmails?: string[] | undefined;
|
|
116
|
-
} | undefined;
|
|
117
|
-
}, {
|
|
118
|
-
enabled?: boolean | undefined;
|
|
119
|
-
host?: string | undefined;
|
|
120
|
-
port?: number | undefined;
|
|
121
|
-
cloudflareAccess?: {
|
|
122
|
-
teamDomain: string;
|
|
123
|
-
policyAUD: string;
|
|
124
|
-
allowedEmails?: string[] | undefined;
|
|
125
|
-
} | undefined;
|
|
126
|
-
}>>;
|
|
127
11
|
}, "strip", z.ZodTypeAny, {
|
|
128
12
|
name: string;
|
|
129
13
|
version: string;
|
|
130
|
-
transport: "stdio" | "http" | "both";
|
|
131
|
-
http?: {
|
|
132
|
-
enabled: boolean;
|
|
133
|
-
host: string;
|
|
134
|
-
port: number;
|
|
135
|
-
cloudflareAccess?: {
|
|
136
|
-
teamDomain: string;
|
|
137
|
-
policyAUD: string;
|
|
138
|
-
allowedEmails?: string[] | undefined;
|
|
139
|
-
} | undefined;
|
|
140
|
-
} | undefined;
|
|
141
14
|
}, {
|
|
142
15
|
name: string;
|
|
143
16
|
version: string;
|
|
144
|
-
http?: {
|
|
145
|
-
enabled?: boolean | undefined;
|
|
146
|
-
host?: string | undefined;
|
|
147
|
-
port?: number | undefined;
|
|
148
|
-
cloudflareAccess?: {
|
|
149
|
-
teamDomain: string;
|
|
150
|
-
policyAUD: string;
|
|
151
|
-
allowedEmails?: string[] | undefined;
|
|
152
|
-
} | undefined;
|
|
153
|
-
} | undefined;
|
|
154
|
-
transport?: "stdio" | "http" | "both" | undefined;
|
|
155
17
|
}>;
|
|
156
|
-
/** Cloudflare Access configuration type */
|
|
157
|
-
export type CloudflareAccessConfig = z.infer<typeof CloudflareAccessConfigSchema>;
|
|
158
|
-
/** HTTP transport configuration type */
|
|
159
|
-
export type HttpConfig = z.infer<typeof HttpConfigSchema>;
|
|
160
|
-
/** Full server configuration type */
|
|
161
18
|
export type FullServerConfig = z.infer<typeof ServerConfigSchema>;
|
package/dist/config/schema.js
CHANGED
|
@@ -1,45 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Configuration schema
|
|
2
|
+
* @fileoverview Configuration schema for macos-mcp server
|
|
3
3
|
* @module config/schema
|
|
4
|
-
* @description Zod schemas for transport mode, HTTP settings, and Cloudflare Access configuration
|
|
5
4
|
*/
|
|
6
5
|
import { z } from 'zod/v3';
|
|
7
|
-
/**
|
|
8
|
-
* Cloudflare Access configuration schema for JWT verification
|
|
9
|
-
* Required when using Cloudflare Tunnel for secure remote access
|
|
10
|
-
*/
|
|
11
|
-
export const CloudflareAccessConfigSchema = z.object({
|
|
12
|
-
/** Cloudflare Access team domain (e.g., "myteam.cloudflareaccess.com") */
|
|
13
|
-
teamDomain: z.string().min(1, 'Team domain is required'),
|
|
14
|
-
/** Application Audience (AUD) tag from Cloudflare Access policy */
|
|
15
|
-
policyAUD: z.string().min(1, 'Policy AUD is required'),
|
|
16
|
-
/** Optional list of allowed email addresses for additional verification */
|
|
17
|
-
allowedEmails: z.array(z.string().email('Invalid email format')).optional(),
|
|
18
|
-
});
|
|
19
|
-
/**
|
|
20
|
-
* HTTP transport configuration schema
|
|
21
|
-
*/
|
|
22
|
-
export const HttpConfigSchema = z.object({
|
|
23
|
-
/** Whether HTTP transport is enabled */
|
|
24
|
-
enabled: z.boolean().default(false),
|
|
25
|
-
/** Host to bind the HTTP server to */
|
|
26
|
-
host: z.string().default('127.0.0.1'),
|
|
27
|
-
/** Port for the HTTP server (1-65535) */
|
|
28
|
-
port: z.number().int().min(1).max(65535).default(3847),
|
|
29
|
-
/** Cloudflare Access configuration for JWT verification */
|
|
30
|
-
cloudflareAccess: CloudflareAccessConfigSchema.optional(),
|
|
31
|
-
});
|
|
32
|
-
/**
|
|
33
|
-
* Full server configuration schema
|
|
34
|
-
*/
|
|
35
6
|
export const ServerConfigSchema = z.object({
|
|
36
7
|
/** Server name (auto-populated from package.json) */
|
|
37
8
|
name: z.string(),
|
|
38
9
|
/** Server version (auto-populated from package.json) */
|
|
39
10
|
version: z.string(),
|
|
40
|
-
/** Transport mode: stdio (default), http, or both */
|
|
41
|
-
transport: z.enum(['stdio', 'http', 'both']).default('stdio'),
|
|
42
|
-
/** HTTP transport configuration (required when transport includes http) */
|
|
43
|
-
http: HttpConfigSchema.optional(),
|
|
44
11
|
});
|
|
45
12
|
//# sourceMappingURL=schema.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,qDAAqD;IACrD,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,wDAAwD;IACxD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;CACpB,CAAC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* index.ts
|
|
4
|
-
* Entry point for the macOS MCP server
|
|
5
|
-
*
|
|
6
|
-
* Supports multiple transport modes:
|
|
7
|
-
* - stdio: Standard input/output (default, for Claude Desktop)
|
|
8
|
-
* - http: HTTP/SSE transport (for remote access via Cloudflare Tunnel)
|
|
9
|
-
* - both: Run both transports simultaneously
|
|
4
|
+
* Entry point for the macOS MCP server (stdio transport)
|
|
10
5
|
*/
|
|
11
6
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,61 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* index.ts
|
|
4
|
-
* Entry point for the macOS MCP server
|
|
5
|
-
*
|
|
6
|
-
* Supports multiple transport modes:
|
|
7
|
-
* - stdio: Standard input/output (default, for Claude Desktop)
|
|
8
|
-
* - http: HTTP/SSE transport (for remote access via Cloudflare Tunnel)
|
|
9
|
-
* - both: Run both transports simultaneously
|
|
4
|
+
* Entry point for the macOS MCP server (stdio transport)
|
|
10
5
|
*/
|
|
11
6
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
12
7
|
import { loadConfig } from './config/index.js';
|
|
13
8
|
import { createServer } from './server/server.js';
|
|
14
9
|
import { contactResolver } from './utils/contactResolver.js';
|
|
15
|
-
/** Active HTTP transport instance for cleanup */
|
|
16
|
-
let httpTransport = null;
|
|
17
|
-
/**
|
|
18
|
-
* Graceful shutdown handler
|
|
19
|
-
* Stops HTTP server if running and exits cleanly
|
|
20
|
-
*/
|
|
21
|
-
async function shutdown() {
|
|
22
|
-
process.stderr.write(`${JSON.stringify({ timestamp: new Date().toISOString(), event: 'shutdown_initiated' })}\n`);
|
|
23
|
-
if (httpTransport) {
|
|
24
|
-
await httpTransport.stop();
|
|
25
|
-
}
|
|
26
|
-
process.exit(0);
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Main entry point
|
|
30
|
-
* Loads configuration and starts appropriate transport(s)
|
|
31
|
-
*/
|
|
32
10
|
async function main() {
|
|
33
11
|
const config = loadConfig();
|
|
34
12
|
const server = createServer(config);
|
|
35
|
-
// Warm contact cache before connecting
|
|
36
|
-
// Fire-and-forget: cache builds concurrently
|
|
37
|
-
// before the first enrichment request arrives.
|
|
13
|
+
// Warm contact cache before connecting transport.
|
|
14
|
+
// Fire-and-forget: cache builds concurrently with stdio handshake.
|
|
38
15
|
void contactResolver.warmCache();
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
process.on('SIGTERM', () => void shutdown());
|
|
42
|
-
// Start stdio transport if configured
|
|
43
|
-
if (config.transport === 'stdio' || config.transport === 'both') {
|
|
44
|
-
const stdioTransport = new StdioServerTransport();
|
|
45
|
-
await server.connect(stdioTransport);
|
|
46
|
-
}
|
|
47
|
-
// Start HTTP transport if configured
|
|
48
|
-
if (config.transport === 'http' || config.transport === 'both') {
|
|
49
|
-
if (!config.http?.enabled) {
|
|
50
|
-
throw new Error('HTTP transport requested but http.enabled is false');
|
|
51
|
-
}
|
|
52
|
-
// Dynamic import to avoid loading express when not needed
|
|
53
|
-
const { createHttpTransport } = await import('./server/transports/http/index.js');
|
|
54
|
-
httpTransport = createHttpTransport(server, config, config.http);
|
|
55
|
-
await httpTransport.start();
|
|
56
|
-
}
|
|
16
|
+
const transport = new StdioServerTransport();
|
|
17
|
+
await server.connect(transport);
|
|
57
18
|
}
|
|
58
|
-
// Handle --check flag for preflight validation
|
|
59
19
|
if (process.argv.includes('--check')) {
|
|
60
20
|
import('./utils/preflight.js').then(async ({ runPreflight, formatResults }) => {
|
|
61
21
|
const results = await runPreflight();
|
|
@@ -65,7 +25,6 @@ if (process.argv.includes('--check')) {
|
|
|
65
25
|
});
|
|
66
26
|
}
|
|
67
27
|
else {
|
|
68
|
-
// Start the application
|
|
69
28
|
main().catch((error) => {
|
|
70
29
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
71
30
|
process.stderr.write(`${JSON.stringify({ timestamp: new Date().toISOString(), error: 'fatal', message: errorMessage })}\n`);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAyB,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAqB,UAAU,EAAE,CAAC;IAC9C,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAEpC,kDAAkD;IAClD,mEAAmE;IACnE,KAAK,eAAe,CAAC,SAAS,EAAE,CAAC;IAEjC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;IACrC,MAAM,CAAC,sBAAsB,CAAC,CAAC,IAAI,CACjC,KAAK,EAAE,EAAE,YAAY,EAAE,aAAa,EAAE,EAAE,EAAE;QACxC,MAAM,OAAO,GAAG,MAAM,YAAY,EAAE,CAAC;QACrC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACpD,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;QAC5D,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC,CACF,CAAC;AACJ,CAAC;KAAM,CAAC;IACN,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;QAC9B,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,IAAI,CACtG,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/server/handlers.js
CHANGED
|
@@ -2,15 +2,13 @@
|
|
|
2
2
|
* server/handlers.ts
|
|
3
3
|
* Request handlers for the MCP server
|
|
4
4
|
*/
|
|
5
|
-
import { CallToolRequestSchema,
|
|
5
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
6
6
|
import { handleToolCall, TOOLS } from '../tools/index.js';
|
|
7
|
-
import { buildPromptResponse, getPromptDefinition, PROMPT_LIST, } from './prompts.js';
|
|
8
7
|
/**
|
|
9
8
|
* Registers all request handlers for the MCP server
|
|
10
9
|
* @param server - The MCP server instance
|
|
11
10
|
*/
|
|
12
11
|
export function registerHandlers(server) {
|
|
13
|
-
// Handler for listing available tools
|
|
14
12
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
15
13
|
process.stderr.write(`${JSON.stringify({
|
|
16
14
|
timestamp: new Date().toISOString(),
|
|
@@ -21,23 +19,6 @@ export function registerHandlers(server) {
|
|
|
21
19
|
})}\n`);
|
|
22
20
|
return { tools: TOOLS };
|
|
23
21
|
});
|
|
24
|
-
// Handler for calling a tool
|
|
25
22
|
server.setRequestHandler(CallToolRequestSchema, async (request) => handleToolCall(request.params.name, request.params.arguments ?? {}));
|
|
26
|
-
// Handler for listing available prompts
|
|
27
|
-
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
28
|
-
prompts: PROMPT_LIST,
|
|
29
|
-
}));
|
|
30
|
-
// Handler for getting a specific prompt
|
|
31
|
-
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
32
|
-
const { name, arguments: args } = request.params;
|
|
33
|
-
if (typeof name !== 'string') {
|
|
34
|
-
throw new Error('Prompt name must be a string.');
|
|
35
|
-
}
|
|
36
|
-
const promptDefinition = getPromptDefinition(name);
|
|
37
|
-
if (!promptDefinition) {
|
|
38
|
-
throw new Error(`Unknown prompt: ${name}`);
|
|
39
|
-
}
|
|
40
|
-
return buildPromptResponse(promptDefinition, args);
|
|
41
|
-
});
|
|
42
23
|
}
|
|
43
24
|
//# sourceMappingURL=handlers.js.map
|