ccphoto 0.2.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/dist/mcp.js ADDED
@@ -0,0 +1,309 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { z } from "zod";
4
+ import fs from "node:fs";
5
+ import os from "node:os";
6
+ import path from "node:path";
7
+ import qrcode from "qrcode-terminal";
8
+ import { startServer, isServerRunning, getServerConfig, waitForPhoto, requestPhoto, hasConnectedClients, sendToPhone, startHttpsServer, isHttpsServerRunning, getLatestFrame, switchToLiveMode } from "./server.js";
9
+ import { getLocalIPv4 } from "./network.js";
10
+ import { generateToken } from "./token.js";
11
+ import { ensureCerts } from "./cert.js";
12
+ import { listPhotos, getLatestPhoto, getPhotoByFilename } from "./storage.js";
13
+ const DEFAULT_OUTPUT_DIR = path.join(os.homedir(), ".ccphoto", "captures");
14
+ const DEFAULT_PORT = 3847;
15
+ function getOutputDir() {
16
+ return process.env.CCPHOTO_DIR || DEFAULT_OUTPUT_DIR;
17
+ }
18
+ function generateQRText(url) {
19
+ let qrText = "";
20
+ qrcode.generate(url, { small: true }, (code) => {
21
+ qrText = code;
22
+ });
23
+ return qrText;
24
+ }
25
+ async function ensureServer() {
26
+ if (isServerRunning()) {
27
+ const cfg = getServerConfig();
28
+ return `http://${cfg.host}:${cfg.port}/?token=${cfg.token}`;
29
+ }
30
+ const host = getLocalIPv4();
31
+ if (!host) {
32
+ throw new Error("Could not detect local network IP. Make sure you're connected to WiFi.");
33
+ }
34
+ const token = generateToken();
35
+ const cfg = await startServer({
36
+ port: DEFAULT_PORT,
37
+ outputDir: getOutputDir(),
38
+ token,
39
+ host,
40
+ });
41
+ return `http://${cfg.host}:${cfg.port}/?token=${cfg.token}`;
42
+ }
43
+ async function ensureHttpsServer() {
44
+ // Ensure HTTP server is running first (shares config/token)
45
+ await ensureServer();
46
+ if (isHttpsServerRunning()) {
47
+ const cfg = getServerConfig();
48
+ return `https://${cfg.host}:${cfg.httpsPort}/?token=${cfg.token}`;
49
+ }
50
+ const cfg = getServerConfig();
51
+ const certs = await ensureCerts(cfg.host);
52
+ await startHttpsServer(cfg, certs);
53
+ return `https://${cfg.host}:${cfg.httpsPort}/?token=${cfg.token}`;
54
+ }
55
+ export async function runMcpServer() {
56
+ const server = new McpServer({
57
+ name: "ccphoto",
58
+ version: "0.2.0",
59
+ });
60
+ server.tool("capture_photo", "Start the camera server and return a QR code for the user to scan with their phone. The server stays running for the entire session — the user only needs to scan once, then they can take photos anytime and switch between Photo and Live modes. If a phone is already connected, it will be notified that a photo is requested (the button will pulse). CRITICAL: You MUST paste the QR code directly in your response text (inside a code block) so the user can see and scan it. After showing the QR, IMMEDIATELY call wait_for_photo to receive the photo.", {}, async () => {
61
+ const alreadyRunning = isServerRunning();
62
+ const url = await ensureServer();
63
+ if (alreadyRunning && hasConnectedClients()) {
64
+ requestPhoto();
65
+ return {
66
+ content: [
67
+ {
68
+ type: "text",
69
+ text: `Phone has been notified — the camera button is pulsing. Waiting for photo...`,
70
+ },
71
+ ],
72
+ };
73
+ }
74
+ const qrText = generateQRText(url);
75
+ return {
76
+ content: [
77
+ {
78
+ type: "text",
79
+ text: `QR_CODE:\n${qrText}\nURL: ${url}`,
80
+ },
81
+ ],
82
+ };
83
+ });
84
+ server.tool("wait_for_photo", "Wait for a photo or a user action from the phone. Blocks until something happens: a photo is uploaded, the user switches to Live mode, or timeout. The phone is fully bidirectional — the user can drive the interaction by tapping Photo or Live on their phone. If the user switches to Live mode, you should call get_live_frame to start watching. If a photo arrives, it is returned as an image. IMPORTANT: After processing the result, IMMEDIATELY call wait_for_photo AGAIN to keep listening. This creates a persistent connection where the user can keep sending photos, voice messages, or switching modes from the phone without typing in Claude Code. Only stop the loop if the user explicitly types something in Claude Code.", {
85
+ timeout_seconds: z
86
+ .number()
87
+ .optional()
88
+ .describe("How long to wait in seconds (default: 120)"),
89
+ }, async ({ timeout_seconds }) => {
90
+ const timeoutMs = (timeout_seconds ?? 120) * 1000;
91
+ const url = isServerRunning()
92
+ ? `http://${getServerConfig().host}:${getServerConfig().port}/?token=${getServerConfig().token}`
93
+ : null;
94
+ const result = await waitForPhoto(timeoutMs);
95
+ if (!result) {
96
+ return {
97
+ content: [
98
+ {
99
+ type: "text",
100
+ text: `No activity within ${Math.round(timeoutMs / 1000)} seconds. The phone is still connected. Call wait_for_photo again to keep listening.`,
101
+ },
102
+ ],
103
+ };
104
+ }
105
+ // Check if this is a user action (has 'action' property) rather than a photo
106
+ if ("action" in result) {
107
+ const action = result.action;
108
+ if (action === "voice_message") {
109
+ const text = result.data?.text || "(empty)";
110
+ return {
111
+ content: [{
112
+ type: "text",
113
+ text: `Voice from phone: ${text}`,
114
+ }],
115
+ };
116
+ }
117
+ if (action === "start_livestream") {
118
+ return {
119
+ content: [
120
+ {
121
+ type: "text",
122
+ text: "The user switched to Live mode on their phone. The camera is now streaming frames every 3 seconds. Call get_live_frame to see what the camera sees, and send_to_phone to show guidance on the phone screen.",
123
+ },
124
+ ],
125
+ };
126
+ }
127
+ return {
128
+ content: [
129
+ {
130
+ type: "text",
131
+ text: `User action from phone: ${action}`,
132
+ },
133
+ ],
134
+ };
135
+ }
136
+ // It's a photo
137
+ const meta = result;
138
+ const imageData = fs.readFileSync(meta.absolutePath);
139
+ const base64 = imageData.toString("base64");
140
+ return {
141
+ content: [
142
+ {
143
+ type: "image",
144
+ data: base64,
145
+ mimeType: meta.mimeType,
146
+ },
147
+ {
148
+ type: "text",
149
+ text: `Photo received: ${meta.filename} (${(meta.sizeBytes / 1024).toFixed(0)} KB)\nSaved at: ${meta.absolutePath}`,
150
+ },
151
+ ],
152
+ };
153
+ });
154
+ server.tool("get_latest_photo", "Get a photo from the phone camera. Returns the most recent photo by default, or a specific photo by filename. Use list_photos to see available filenames. Does not wait for a new photo — use wait_for_photo for that.", {
155
+ filename: z
156
+ .string()
157
+ .optional()
158
+ .describe("Specific photo filename to retrieve. If omitted, returns the most recent photo."),
159
+ }, async ({ filename }) => {
160
+ const outputDir = getOutputDir();
161
+ const meta = filename
162
+ ? getPhotoByFilename(outputDir, filename)
163
+ : getLatestPhoto(outputDir);
164
+ if (!meta) {
165
+ return {
166
+ content: [
167
+ {
168
+ type: "text",
169
+ text: filename
170
+ ? `Photo "${filename}" not found. Use list_photos to see available photos.`
171
+ : "No photos have been captured yet. Use capture_photo first.",
172
+ },
173
+ ],
174
+ };
175
+ }
176
+ const imageData = fs.readFileSync(meta.absolutePath);
177
+ const base64 = imageData.toString("base64");
178
+ return {
179
+ content: [
180
+ {
181
+ type: "image",
182
+ data: base64,
183
+ mimeType: meta.mimeType,
184
+ },
185
+ {
186
+ type: "text",
187
+ text: `Photo: ${meta.filename} (${(meta.sizeBytes / 1024).toFixed(0)} KB)\nCaptured: ${meta.timestamp.toISOString()}\nPath: ${meta.absolutePath}`,
188
+ },
189
+ ],
190
+ };
191
+ });
192
+ server.tool("list_photos", "List all captured photos with their filenames, timestamps, and file sizes. Use a filename with get_latest_photo to retrieve a specific photo.", {}, async () => {
193
+ const outputDir = getOutputDir();
194
+ const photos = listPhotos(outputDir);
195
+ if (photos.length === 0) {
196
+ return {
197
+ content: [
198
+ {
199
+ type: "text",
200
+ text: "No photos have been captured yet. Use capture_photo first.",
201
+ },
202
+ ],
203
+ };
204
+ }
205
+ const lines = photos.map((p) => `- ${p.filename} (${(p.sizeBytes / 1024).toFixed(0)} KB) — ${p.timestamp.toISOString()}`);
206
+ return {
207
+ content: [
208
+ {
209
+ type: "text",
210
+ text: `${photos.length} photo(s) captured:\n\n${lines.join("\n")}\n\nDirectory: ${outputDir}`,
211
+ },
212
+ ],
213
+ };
214
+ });
215
+ server.tool("send_to_phone", "Send text and/or an image to the connected phone display. The phone becomes a reference screen — useful for showing wiring instructions, pinout diagrams, code snippets, or reference images while the user works with both hands. Text supports basic markdown (headers, bold, code, lists). Requires a phone to be connected via capture_photo first.", {
216
+ text: z.string().optional().describe("Text content to display. Supports basic markdown: **bold**, `code`, # headers, - lists, ```code blocks```."),
217
+ image_base64: z.string().optional().describe("Base64-encoded image data to display on the phone."),
218
+ image_mime_type: z.string().optional().describe("MIME type of the image (e.g. 'image/png'). Required when image_base64 is provided."),
219
+ speak: z.preprocess((v) => v === "true" || v === true, z.boolean()).optional().describe("If true, the phone will speak the text aloud using text-to-speech."),
220
+ }, async ({ text, image_base64, image_mime_type, speak }) => {
221
+ if (!text && !image_base64) {
222
+ return {
223
+ content: [{ type: "text", text: "Error: provide at least 'text' or 'image_base64'." }],
224
+ };
225
+ }
226
+ if (image_base64 && !image_mime_type) {
227
+ return {
228
+ content: [{ type: "text", text: "Error: 'image_mime_type' is required when 'image_base64' is provided." }],
229
+ };
230
+ }
231
+ if (!(isServerRunning() || isHttpsServerRunning()) || !hasConnectedClients()) {
232
+ return {
233
+ content: [{ type: "text", text: "No phone connected. Use capture_photo or start_livestream first to connect a phone." }],
234
+ };
235
+ }
236
+ const message = {};
237
+ if (text)
238
+ message.text = text;
239
+ if (image_base64) {
240
+ message.imageData = image_base64;
241
+ message.mimeType = image_mime_type;
242
+ }
243
+ if (speak)
244
+ message.speak = true;
245
+ sendToPhone(message);
246
+ const parts = [];
247
+ if (text)
248
+ parts.push(`text (${text.length} chars)`);
249
+ if (image_base64)
250
+ parts.push(`image (${image_mime_type})`);
251
+ if (speak)
252
+ parts.push("(spoken)");
253
+ return {
254
+ content: [{ type: "text", text: `Sent to phone: ${parts.join(" + ")}` }],
255
+ };
256
+ });
257
+ server.tool("start_livestream", "Start a semi-real-time video stream from the phone camera. If a phone is already connected, it switches to live mode automatically (no new QR needed). The phone streams frames every 3 seconds. Call get_live_frame to see what the camera sees, and send_to_phone to show guidance on the phone screen. CRITICAL: If showing a QR code, paste it in your response text inside a code block.", {}, async () => {
258
+ const httpsUrl = await ensureHttpsServer();
259
+ // Always send the SSE event to notify connected phones
260
+ if (hasConnectedClients()) {
261
+ switchToLiveMode();
262
+ }
263
+ // Always show the HTTPS QR code — the phone needs HTTPS for getUserMedia
264
+ // even if already connected via HTTP
265
+ const qrText = generateQRText(httpsUrl);
266
+ return {
267
+ content: [{
268
+ type: "text",
269
+ text: `Livestream ready! Scan the QR code to connect.\n\nOn Android Chrome: tap "Advanced" then "Proceed" past the certificate warning (one-time).\n\nQR_CODE:\n${qrText}\nURL: ${httpsUrl}\n\nAfter connecting, call get_live_frame to see what the camera sees.`,
270
+ }],
271
+ };
272
+ });
273
+ server.tool("get_live_frame", "Get the latest frame from the phone's live camera stream. Returns the most recent frame as an image with a timestamp showing how fresh it is. Use wait_seconds to delay before grabbing the frame — this gives the user time to adjust the camera after receiving guidance via send_to_phone. Combine send_to_phone + get_live_frame(wait_seconds) in a loop to guide the user hands-free.", {
274
+ wait_seconds: z.coerce.number().optional().describe("Wait this many seconds before returning the frame. Use after sending guidance to give the user time to adjust the camera (e.g., 5-10 seconds)."),
275
+ }, async ({ wait_seconds }) => {
276
+ if (wait_seconds && wait_seconds > 0) {
277
+ await new Promise((resolve) => setTimeout(resolve, wait_seconds * 1000));
278
+ }
279
+ const frame = getLatestFrame();
280
+ if (!frame) {
281
+ return {
282
+ content: [
283
+ {
284
+ type: "text",
285
+ text: "No frames received yet. Make sure the phone is connected in live mode. Use start_livestream to begin.",
286
+ },
287
+ ],
288
+ };
289
+ }
290
+ const ageMs = Date.now() - frame.timestamp.getTime();
291
+ const base64 = frame.data.toString("base64");
292
+ return {
293
+ content: [
294
+ {
295
+ type: "image",
296
+ data: base64,
297
+ mimeType: frame.mimeType,
298
+ },
299
+ {
300
+ type: "text",
301
+ text: `Live frame (${(frame.data.length / 1024).toFixed(0)} KB) — ${ageMs < 10000 ? "fresh" : "stale"} (${(ageMs / 1000).toFixed(1)}s ago)${frame.width ? ` — ${frame.width}x${frame.height}` : ""}`,
302
+ },
303
+ ],
304
+ };
305
+ });
306
+ const transport = new StdioServerTransport();
307
+ await server.connect(transport);
308
+ }
309
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function renderMobilePage(token: string): string;