@unpuzzle/viddy-mcp 0.1.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/log.js ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Structured stderr logging.
3
+ *
4
+ * stdout carries the MCP protocol frames — anything written there corrupts the
5
+ * stream and the host drops the connection. Every diagnostic goes to stderr.
6
+ *
7
+ * This lives in its own module (rather than in index.ts, as the Vibe Marketing
8
+ * package does) so auth/oauth/api can import it without a cycle back through
9
+ * the server entry point.
10
+ */
11
+ export function log(level, message, extra) {
12
+ const ts = new Date().toISOString();
13
+ const line = extra !== undefined
14
+ ? `[${ts}] ${level} ${message} ${safeJson(extra)}`
15
+ : `[${ts}] ${level} ${message}`;
16
+ process.stderr.write(line + "\n");
17
+ }
18
+ /** Never let a logging call throw on a circular object. */
19
+ function safeJson(value) {
20
+ try {
21
+ return JSON.stringify(value);
22
+ }
23
+ catch {
24
+ return String(value);
25
+ }
26
+ }
27
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,UAAU,GAAG,CAAC,KAAe,EAAE,OAAe,EAAE,KAAe;IACnE,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACpC,MAAM,IAAI,GACR,KAAK,KAAK,SAAS;QACjB,CAAC,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,OAAO,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE;QAClD,CAAC,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,OAAO,EAAE,CAAC;IACpC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,2DAA2D;AAC3D,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;AACH,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * OAuth 2.1 client flow — the browser-based PKCE dance against Viddy.
3
+ *
4
+ * runAuthorizationFlow():
5
+ * 1. Generate a PKCE verifier + S256 challenge.
6
+ * 2. Bind a temp HTTP server to 127.0.0.1 on a free port (port 0).
7
+ * 3. Open the browser to /api/oauth/authorize with a loopback redirect_uri.
8
+ * 4. Receive ?code=&state= on /callback, verify state, close the server.
9
+ * 5. POST /api/oauth/token with the code + verifier.
10
+ * 6. Persist the tokens.
11
+ *
12
+ * Loopback security model (RFC 8252):
13
+ * - Viddy's oauth_client row for `viddy-mcp` registers loopback redirect
14
+ * URIs, so the port can be chosen at runtime.
15
+ * - Binding to 127.0.0.1 explicitly means only this machine's user can reach
16
+ * the callback; PKCE is the proof of possession.
17
+ * - `state` is fresh per flow and verified on callback (CSRF defence).
18
+ */
19
+ import { type StoredTokens } from "./auth.js";
20
+ /**
21
+ * Run the browser-based OAuth flow and resolve to fresh, saved tokens.
22
+ * `timeoutMs` is how long to wait for the user at the consent screen.
23
+ */
24
+ export declare function runAuthorizationFlow(opts?: {
25
+ timeoutMs?: number;
26
+ }): Promise<StoredTokens>;
27
+ /**
28
+ * Trade a refresh token for a new pair and save it.
29
+ *
30
+ * Refresh tokens rotate, so the old one is dead the moment this succeeds.
31
+ * Throws when the grant was revoked from Settings → Connected Apps; the caller
32
+ * clears stored tokens and re-runs the consent flow.
33
+ */
34
+ export declare function refreshTokens(currentRefreshToken: string): Promise<StoredTokens>;
35
+ //# sourceMappingURL=oauth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAQH,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAkF1D;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAChC,OAAO,CAAC,YAAY,CAAC,CA2CvB;AA+HD;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,mBAAmB,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAUtF"}
package/dist/oauth.js ADDED
@@ -0,0 +1,252 @@
1
+ /**
2
+ * OAuth 2.1 client flow — the browser-based PKCE dance against Viddy.
3
+ *
4
+ * runAuthorizationFlow():
5
+ * 1. Generate a PKCE verifier + S256 challenge.
6
+ * 2. Bind a temp HTTP server to 127.0.0.1 on a free port (port 0).
7
+ * 3. Open the browser to /api/oauth/authorize with a loopback redirect_uri.
8
+ * 4. Receive ?code=&state= on /callback, verify state, close the server.
9
+ * 5. POST /api/oauth/token with the code + verifier.
10
+ * 6. Persist the tokens.
11
+ *
12
+ * Loopback security model (RFC 8252):
13
+ * - Viddy's oauth_client row for `viddy-mcp` registers loopback redirect
14
+ * URIs, so the port can be chosen at runtime.
15
+ * - Binding to 127.0.0.1 explicitly means only this machine's user can reach
16
+ * the callback; PKCE is the proof of possession.
17
+ * - `state` is fresh per flow and verified on callback (CSRF defence).
18
+ */
19
+ import { createServer } from "node:http";
20
+ import { spawn } from "node:child_process";
21
+ import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
22
+ import { platform } from "node:os";
23
+ import { CLIENT_ID, REQUESTED_SCOPES, getBaseUrl } from "./config.js";
24
+ import { saveTokens } from "./auth.js";
25
+ import { log } from "./log.js";
26
+ function pkcePair() {
27
+ const verifier = randomBytes(32).toString("base64url");
28
+ const challenge = createHash("sha256").update(verifier).digest("base64url");
29
+ return { verifier, challenge };
30
+ }
31
+ /** Constant-time state comparison — the values are same-length base64url. */
32
+ function stateMatches(a, b) {
33
+ const ab = Buffer.from(a, "utf8");
34
+ const bb = Buffer.from(b, "utf8");
35
+ if (ab.length !== bb.length)
36
+ return false;
37
+ return timingSafeEqual(ab, bb);
38
+ }
39
+ /** Open the user's default browser. OS commands, so no extra dependency. */
40
+ function openBrowser(url) {
41
+ const override = process.env["VIDDY_BROWSER_CMD"];
42
+ if (override) {
43
+ spawn(override, [url], { stdio: "ignore", detached: true }).unref();
44
+ return;
45
+ }
46
+ const p = platform();
47
+ if (p === "darwin") {
48
+ spawn("open", [url], { stdio: "ignore", detached: true }).unref();
49
+ }
50
+ else if (p === "win32") {
51
+ spawn("cmd", ["/c", "start", "", url], { stdio: "ignore", detached: true }).unref();
52
+ }
53
+ else {
54
+ spawn("xdg-open", [url], { stdio: "ignore", detached: true }).unref();
55
+ }
56
+ }
57
+ function escapeHtml(s) {
58
+ return s.replace(/[&<>"']/g, (c) => {
59
+ switch (c) {
60
+ case "&":
61
+ return "&amp;";
62
+ case "<":
63
+ return "&lt;";
64
+ case ">":
65
+ return "&gt;";
66
+ case '"':
67
+ return "&quot;";
68
+ default:
69
+ return "&#39;";
70
+ }
71
+ });
72
+ }
73
+ /** The tiny page the user lands on after the redirect. */
74
+ function renderCallbackHtml(ok, message) {
75
+ const title = ok ? "Connected" : "Sign-in error";
76
+ return `<!doctype html><html><head><meta charset="utf-8"><title>${title}</title>
77
+ <style>
78
+ body{font-family:system-ui,sans-serif;max-width:440px;margin:80px auto;padding:0 20px;color:#111;line-height:1.5}
79
+ h1{font-size:20px;margin:0 0 10px}
80
+ p{color:#555}
81
+ .ok{color:#0f6b35}
82
+ .err{color:#b91c1c}
83
+ @media (prefers-color-scheme: dark){
84
+ body{background:#0b0b0d;color:#e8e8ea}
85
+ p{color:#a1a1aa}
86
+ .ok{color:#4ade80}
87
+ .err{color:#f87171}
88
+ }
89
+ </style></head><body>
90
+ <h1 class="${ok ? "ok" : "err"}">${ok ? "Connected to Viddy" : "Sign-in error"}</h1>
91
+ <p>${escapeHtml(message)}</p>
92
+ ${ok ? "<p>You can close this tab and return to your terminal.</p>" : ""}
93
+ </body></html>`;
94
+ }
95
+ /**
96
+ * Run the browser-based OAuth flow and resolve to fresh, saved tokens.
97
+ * `timeoutMs` is how long to wait for the user at the consent screen.
98
+ */
99
+ export async function runAuthorizationFlow(opts = {}) {
100
+ const timeoutMs = opts.timeoutMs ?? 5 * 60 * 1000;
101
+ const baseUrl = getBaseUrl();
102
+ const { verifier, challenge } = pkcePair();
103
+ const state = randomBytes(16).toString("base64url");
104
+ const { server, redirectUri, codePromise } = await startCallbackServer(state);
105
+ let timer;
106
+ try {
107
+ const authorizeUrl = new URL("/api/oauth/authorize", baseUrl);
108
+ authorizeUrl.searchParams.set("client_id", CLIENT_ID);
109
+ authorizeUrl.searchParams.set("redirect_uri", redirectUri);
110
+ authorizeUrl.searchParams.set("response_type", "code");
111
+ authorizeUrl.searchParams.set("scope", REQUESTED_SCOPES.join(" "));
112
+ authorizeUrl.searchParams.set("state", state);
113
+ authorizeUrl.searchParams.set("code_challenge", challenge);
114
+ authorizeUrl.searchParams.set("code_challenge_method", "S256");
115
+ log("info", "opening browser for Viddy consent");
116
+ process.stderr.write(`\nIf your browser didn't open automatically, paste this URL:\n ${authorizeUrl.toString()}\n\n`);
117
+ openBrowser(authorizeUrl.toString());
118
+ const code = await Promise.race([
119
+ codePromise,
120
+ new Promise((_, reject) => {
121
+ timer = setTimeout(() => reject(new Error("Viddy sign-in timed out after 5 minutes. Run the `connect` tool to try again.")), timeoutMs);
122
+ }),
123
+ ]);
124
+ const tokens = await exchangeCodeForTokens({ code, verifier, redirectUri });
125
+ await saveTokens(tokens);
126
+ log("info", "OAuth flow complete; tokens saved");
127
+ return tokens;
128
+ }
129
+ finally {
130
+ if (timer)
131
+ clearTimeout(timer);
132
+ server.close();
133
+ }
134
+ }
135
+ function startCallbackServer(expectedState) {
136
+ return new Promise((resolveOuter, rejectOuter) => {
137
+ let onCode;
138
+ let onError;
139
+ const codePromise = new Promise((res, rej) => {
140
+ onCode = res;
141
+ onError = rej;
142
+ });
143
+ const server = createServer((req, res) => {
144
+ if (!req.url) {
145
+ res.writeHead(400).end();
146
+ return;
147
+ }
148
+ const url = new URL(req.url, "http://127.0.0.1");
149
+ if (url.pathname !== "/callback") {
150
+ // Favicon and stray probes shouldn't fail the flow.
151
+ res.writeHead(404, { "Content-Type": "text/html; charset=utf-8" });
152
+ res.end(renderCallbackHtml(false, "Unknown path on the local callback server."));
153
+ return;
154
+ }
155
+ const code = url.searchParams.get("code");
156
+ const state = url.searchParams.get("state");
157
+ const error = url.searchParams.get("error");
158
+ const errorDescription = url.searchParams.get("error_description");
159
+ if (error) {
160
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
161
+ res.end(renderCallbackHtml(false, `${error}: ${errorDescription ?? "sign-in failed"}`));
162
+ onError(new Error(`OAuth error: ${error} — ${errorDescription ?? ""}`));
163
+ return;
164
+ }
165
+ if (!code) {
166
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
167
+ res.end(renderCallbackHtml(false, "Authorization response did not include a code."));
168
+ onError(new Error("OAuth callback missing ?code="));
169
+ return;
170
+ }
171
+ if (!state || !stateMatches(state, expectedState)) {
172
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
173
+ res.end(renderCallbackHtml(false, "CSRF check failed: state mismatch."));
174
+ onError(new Error("OAuth callback state mismatch — possible CSRF"));
175
+ return;
176
+ }
177
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
178
+ res.end(renderCallbackHtml(true, "Authorization complete."));
179
+ onCode(code);
180
+ });
181
+ server.on("error", (e) => rejectOuter(e));
182
+ // Port 0 → the OS picks a free port. `localhost` (not the literal
183
+ // 127.0.0.1) goes in the redirect_uri because some environments rewrite
184
+ // one to the other during the redirect, which would then mismatch the
185
+ // value bound to the auth code. Viddy's loopback matcher accepts both.
186
+ server.listen(0, "127.0.0.1", () => {
187
+ const addr = server.address();
188
+ if (!addr || typeof addr === "string") {
189
+ server.close();
190
+ rejectOuter(new Error("failed to read callback server port"));
191
+ return;
192
+ }
193
+ resolveOuter({
194
+ server,
195
+ redirectUri: `http://localhost:${addr.port}/callback`,
196
+ codePromise,
197
+ });
198
+ });
199
+ });
200
+ }
201
+ async function postToken(body) {
202
+ const res = await fetch(`${getBaseUrl()}/api/oauth/token`, {
203
+ method: "POST",
204
+ headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json" },
205
+ body: body.toString(),
206
+ });
207
+ let json;
208
+ try {
209
+ json = (await res.json());
210
+ }
211
+ catch {
212
+ throw new Error(`Viddy token endpoint returned a non-JSON response (HTTP ${res.status}).`);
213
+ }
214
+ if (!res.ok) {
215
+ throw new Error(`${json["error"] ?? `HTTP ${res.status}`} — ${json["error_description"] ?? "token request rejected"}`);
216
+ }
217
+ const t = json;
218
+ const now = Date.now();
219
+ return {
220
+ accessToken: t.access_token,
221
+ refreshToken: t.refresh_token,
222
+ expiresAt: now + t.expires_in * 1000,
223
+ scope: t.scope,
224
+ grantedAt: now,
225
+ };
226
+ }
227
+ async function exchangeCodeForTokens(args) {
228
+ return postToken(new URLSearchParams({
229
+ grant_type: "authorization_code",
230
+ client_id: CLIENT_ID,
231
+ code: args.code,
232
+ code_verifier: args.verifier,
233
+ redirect_uri: args.redirectUri,
234
+ }));
235
+ }
236
+ /**
237
+ * Trade a refresh token for a new pair and save it.
238
+ *
239
+ * Refresh tokens rotate, so the old one is dead the moment this succeeds.
240
+ * Throws when the grant was revoked from Settings → Connected Apps; the caller
241
+ * clears stored tokens and re-runs the consent flow.
242
+ */
243
+ export async function refreshTokens(currentRefreshToken) {
244
+ const tokens = await postToken(new URLSearchParams({
245
+ grant_type: "refresh_token",
246
+ client_id: CLIENT_ID,
247
+ refresh_token: currentRefreshToken,
248
+ }));
249
+ await saveTokens(tokens);
250
+ return tokens;
251
+ }
252
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,EAAE,UAAU,EAAqB,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAU/B,SAAS,QAAQ;IACf,MAAM,QAAQ,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACvD,MAAM,SAAS,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC5E,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AACjC,CAAC;AAED,6EAA6E;AAC7E,SAAS,YAAY,CAAC,CAAS,EAAE,CAAS;IACxC,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAClC,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAClC,IAAI,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1C,OAAO,eAAe,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC;AAED,4EAA4E;AAC5E,SAAS,WAAW,CAAC,GAAW;IAC9B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IAClD,IAAI,QAAQ,EAAE,CAAC;QACb,KAAK,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;QACpE,OAAO;IACT,CAAC;IACD,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC;IACrB,IAAI,CAAC,KAAK,QAAQ,EAAE,CAAC;QACnB,KAAK,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;IACpE,CAAC;SAAM,IAAI,CAAC,KAAK,OAAO,EAAE,CAAC;QACzB,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;IACtF,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;IACxE,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE;QACjC,QAAQ,CAAC,EAAE,CAAC;YACV,KAAK,GAAG;gBACN,OAAO,OAAO,CAAC;YACjB,KAAK,GAAG;gBACN,OAAO,MAAM,CAAC;YAChB,KAAK,GAAG;gBACN,OAAO,MAAM,CAAC;YAChB,KAAK,GAAG;gBACN,OAAO,QAAQ,CAAC;YAClB;gBACE,OAAO,OAAO,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,0DAA0D;AAC1D,SAAS,kBAAkB,CAAC,EAAW,EAAE,OAAe;IACtD,MAAM,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,eAAe,CAAC;IACjD,OAAO,2DAA2D,KAAK;;;;;;;;;;;;;;aAc5D,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,eAAe;KACzE,UAAU,CAAC,OAAO,CAAC;EACtB,EAAE,CAAC,CAAC,CAAC,4DAA4D,CAAC,CAAC,CAAC,EAAE;eACzD,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,OAA+B,EAAE;IAEjC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;IAClD,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,QAAQ,EAAE,CAAC;IAC3C,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAEpD,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,MAAM,mBAAmB,CAAC,KAAK,CAAC,CAAC;IAC9E,IAAI,KAAiC,CAAC;IAEtC,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,sBAAsB,EAAE,OAAO,CAAC,CAAC;QAC9D,YAAY,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;QACtD,YAAY,CAAC,YAAY,CAAC,GAAG,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;QAC3D,YAAY,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;QACvD,YAAY,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACnE,YAAY,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC9C,YAAY,CAAC,YAAY,CAAC,GAAG,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC;QAC3D,YAAY,CAAC,YAAY,CAAC,GAAG,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC;QAE/D,GAAG,CAAC,MAAM,EAAE,mCAAmC,CAAC,CAAC;QACjD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,mEAAmE,YAAY,CAAC,QAAQ,EAAE,MAAM,CACjG,CAAC;QACF,WAAW,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC,CAAC;QAErC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;YAC9B,WAAW;YACX,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;gBAC/B,KAAK,GAAG,UAAU,CAChB,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,+EAA+E,CAAC,CAAC,EACxG,SAAS,CACV,CAAC;YACJ,CAAC,CAAC;SACH,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAC;QAC5E,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;QACzB,GAAG,CAAC,MAAM,EAAE,mCAAmC,CAAC,CAAC;QACjD,OAAO,MAAM,CAAC;IAChB,CAAC;YAAS,CAAC;QACT,IAAI,KAAK;YAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QAC/B,MAAM,CAAC,KAAK,EAAE,CAAC;IACjB,CAAC;AACH,CAAC;AAQD,SAAS,mBAAmB,CAAC,aAAqB;IAChD,OAAO,IAAI,OAAO,CAAC,CAAC,YAAY,EAAE,WAAW,EAAE,EAAE;QAC/C,IAAI,MAA+B,CAAC;QACpC,IAAI,OAA4B,CAAC;QACjC,MAAM,WAAW,GAAG,IAAI,OAAO,CAAS,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACnD,MAAM,GAAG,GAAG,CAAC;YACb,OAAO,GAAG,GAAG,CAAC;QAChB,CAAC,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,GAAoB,EAAE,GAAmB,EAAE,EAAE;YACxE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;gBACb,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;gBACzB,OAAO;YACT,CAAC;YACD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC;YACjD,IAAI,GAAG,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;gBACjC,oDAAoD;gBACpD,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE,CAAC,CAAC;gBACnE,GAAG,CAAC,GAAG,CAAC,kBAAkB,CAAC,KAAK,EAAE,4CAA4C,CAAC,CAAC,CAAC;gBACjF,OAAO;YACT,CAAC;YAED,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC1C,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC5C,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC5C,MAAM,gBAAgB,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;YAEnE,IAAI,KAAK,EAAE,CAAC;gBACV,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE,CAAC,CAAC;gBACnE,GAAG,CAAC,GAAG,CAAC,kBAAkB,CAAC,KAAK,EAAE,GAAG,KAAK,KAAK,gBAAgB,IAAI,gBAAgB,EAAE,CAAC,CAAC,CAAC;gBACxF,OAAO,CAAC,IAAI,KAAK,CAAC,gBAAgB,KAAK,MAAM,gBAAgB,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;gBACxE,OAAO;YACT,CAAC;YACD,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE,CAAC,CAAC;gBACnE,GAAG,CAAC,GAAG,CAAC,kBAAkB,CAAC,KAAK,EAAE,gDAAgD,CAAC,CAAC,CAAC;gBACrF,OAAO,CAAC,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC,CAAC;gBACpD,OAAO;YACT,CAAC;YACD,IAAI,CAAC,KAAK,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC,EAAE,CAAC;gBAClD,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE,CAAC,CAAC;gBACnE,GAAG,CAAC,GAAG,CAAC,kBAAkB,CAAC,KAAK,EAAE,oCAAoC,CAAC,CAAC,CAAC;gBACzE,OAAO,CAAC,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC,CAAC;gBACpE,OAAO;YACT,CAAC;YAED,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE,CAAC,CAAC;YACnE,GAAG,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,EAAE,yBAAyB,CAAC,CAAC,CAAC;YAC7D,MAAM,CAAC,IAAI,CAAC,CAAC;QACf,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;QAE1C,kEAAkE;QAClE,wEAAwE;QACxE,sEAAsE;QACtE,uEAAuE;QACvE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE;YACjC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;YAC9B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACtC,MAAM,CAAC,KAAK,EAAE,CAAC;gBACf,WAAW,CAAC,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC,CAAC;gBAC9D,OAAO;YACT,CAAC;YACD,YAAY,CAAC;gBACX,MAAM;gBACN,WAAW,EAAE,oBAAoB,IAAI,CAAC,IAAI,WAAW;gBACrD,WAAW;aACZ,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,IAAqB;IAC5C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,UAAU,EAAE,kBAAkB,EAAE;QACzD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE,MAAM,EAAE,kBAAkB,EAAE;QAC5F,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE;KACtB,CAAC,CAAC;IAEH,IAAI,IAA6B,CAAC;IAClC,IAAI,CAAC;QACH,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA4B,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,2DAA2D,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC;IAC7F,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,mBAAmB,CAAC,IAAI,wBAAwB,EAAE,CACtG,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,GAAG,IAAgC,CAAC;IAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,OAAO;QACL,WAAW,EAAE,CAAC,CAAC,YAAY;QAC3B,YAAY,EAAE,CAAC,CAAC,aAAa;QAC7B,SAAS,EAAE,GAAG,GAAG,CAAC,CAAC,UAAU,GAAG,IAAI;QACpC,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,SAAS,EAAE,GAAG;KACf,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,qBAAqB,CAAC,IAIpC;IACC,OAAO,SAAS,CACd,IAAI,eAAe,CAAC;QAClB,UAAU,EAAE,oBAAoB;QAChC,SAAS,EAAE,SAAS;QACpB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,aAAa,EAAE,IAAI,CAAC,QAAQ;QAC5B,YAAY,EAAE,IAAI,CAAC,WAAW;KAC/B,CAAC,CACH,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,mBAA2B;IAC7D,MAAM,MAAM,GAAG,MAAM,SAAS,CAC5B,IAAI,eAAe,CAAC;QAClB,UAAU,EAAE,eAAe;QAC3B,SAAS,EAAE,SAAS;QACpB,aAAa,EAAE,mBAAmB;KACnC,CAAC,CACH,CAAC;IACF,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;IACzB,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@unpuzzle/viddy-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for Viddy \u2014 ask Claude about your screen recordings: transcripts, timestamped notes, and shareable links.",
5
+ "type": "module",
6
+ "bin": {
7
+ "viddy-mcp": "./dist/index.js"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "files": [
12
+ "dist",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "scripts": {
17
+ "build": "node scripts/check-integrity.mjs && tsc && node scripts/bundle.mjs",
18
+ "dev": "tsc --watch",
19
+ "start": "node dist/index.js",
20
+ "typecheck": "tsc --noEmit",
21
+ "prepublishOnly": "npm run build && node scripts/verify-package.mjs",
22
+ "bundle": "node scripts/bundle.mjs"
23
+ },
24
+ "keywords": [
25
+ "mcp",
26
+ "model-context-protocol",
27
+ "claude",
28
+ "viddy",
29
+ "screen-recording",
30
+ "transcript",
31
+ "anthropic"
32
+ ],
33
+ "author": "Muscled <hello@muscled.co>",
34
+ "license": "MIT",
35
+ "homepage": "https://viddy.lol/install/mcp",
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "engines": {
40
+ "node": ">=18"
41
+ },
42
+ "dependencies": {
43
+ "@modelcontextprotocol/sdk": "^1.29.0",
44
+ "env-paths": "^3.0.0",
45
+ "zod": "^4.4.3"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^22.10.5",
49
+ "@viddy/mcp-core": "*",
50
+ "esbuild": "^0.25.12",
51
+ "typescript": "^5"
52
+ }
53
+ }