synthux-mcp 2.1.0 → 2.1.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
@@ -8,7 +8,13 @@ Connect desktop AI coding agents (**Google Antigravity**, **Claude Desktop / Cla
8
8
  - **Live Chrome Extension Bridge:** Connects to the synthux MV3 Chrome Extension over local WebSocket (`ws://127.0.0.1:17483/ws`).
9
9
  - **Two AI Evaluation Modes:**
10
10
  1. **Agent-Brain Mode (Passthrough):** Your connected MCP Agent (e.g., Antigravity Gemini 4 Argon, Claude, Codex) uses `synthux_prepare_external_audit` to scan the live DOM, run `axe-core` WCAG 2.2 AA checks, and get Sectoral Rule Pack prompts — evaluates them with its own intelligence — and calls `synthux_submit_external_audit` to render live page heatmaps, annotated screenshots, and Enhanced v2.0 Markdown/JSON reports.
11
- 2. **Extension AI Engine Mode:** Trigger `synthux_run_ux_audit` to run the audit using the extension's configured **Local LLM (Ollama: `gemma4`, `qwen3.8`, `deepseek-r1`)** or **Cloud BYOK API (`Gemini`, `OpenAI`, `Claude`)**.
11
+ 2. **Extension AI Engine Mode:** Trigger `synthux_run_ux_audit` to run the audit using the extension's configured **Local LLM (Ollama: `gemma4`, `qwen3.8`, `deepseek-r1`)**, ** Apple Intelligence (macOS 26/27 `FoundationModels`)**, **Chrome Built-in AI (`window.LanguageModel`)**, or **Cloud BYOK API (`Gemini`, `OpenAI`, `Claude`)**.
12
+ - **Native macOS 26/27 Apple Intelligence Bridge (`mcp/apple-fm-bridge.swift`):**
13
+ - Automatically compiles and caches a native Swift bridge to Apple's `FoundationModels.framework` (`SystemLanguageModel.default` & `PrivateCloudComputeLanguageModel` on macOS 27+).
14
+ - Exposes local HTTP endpoints on `http://127.0.0.1:17483`:
15
+ - `GET /apple-fm/status` & `POST /apple-fm/generate` (Native synthux Apple Intelligence provider)
16
+ - `GET /api/tags`, `POST /api/show`, `POST /api/generate` (Ollama-compatible endpoints)
17
+ - `GET /v1/models`, `POST /v1/chat/completions` (OpenAI-compatible endpoints)
12
18
  - **Privacy & Security Hardened:**
13
19
  - Strictly bound to `127.0.0.1` loopback (never exposed to LAN/`0.0.0.0`).
14
20
  - Strict Origin Verification blocks all `http://` and `https://` web page origins from accessing `/ws` or `/mcp` (CSRF / WebSocket hijacking protection).
@@ -0,0 +1,277 @@
1
+ #!/usr/bin/env swift
2
+ /**
3
+ * synthux — Apple Foundation Models (macOS 26 / macOS 27+) Local Bridge
4
+ *
5
+ * Interfaces directly with Apple's native `FoundationModels` framework:
6
+ * - `SystemLanguageModel.default` (On-Device Apple Silicon Neural Engine model)
7
+ * - `SystemLanguageModel(useCase: .contentTagging)` (Fast structured tagging)
8
+ * - `PrivateCloudComputeLanguageModel()` (macOS 27+ Apple Private Cloud Compute)
9
+ *
10
+ * Commands:
11
+ * 1. `synthux-apple-fm status`
12
+ * Outputs JSON with availability, macOS version, context window size, and models.
13
+ * 2. `synthux-apple-fm generate`
14
+ * Reads JSON payload from stdin:
15
+ * { "model": "apple-on-device", "prompt": "...", "system": "...", "temperature": 0.3, "maxTokens": 2048 }
16
+ * Outputs JSON response to stdout:
17
+ * { "success": true, "model": "apple-on-device", "response": "{...}", "inputTokens": 0, "outputTokens": 0 }
18
+ */
19
+
20
+ import Foundation
21
+ #if canImport(FoundationModels)
22
+ import FoundationModels
23
+ #endif
24
+
25
+ struct BridgeRequest: Decodable {
26
+ let model: String?
27
+ let prompt: String
28
+ let system: String?
29
+ let temperature: Double?
30
+ let maxTokens: Int?
31
+ }
32
+
33
+ func jsonOut(_ dict: [String: Any]) {
34
+ if let data = try? JSONSerialization.data(withJSONObject: dict, options: []),
35
+ let str = String(data: data, encoding: .utf8) {
36
+ FileHandle.standardOutput.write((str + "\n").data(using: .utf8)!)
37
+ }
38
+ }
39
+
40
+ func cleanJsonResponse(_ text: String) -> String {
41
+ var trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
42
+ if trimmed.hasPrefix("```json") {
43
+ trimmed = String(trimmed.dropFirst(7))
44
+ } else if trimmed.hasPrefix("```") {
45
+ trimmed = String(trimmed.dropFirst(3))
46
+ }
47
+ if trimmed.hasSuffix("```") {
48
+ trimmed = String(trimmed.dropLast(3))
49
+ }
50
+ return trimmed.trimmingCharacters(in: .whitespacesAndNewlines)
51
+ }
52
+
53
+ func osVersionString() -> String {
54
+ let v = ProcessInfo.processInfo.operatingSystemVersion
55
+ return "macOS \(v.majorVersion).\(v.minorVersion).\(v.patchVersion)"
56
+ }
57
+
58
+ struct SynthuxAppleFMBridge {
59
+ static func run() async {
60
+ let args = CommandLine.arguments
61
+ let command = args.count > 1 ? args[1] : "status"
62
+
63
+ #if canImport(FoundationModels)
64
+ if #available(macOS 26.0, *) {
65
+ if command == "status" {
66
+ await handleStatus()
67
+ return
68
+ } else if command == "generate" {
69
+ await handleGenerate()
70
+ return
71
+ }
72
+ }
73
+ #endif
74
+
75
+ jsonOut([
76
+ "available": false,
77
+ "status": "unsupported_os",
78
+ "reason": "Apple FoundationModels framework requires macOS 26.0 or newer (macOS 27 recommended).",
79
+ "osVersion": osVersionString(),
80
+ "models": []
81
+ ])
82
+ }
83
+
84
+ #if canImport(FoundationModels)
85
+ @available(macOS 26.0, *)
86
+ static func handleStatus() async {
87
+ let defaultModel = SystemLanguageModel.default
88
+ var statusStr = "connected"
89
+ var reasonStr: String? = nil
90
+ var isAvailable = false
91
+
92
+ switch defaultModel.availability {
93
+ case .available:
94
+ isAvailable = true
95
+ statusStr = "connected"
96
+ case .unavailable(let reason):
97
+ isAvailable = false
98
+ switch reason {
99
+ case .deviceNotEligible:
100
+ statusStr = "device_not_eligible"
101
+ reasonStr = "This Mac is not eligible for Apple Intelligence (Apple Silicon M1 or newer required)."
102
+ case .appleIntelligenceNotEnabled:
103
+ statusStr = "apple_intelligence_disabled"
104
+ reasonStr = "Apple Intelligence is disabled. Enable it in System Settings → Apple Intelligence & Siri."
105
+ case .modelNotReady:
106
+ statusStr = "model_not_ready"
107
+ reasonStr = "Apple Foundation Model is still preparing or downloading in macOS System Settings."
108
+ @unknown default:
109
+ statusStr = "unavailable"
110
+ reasonStr = "Apple Foundation Model is currently unavailable."
111
+ }
112
+ }
113
+
114
+ var contextSize = 4096
115
+ var variantName = "Apple On-Device Foundation Model"
116
+ var pccAvailable = false
117
+
118
+ if #available(macOS 27.0, *) {
119
+ if defaultModel.contextSize > 0 {
120
+ contextSize = defaultModel.contextSize
121
+ }
122
+ variantName = defaultModel.variant.displayName
123
+ let pccModel = PrivateCloudComputeLanguageModel()
124
+ pccAvailable = pccModel.isAvailable
125
+ }
126
+
127
+ var models: [[String: Any]] = [
128
+ [
129
+ "id": "apple-on-device",
130
+ "name": "Apple Foundation Model (On-Device ANE)",
131
+ "size": 0
132
+ ],
133
+ [
134
+ "id": "apple-content-tagging",
135
+ "name": "Apple Foundation Model (Fast Tagging)",
136
+ "size": 0
137
+ ]
138
+ ]
139
+
140
+ if #available(macOS 27.0, *) {
141
+ models.insert([
142
+ "id": "apple-pcc",
143
+ "name": "Apple Private Cloud Compute (macOS 27)",
144
+ "size": 0
145
+ ], at: 1)
146
+ }
147
+
148
+ jsonOut([
149
+ "available": isAvailable,
150
+ "status": statusStr,
151
+ "reason": reasonStr as Any,
152
+ "osVersion": osVersionString(),
153
+ "contextSize": contextSize,
154
+ "variant": variantName,
155
+ "pccAvailable": pccAvailable,
156
+ "models": models
157
+ ])
158
+ }
159
+
160
+ @available(macOS 26.0, *)
161
+ static func handleGenerate() async {
162
+ let inputData = FileHandle.standardInput.readDataToEndOfFile()
163
+ guard !inputData.isEmpty,
164
+ let req = try? JSONDecoder().decode(BridgeRequest.self, from: inputData) else {
165
+ jsonOut([
166
+ "success": false,
167
+ "error": "Invalid JSON input payload for Apple Foundation Model bridge."
168
+ ])
169
+ return
170
+ }
171
+
172
+ let startTime = Date()
173
+ let requestedModel = req.model ?? "apple-on-device"
174
+ let temperature = req.temperature ?? 0.3
175
+ let maxTokens = req.maxTokens ?? 2048
176
+ let options = GenerationOptions(
177
+ temperature: temperature,
178
+ maximumResponseTokens: maxTokens
179
+ )
180
+
181
+ // Trim prompt safely if it exceeds on-device context budget (~12,000 chars ≈ 3,000 tokens)
182
+ let maxPromptChars = requestedModel == "apple-pcc" ? 28000 : 12000
183
+ let safePrompt: String
184
+ if req.prompt.count > maxPromptChars {
185
+ safePrompt = String(req.prompt.prefix(maxPromptChars)) + "\n\n[DOM truncated to fit on-device context window. Respond ONLY with valid JSON.]"
186
+ } else {
187
+ safePrompt = req.prompt
188
+ }
189
+
190
+ do {
191
+ var responseText = ""
192
+ var inputTokens = max(1, safePrompt.count / 4)
193
+ var outputTokens = 0
194
+
195
+ if #available(macOS 27.0, *), requestedModel == "apple-pcc" {
196
+ let pccModel = PrivateCloudComputeLanguageModel()
197
+ if pccModel.isAvailable {
198
+ let session = LanguageModelSession(
199
+ model: pccModel,
200
+ instructions: req.system
201
+ )
202
+ let res = try await session.respond(to: safePrompt, options: options)
203
+ responseText = cleanJsonResponse(res.content)
204
+ inputTokens = res.usage.input.totalTokenCount
205
+ outputTokens = res.usage.output.totalTokenCount
206
+ } else {
207
+ // Fallback to on-device model if PCC is not ready
208
+ let onDevice = SystemLanguageModel(
209
+ useCase: .general,
210
+ guardrails: .permissiveContentTransformations
211
+ )
212
+ let session = LanguageModelSession(
213
+ model: onDevice,
214
+ instructions: req.system
215
+ )
216
+ let res = try await session.respond(to: safePrompt, options: options)
217
+ responseText = cleanJsonResponse(res.content)
218
+ inputTokens = res.usage.input.totalTokenCount
219
+ outputTokens = res.usage.output.totalTokenCount
220
+ }
221
+ } else {
222
+ let useCase: SystemLanguageModel.UseCase = (requestedModel == "apple-content-tagging")
223
+ ? .contentTagging
224
+ : .general
225
+ let onDevice = SystemLanguageModel(
226
+ useCase: useCase,
227
+ guardrails: .permissiveContentTransformations
228
+ )
229
+
230
+ guard onDevice.isAvailable else {
231
+ jsonOut([
232
+ "success": false,
233
+ "error": "Apple Foundation Model is not available on this Mac (\(onDevice.availability)). Make sure Apple Intelligence is enabled in System Settings."
234
+ ])
235
+ return
236
+ }
237
+
238
+ let session = LanguageModelSession(
239
+ model: onDevice,
240
+ instructions: req.system
241
+ )
242
+ let res = try await session.respond(to: safePrompt, options: options)
243
+ responseText = cleanJsonResponse(res.content)
244
+ if #available(macOS 27.0, *) {
245
+ inputTokens = res.usage.input.totalTokenCount
246
+ outputTokens = res.usage.output.totalTokenCount
247
+ } else {
248
+ outputTokens = max(1, responseText.count / 4)
249
+ }
250
+ }
251
+
252
+ let elapsedNs = Int64(Date().timeIntervalSince(startTime) * 1_000_000_000)
253
+
254
+ jsonOut([
255
+ "success": true,
256
+ "model": requestedModel,
257
+ "response": responseText,
258
+ "inputTokens": inputTokens,
259
+ "outputTokens": outputTokens,
260
+ "totalDuration": elapsedNs
261
+ ])
262
+ } catch {
263
+ jsonOut([
264
+ "success": false,
265
+ "error": "Apple Foundation Model error: \(error.localizedDescription)"
266
+ ])
267
+ }
268
+ }
269
+ #endif
270
+ }
271
+
272
+ Task {
273
+ await SynthuxAppleFMBridge.run()
274
+ exit(0)
275
+ }
276
+ RunLoop.main.run()
277
+
@@ -0,0 +1,277 @@
1
+ #!/usr/bin/env swift
2
+ /**
3
+ * synthux — Apple Foundation Models (macOS 26 / macOS 27+) Local Bridge
4
+ *
5
+ * Interfaces directly with Apple's native `FoundationModels` framework:
6
+ * - `SystemLanguageModel.default` (On-Device Apple Silicon Neural Engine model)
7
+ * - `SystemLanguageModel(useCase: .contentTagging)` (Fast structured tagging)
8
+ * - `PrivateCloudComputeLanguageModel()` (macOS 27+ Apple Private Cloud Compute)
9
+ *
10
+ * Commands:
11
+ * 1. `synthux-apple-fm status`
12
+ * Outputs JSON with availability, macOS version, context window size, and models.
13
+ * 2. `synthux-apple-fm generate`
14
+ * Reads JSON payload from stdin:
15
+ * { "model": "apple-on-device", "prompt": "...", "system": "...", "temperature": 0.3, "maxTokens": 2048 }
16
+ * Outputs JSON response to stdout:
17
+ * { "success": true, "model": "apple-on-device", "response": "{...}", "inputTokens": 0, "outputTokens": 0 }
18
+ */
19
+
20
+ import Foundation
21
+ #if canImport(FoundationModels)
22
+ import FoundationModels
23
+ #endif
24
+
25
+ struct BridgeRequest: Decodable {
26
+ let model: String?
27
+ let prompt: String
28
+ let system: String?
29
+ let temperature: Double?
30
+ let maxTokens: Int?
31
+ }
32
+
33
+ func jsonOut(_ dict: [String: Any]) {
34
+ if let data = try? JSONSerialization.data(withJSONObject: dict, options: []),
35
+ let str = String(data: data, encoding: .utf8) {
36
+ FileHandle.standardOutput.write((str + "\n").data(using: .utf8)!)
37
+ }
38
+ }
39
+
40
+ func cleanJsonResponse(_ text: String) -> String {
41
+ var trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
42
+ if trimmed.hasPrefix("```json") {
43
+ trimmed = String(trimmed.dropFirst(7))
44
+ } else if trimmed.hasPrefix("```") {
45
+ trimmed = String(trimmed.dropFirst(3))
46
+ }
47
+ if trimmed.hasSuffix("```") {
48
+ trimmed = String(trimmed.dropLast(3))
49
+ }
50
+ return trimmed.trimmingCharacters(in: .whitespacesAndNewlines)
51
+ }
52
+
53
+ func osVersionString() -> String {
54
+ let v = ProcessInfo.processInfo.operatingSystemVersion
55
+ return "macOS \(v.majorVersion).\(v.minorVersion).\(v.patchVersion)"
56
+ }
57
+
58
+ struct SynthuxAppleFMBridge {
59
+ static func run() async {
60
+ let args = CommandLine.arguments
61
+ let command = args.count > 1 ? args[1] : "status"
62
+
63
+ #if canImport(FoundationModels)
64
+ if #available(macOS 26.0, *) {
65
+ if command == "status" {
66
+ await handleStatus()
67
+ return
68
+ } else if command == "generate" {
69
+ await handleGenerate()
70
+ return
71
+ }
72
+ }
73
+ #endif
74
+
75
+ jsonOut([
76
+ "available": false,
77
+ "status": "unsupported_os",
78
+ "reason": "Apple FoundationModels framework requires macOS 26.0 or newer (macOS 27 recommended).",
79
+ "osVersion": osVersionString(),
80
+ "models": []
81
+ ])
82
+ }
83
+
84
+ #if canImport(FoundationModels)
85
+ @available(macOS 26.0, *)
86
+ static func handleStatus() async {
87
+ let defaultModel = SystemLanguageModel.default
88
+ var statusStr = "connected"
89
+ var reasonStr: String? = nil
90
+ var isAvailable = false
91
+
92
+ switch defaultModel.availability {
93
+ case .available:
94
+ isAvailable = true
95
+ statusStr = "connected"
96
+ case .unavailable(let reason):
97
+ isAvailable = false
98
+ switch reason {
99
+ case .deviceNotEligible:
100
+ statusStr = "device_not_eligible"
101
+ reasonStr = "This Mac is not eligible for Apple Intelligence (Apple Silicon M1 or newer required)."
102
+ case .appleIntelligenceNotEnabled:
103
+ statusStr = "apple_intelligence_disabled"
104
+ reasonStr = "Apple Intelligence is disabled. Enable it in System Settings → Apple Intelligence & Siri."
105
+ case .modelNotReady:
106
+ statusStr = "model_not_ready"
107
+ reasonStr = "Apple Foundation Model is still preparing or downloading in macOS System Settings."
108
+ @unknown default:
109
+ statusStr = "unavailable"
110
+ reasonStr = "Apple Foundation Model is currently unavailable."
111
+ }
112
+ }
113
+
114
+ var contextSize = 4096
115
+ var variantName = "Apple On-Device Foundation Model"
116
+ var pccAvailable = false
117
+
118
+ if #available(macOS 27.0, *) {
119
+ if defaultModel.contextSize > 0 {
120
+ contextSize = defaultModel.contextSize
121
+ }
122
+ variantName = defaultModel.variant.displayName
123
+ let pccModel = PrivateCloudComputeLanguageModel()
124
+ pccAvailable = pccModel.isAvailable
125
+ }
126
+
127
+ var models: [[String: Any]] = [
128
+ [
129
+ "id": "apple-on-device",
130
+ "name": "Apple Foundation Model (On-Device ANE)",
131
+ "size": 0
132
+ ],
133
+ [
134
+ "id": "apple-content-tagging",
135
+ "name": "Apple Foundation Model (Fast Tagging)",
136
+ "size": 0
137
+ ]
138
+ ]
139
+
140
+ if #available(macOS 27.0, *) {
141
+ models.insert([
142
+ "id": "apple-pcc",
143
+ "name": "Apple Private Cloud Compute (macOS 27)",
144
+ "size": 0
145
+ ], at: 1)
146
+ }
147
+
148
+ jsonOut([
149
+ "available": isAvailable,
150
+ "status": statusStr,
151
+ "reason": reasonStr as Any,
152
+ "osVersion": osVersionString(),
153
+ "contextSize": contextSize,
154
+ "variant": variantName,
155
+ "pccAvailable": pccAvailable,
156
+ "models": models
157
+ ])
158
+ }
159
+
160
+ @available(macOS 26.0, *)
161
+ static func handleGenerate() async {
162
+ let inputData = FileHandle.standardInput.readDataToEndOfFile()
163
+ guard !inputData.isEmpty,
164
+ let req = try? JSONDecoder().decode(BridgeRequest.self, from: inputData) else {
165
+ jsonOut([
166
+ "success": false,
167
+ "error": "Invalid JSON input payload for Apple Foundation Model bridge."
168
+ ])
169
+ return
170
+ }
171
+
172
+ let startTime = Date()
173
+ let requestedModel = req.model ?? "apple-on-device"
174
+ let temperature = req.temperature ?? 0.3
175
+ let maxTokens = req.maxTokens ?? 2048
176
+ let options = GenerationOptions(
177
+ temperature: temperature,
178
+ maximumResponseTokens: maxTokens
179
+ )
180
+
181
+ // Trim prompt safely if it exceeds on-device context budget (~12,000 chars ≈ 3,000 tokens)
182
+ let maxPromptChars = requestedModel == "apple-pcc" ? 28000 : 12000
183
+ let safePrompt: String
184
+ if req.prompt.count > maxPromptChars {
185
+ safePrompt = String(req.prompt.prefix(maxPromptChars)) + "\n\n[DOM truncated to fit on-device context window. Respond ONLY with valid JSON.]"
186
+ } else {
187
+ safePrompt = req.prompt
188
+ }
189
+
190
+ do {
191
+ var responseText = ""
192
+ var inputTokens = max(1, safePrompt.count / 4)
193
+ var outputTokens = 0
194
+
195
+ if #available(macOS 27.0, *), requestedModel == "apple-pcc" {
196
+ let pccModel = PrivateCloudComputeLanguageModel()
197
+ if pccModel.isAvailable {
198
+ let session = LanguageModelSession(
199
+ model: pccModel,
200
+ instructions: req.system
201
+ )
202
+ let res = try await session.respond(to: safePrompt, options: options)
203
+ responseText = cleanJsonResponse(res.content)
204
+ inputTokens = res.usage.input.totalTokenCount
205
+ outputTokens = res.usage.output.totalTokenCount
206
+ } else {
207
+ // Fallback to on-device model if PCC is not ready
208
+ let onDevice = SystemLanguageModel(
209
+ useCase: .general,
210
+ guardrails: .permissiveContentTransformations
211
+ )
212
+ let session = LanguageModelSession(
213
+ model: onDevice,
214
+ instructions: req.system
215
+ )
216
+ let res = try await session.respond(to: safePrompt, options: options)
217
+ responseText = cleanJsonResponse(res.content)
218
+ inputTokens = res.usage.input.totalTokenCount
219
+ outputTokens = res.usage.output.totalTokenCount
220
+ }
221
+ } else {
222
+ let useCase: SystemLanguageModel.UseCase = (requestedModel == "apple-content-tagging")
223
+ ? .contentTagging
224
+ : .general
225
+ let onDevice = SystemLanguageModel(
226
+ useCase: useCase,
227
+ guardrails: .permissiveContentTransformations
228
+ )
229
+
230
+ guard onDevice.isAvailable else {
231
+ jsonOut([
232
+ "success": false,
233
+ "error": "Apple Foundation Model is not available on this Mac (\(onDevice.availability)). Make sure Apple Intelligence is enabled in System Settings."
234
+ ])
235
+ return
236
+ }
237
+
238
+ let session = LanguageModelSession(
239
+ model: onDevice,
240
+ instructions: req.system
241
+ )
242
+ let res = try await session.respond(to: safePrompt, options: options)
243
+ responseText = cleanJsonResponse(res.content)
244
+ if #available(macOS 27.0, *) {
245
+ inputTokens = res.usage.input.totalTokenCount
246
+ outputTokens = res.usage.output.totalTokenCount
247
+ } else {
248
+ outputTokens = max(1, responseText.count / 4)
249
+ }
250
+ }
251
+
252
+ let elapsedNs = Int64(Date().timeIntervalSince(startTime) * 1_000_000_000)
253
+
254
+ jsonOut([
255
+ "success": true,
256
+ "model": requestedModel,
257
+ "response": responseText,
258
+ "inputTokens": inputTokens,
259
+ "outputTokens": outputTokens,
260
+ "totalDuration": elapsedNs
261
+ ])
262
+ } catch {
263
+ jsonOut([
264
+ "success": false,
265
+ "error": "Apple Foundation Model error: \(error.localizedDescription)"
266
+ ])
267
+ }
268
+ }
269
+ #endif
270
+ }
271
+
272
+ Task {
273
+ await SynthuxAppleFMBridge.run()
274
+ exit(0)
275
+ }
276
+ RunLoop.main.run()
277
+