@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/LICENSE +21 -0
- package/README.md +119 -0
- package/dist/api.d.ts +34 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +121 -0
- package/dist/api.js.map +1 -0
- package/dist/auth.d.ts +35 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +95 -0
- package/dist/auth.js.map +1 -0
- package/dist/config.d.ts +22 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +31 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +663 -0
- package/dist/index.js.map +1 -0
- package/dist/log.d.ts +13 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +27 -0
- package/dist/log.js.map +1 -0
- package/dist/oauth.d.ts +35 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +252 -0
- package/dist/oauth.js.map +1 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Muscled
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# @unpuzzle/viddy-mcp
|
|
2
|
+
|
|
3
|
+
The Model Context Protocol server for [Viddy](https://viddy.lol).
|
|
4
|
+
|
|
5
|
+
Ask Claude about your screen recordings: pull transcripts, find the moment you
|
|
6
|
+
said something, and read the timestamped notes people left on a share link.
|
|
7
|
+
|
|
8
|
+
> If you're on **claude.ai**, you don't need this package — add
|
|
9
|
+
> `https://viddy.lol/mcp` as a Connector instead.
|
|
10
|
+
> See [viddy.lol/install/mcp](https://viddy.lol/install/mcp).
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm install -g @unpuzzle/viddy-mcp
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Then register it with your MCP client:
|
|
19
|
+
|
|
20
|
+
### Claude Code
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
claude mcp add viddy -- viddy-mcp
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Cursor / Claude Desktop
|
|
27
|
+
|
|
28
|
+
Add this to your MCP config (usually `~/.cursor/mcp.json` or your client's
|
|
29
|
+
settings file):
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"mcpServers": {
|
|
34
|
+
"viddy": {
|
|
35
|
+
"command": "viddy-mcp"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## First-run sign-in
|
|
42
|
+
|
|
43
|
+
The first time Claude calls a tool that needs your account, the MCP opens your
|
|
44
|
+
browser to the Viddy consent screen. Sign in, click **Allow**, and you're done.
|
|
45
|
+
Tokens are saved locally and refreshed automatically:
|
|
46
|
+
|
|
47
|
+
- macOS / Linux: `~/.config/viddy-mcp/auth.json`
|
|
48
|
+
- Windows: `%APPDATA%\viddy-mcp\auth.json`
|
|
49
|
+
|
|
50
|
+
The file is written `chmod 600` (owner-only). Nothing is stored anywhere else,
|
|
51
|
+
and the package never sees your Google password.
|
|
52
|
+
|
|
53
|
+
To revoke access later, visit
|
|
54
|
+
[Settings → Connected Apps](https://viddy.lol/settings/connected-apps) on
|
|
55
|
+
viddy.lol. To just forget the credentials on this machine, ask Claude to
|
|
56
|
+
disconnect.
|
|
57
|
+
|
|
58
|
+
## Tools
|
|
59
|
+
|
|
60
|
+
Claude picks the right tool from what you ask — you don't need the names.
|
|
61
|
+
|
|
62
|
+
**Videos** — `list_videos`, `get_video`
|
|
63
|
+
|
|
64
|
+
**Transcripts** — `get_transcript`, `search_transcripts`
|
|
65
|
+
|
|
66
|
+
**Feedback** — `list_notes`
|
|
67
|
+
|
|
68
|
+
**Connection** — `ping`, `connect`, `disconnect`, `whoami`
|
|
69
|
+
|
|
70
|
+
## What you can ask
|
|
71
|
+
|
|
72
|
+
- "What videos did I record this week?"
|
|
73
|
+
- "Summarize my latest Viddy recording."
|
|
74
|
+
- "When did I talk about pricing in my recordings?"
|
|
75
|
+
- "What feedback did people leave on the onboarding walkthrough?"
|
|
76
|
+
- "Pull the transcript between 10 and 20 minutes of that demo and turn it into release notes."
|
|
77
|
+
|
|
78
|
+
Transcripts come from the Viddy desktop app, which transcribes on-device after
|
|
79
|
+
a recording finishes. Videos uploaded through the web app aren't transcribed,
|
|
80
|
+
so `get_transcript` returns status `none` for those.
|
|
81
|
+
|
|
82
|
+
## Permissions
|
|
83
|
+
|
|
84
|
+
The MCP requests three read-only scopes and nothing else:
|
|
85
|
+
|
|
86
|
+
| Scope | What it reads |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| `videos.read` | Your video list and metadata |
|
|
89
|
+
| `transcripts.read` | Transcript text and timestamps |
|
|
90
|
+
| `annotations.read` | Timestamped notes on your videos |
|
|
91
|
+
|
|
92
|
+
There is no write scope. This package cannot record, delete, rename, or
|
|
93
|
+
re-share anything.
|
|
94
|
+
|
|
95
|
+
## Pointing at a different Viddy
|
|
96
|
+
|
|
97
|
+
Set `VIDDY_BASE_URL` to target a non-production instance. Credentials are
|
|
98
|
+
stored per base URL, so a local dev connection won't disturb your production
|
|
99
|
+
one.
|
|
100
|
+
|
|
101
|
+
```sh
|
|
102
|
+
VIDDY_BASE_URL=http://localhost:3140 viddy-mcp
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Troubleshooting
|
|
106
|
+
|
|
107
|
+
**"Not connected to Viddy"** — the grant was revoked or the credentials file
|
|
108
|
+
was removed. Ask Claude to connect again.
|
|
109
|
+
|
|
110
|
+
**A tool says it needs a scope you don't have** — the scope list grew since you
|
|
111
|
+
connected. Revoke the grant under Settings → Connected Apps, then reconnect to
|
|
112
|
+
consent to the new list.
|
|
113
|
+
|
|
114
|
+
**The browser never opens** — the consent URL is also printed to stderr; paste
|
|
115
|
+
it in yourself. Set `VIDDY_BROWSER_CMD` to override the browser command.
|
|
116
|
+
|
|
117
|
+
## License
|
|
118
|
+
|
|
119
|
+
MIT. See `LICENSE`.
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authenticated HTTP helper for the Viddy API, and the ApiClient that
|
|
3
|
+
* @viddy/mcp-core tools run against.
|
|
4
|
+
*
|
|
5
|
+
* apiFetch(path, init?):
|
|
6
|
+
* 1. Load stored tokens; with none, run the OAuth flow (first-run consent).
|
|
7
|
+
* 2. Refresh proactively when the access token is near expiry.
|
|
8
|
+
* 3. Send `Authorization: Bearer <access_token>`.
|
|
9
|
+
* 4. On 401, refresh once and replay the request.
|
|
10
|
+
* 5. On 403 insufficient_scope, explain how to re-consent.
|
|
11
|
+
* 6. Return parsed JSON, or throw with a message worth showing the user.
|
|
12
|
+
*/
|
|
13
|
+
import type { ApiClient } from "@viddy/mcp-core";
|
|
14
|
+
export interface ApiOptions extends Omit<RequestInit, "headers"> {
|
|
15
|
+
/** Surface "not connected" instead of opening a browser. */
|
|
16
|
+
noAutoAuth?: boolean;
|
|
17
|
+
headers?: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
export declare class ApiError extends Error {
|
|
20
|
+
status: number;
|
|
21
|
+
body: unknown;
|
|
22
|
+
constructor(message: string, status: number, body: unknown);
|
|
23
|
+
}
|
|
24
|
+
export declare class NotConnectedError extends Error {
|
|
25
|
+
constructor();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Make an authenticated request. `path` is rooted at the Viddy origin
|
|
29
|
+
* (e.g. "/api/v1/videos?limit=5"). Returns the parsed JSON body.
|
|
30
|
+
*/
|
|
31
|
+
export declare function apiFetch<T = unknown>(path: string, init?: ApiOptions): Promise<T>;
|
|
32
|
+
/** The ApiClient handed to @viddy/mcp-core tool handlers. */
|
|
33
|
+
export declare const apiClient: ApiClient;
|
|
34
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAWH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,WAAW,UAAW,SAAQ,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC;IAC9D,4DAA4D;IAC5D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,qBAAa,QAAS,SAAQ,KAAK;IAGxB,MAAM,EAAE,MAAM;IACd,IAAI,EAAE,OAAO;gBAFpB,OAAO,EAAE,MAAM,EACR,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO;CAKvB;AAED,qBAAa,iBAAkB,SAAQ,KAAK;;CAO3C;AA0BD;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,UAAe,GAAG,OAAO,CAAC,CAAC,CAAC,CAgE3F;AAED,6DAA6D;AAC7D,eAAO,MAAM,SAAS,EAAE,SAGvB,CAAC"}
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authenticated HTTP helper for the Viddy API, and the ApiClient that
|
|
3
|
+
* @viddy/mcp-core tools run against.
|
|
4
|
+
*
|
|
5
|
+
* apiFetch(path, init?):
|
|
6
|
+
* 1. Load stored tokens; with none, run the OAuth flow (first-run consent).
|
|
7
|
+
* 2. Refresh proactively when the access token is near expiry.
|
|
8
|
+
* 3. Send `Authorization: Bearer <access_token>`.
|
|
9
|
+
* 4. On 401, refresh once and replay the request.
|
|
10
|
+
* 5. On 403 insufficient_scope, explain how to re-consent.
|
|
11
|
+
* 6. Return parsed JSON, or throw with a message worth showing the user.
|
|
12
|
+
*/
|
|
13
|
+
import { getBaseUrl } from "./config.js";
|
|
14
|
+
import { accessTokenIsFresh, clearTokens, loadTokens, } from "./auth.js";
|
|
15
|
+
import { refreshTokens, runAuthorizationFlow } from "./oauth.js";
|
|
16
|
+
import { log } from "./log.js";
|
|
17
|
+
export class ApiError extends Error {
|
|
18
|
+
status;
|
|
19
|
+
body;
|
|
20
|
+
constructor(message, status, body) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.status = status;
|
|
23
|
+
this.body = body;
|
|
24
|
+
this.name = "ApiError";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class NotConnectedError extends Error {
|
|
28
|
+
constructor() {
|
|
29
|
+
super("Not connected to Viddy. Run the `connect` tool to sign in — or just ask for your videos again and consent will open in your browser.");
|
|
30
|
+
this.name = "NotConnectedError";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function ensureTokens(opts) {
|
|
34
|
+
let tokens = await loadTokens();
|
|
35
|
+
if (!tokens) {
|
|
36
|
+
if (opts.noAutoAuth)
|
|
37
|
+
throw new NotConnectedError();
|
|
38
|
+
log("info", "no tokens found; starting OAuth flow");
|
|
39
|
+
tokens = await runAuthorizationFlow();
|
|
40
|
+
}
|
|
41
|
+
if (!accessTokenIsFresh(tokens)) {
|
|
42
|
+
log("info", "access token near expiry; refreshing");
|
|
43
|
+
try {
|
|
44
|
+
tokens = await refreshTokens(tokens.refreshToken);
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
// Usually means the grant was revoked. Drop the dead tokens, re-consent.
|
|
48
|
+
log("warn", "refresh failed; clearing tokens", {
|
|
49
|
+
error: e instanceof Error ? e.message : String(e),
|
|
50
|
+
});
|
|
51
|
+
await clearTokens();
|
|
52
|
+
if (opts.noAutoAuth)
|
|
53
|
+
throw new NotConnectedError();
|
|
54
|
+
tokens = await runAuthorizationFlow();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return tokens;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Make an authenticated request. `path` is rooted at the Viddy origin
|
|
61
|
+
* (e.g. "/api/v1/videos?limit=5"). Returns the parsed JSON body.
|
|
62
|
+
*/
|
|
63
|
+
export async function apiFetch(path, init = {}) {
|
|
64
|
+
const { noAutoAuth, headers: extraHeaders, ...rest } = init;
|
|
65
|
+
const tokens = await ensureTokens({ noAutoAuth: noAutoAuth ?? false });
|
|
66
|
+
const doRequest = (token) => fetch(`${getBaseUrl()}${path}`, {
|
|
67
|
+
...rest,
|
|
68
|
+
headers: {
|
|
69
|
+
...extraHeaders,
|
|
70
|
+
Authorization: `Bearer ${token}`,
|
|
71
|
+
Accept: "application/json",
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
let res = await doRequest(tokens.accessToken);
|
|
75
|
+
// 401 → one refresh + replay. A second 401 is a real failure, not a stale token.
|
|
76
|
+
if (res.status === 401) {
|
|
77
|
+
log("info", "got 401; attempting refresh + replay");
|
|
78
|
+
let refreshed;
|
|
79
|
+
try {
|
|
80
|
+
refreshed = await refreshTokens(tokens.refreshToken);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
await clearTokens();
|
|
84
|
+
throw new NotConnectedError();
|
|
85
|
+
}
|
|
86
|
+
res = await doRequest(refreshed.accessToken);
|
|
87
|
+
}
|
|
88
|
+
if (!res.ok) {
|
|
89
|
+
let body = null;
|
|
90
|
+
try {
|
|
91
|
+
body = await res.json();
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
body = await res.text().catch(() => null);
|
|
95
|
+
}
|
|
96
|
+
if (res.status === 403 && typeof body === "object" && body !== null && "reason" in body) {
|
|
97
|
+
const b = body;
|
|
98
|
+
if (b.reason === "insufficient_scope") {
|
|
99
|
+
throw new ApiError(`This tool needs the "${b.required_scope}" scope, which the current connection doesn't have. ` +
|
|
100
|
+
"Revoke Viddy MCP under Settings → Connected Apps on viddy.lol, then run `connect` again to grant it.", 403, body);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Lead with the human sentence (error_description) — Claude shows this to
|
|
104
|
+
// the user. The machine code only appears when there is no description.
|
|
105
|
+
const code = typeof body === "object" && body !== null && "error" in body
|
|
106
|
+
? String(body.error)
|
|
107
|
+
: `HTTP ${res.status}`;
|
|
108
|
+
const description = typeof body === "object" && body !== null && "error_description" in body
|
|
109
|
+
? String(body.error_description)
|
|
110
|
+
: "";
|
|
111
|
+
throw new ApiError(description || `${code} (${res.status})`, res.status, body);
|
|
112
|
+
}
|
|
113
|
+
if (res.status === 204)
|
|
114
|
+
return undefined;
|
|
115
|
+
return (await res.json());
|
|
116
|
+
}
|
|
117
|
+
/** The ApiClient handed to @viddy/mcp-core tool handlers. */
|
|
118
|
+
export const apiClient = {
|
|
119
|
+
fetch: (path, init) => apiFetch(path, init),
|
|
120
|
+
};
|
|
121
|
+
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token storage — load/save the OAuth tokens to disk.
|
|
3
|
+
*
|
|
4
|
+
* Storage path (env-paths, so it's right on every platform):
|
|
5
|
+
* macOS/Linux: ~/.config/viddy-mcp/auth.json
|
|
6
|
+
* Windows: %APPDATA%\viddy-mcp\auth.json
|
|
7
|
+
*
|
|
8
|
+
* The file holds a bearer token for the user's Viddy account, so it is written
|
|
9
|
+
* chmod 600. On Windows, user-profile ACLs already make it owner-only and
|
|
10
|
+
* POSIX modes are meaningless, so chmod failures are ignored.
|
|
11
|
+
*
|
|
12
|
+
* One file holds tokens per base URL, so someone pointing VIDDY_BASE_URL at a
|
|
13
|
+
* local dev server does not clobber their production connection.
|
|
14
|
+
*/
|
|
15
|
+
export interface StoredTokens {
|
|
16
|
+
accessToken: string;
|
|
17
|
+
refreshToken: string;
|
|
18
|
+
/** Unix ms at which the access token expires. */
|
|
19
|
+
expiresAt: number;
|
|
20
|
+
/** Granted scopes, space-separated, as returned by /api/oauth/token. */
|
|
21
|
+
scope: string;
|
|
22
|
+
/** When the grant was first issued (ms). */
|
|
23
|
+
grantedAt: number;
|
|
24
|
+
}
|
|
25
|
+
/** Tokens for the current base URL, or null if not connected. */
|
|
26
|
+
export declare function loadTokens(): Promise<StoredTokens | null>;
|
|
27
|
+
/** Save tokens for the current base URL, preserving entries for other hosts. */
|
|
28
|
+
export declare function saveTokens(tokens: StoredTokens): Promise<void>;
|
|
29
|
+
/** Forget tokens for the current base URL. Does not revoke server-side. */
|
|
30
|
+
export declare function clearTokens(): Promise<void>;
|
|
31
|
+
/** Where the auth file lives — surfaced in help/debug output. */
|
|
32
|
+
export declare function authFilePath(): string;
|
|
33
|
+
/** Is the access token still usable? 30s safety margin for clock skew + flight time. */
|
|
34
|
+
export declare function accessTokenIsFresh(tokens: StoredTokens): boolean;
|
|
35
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAcH,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;CACnB;AA+CD,iEAAiE;AACjE,wBAAsB,UAAU,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAI/D;AAED,gFAAgF;AAChF,wBAAsB,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAIpE;AAED,2EAA2E;AAC3E,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAKjD;AAED,iEAAiE;AACjE,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wFAAwF;AACxF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAEhE"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token storage — load/save the OAuth tokens to disk.
|
|
3
|
+
*
|
|
4
|
+
* Storage path (env-paths, so it's right on every platform):
|
|
5
|
+
* macOS/Linux: ~/.config/viddy-mcp/auth.json
|
|
6
|
+
* Windows: %APPDATA%\viddy-mcp\auth.json
|
|
7
|
+
*
|
|
8
|
+
* The file holds a bearer token for the user's Viddy account, so it is written
|
|
9
|
+
* chmod 600. On Windows, user-profile ACLs already make it owner-only and
|
|
10
|
+
* POSIX modes are meaningless, so chmod failures are ignored.
|
|
11
|
+
*
|
|
12
|
+
* One file holds tokens per base URL, so someone pointing VIDDY_BASE_URL at a
|
|
13
|
+
* local dev server does not clobber their production connection.
|
|
14
|
+
*/
|
|
15
|
+
import { mkdir, readFile, writeFile, chmod, rename, unlink } from "node:fs/promises";
|
|
16
|
+
import { dirname, join } from "node:path";
|
|
17
|
+
import envPaths from "env-paths";
|
|
18
|
+
import { getBaseUrl } from "./config.js";
|
|
19
|
+
import { log } from "./log.js";
|
|
20
|
+
const PATHS = envPaths("viddy-mcp", { suffix: "" });
|
|
21
|
+
const AUTH_PATH = join(PATHS.config, "auth.json");
|
|
22
|
+
async function readFileOrNull() {
|
|
23
|
+
try {
|
|
24
|
+
const parsed = JSON.parse(await readFile(AUTH_PATH, "utf8"));
|
|
25
|
+
if (parsed?.schemaVersion !== 1) {
|
|
26
|
+
log("warn", "auth.json schemaVersion mismatch; ignoring stored tokens", {
|
|
27
|
+
path: AUTH_PATH,
|
|
28
|
+
});
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return parsed;
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
if (e?.code === "ENOENT")
|
|
35
|
+
return null;
|
|
36
|
+
log("warn", "failed to read auth.json; treating as no stored tokens", {
|
|
37
|
+
path: AUTH_PATH,
|
|
38
|
+
error: e instanceof Error ? e.message : String(e),
|
|
39
|
+
});
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Write via temp file + rename so a crash mid-write can't leave a truncated
|
|
45
|
+
* auth.json. chmod happens on the temp file, before it is visible under the
|
|
46
|
+
* real name, so the token is never briefly world-readable.
|
|
47
|
+
*/
|
|
48
|
+
async function writeAtomic(file) {
|
|
49
|
+
await mkdir(dirname(AUTH_PATH), { recursive: true, mode: 0o700 });
|
|
50
|
+
const tmp = `${AUTH_PATH}.${process.pid}.tmp`;
|
|
51
|
+
try {
|
|
52
|
+
await writeFile(tmp, JSON.stringify(file, null, 2), { encoding: "utf8", mode: 0o600 });
|
|
53
|
+
try {
|
|
54
|
+
await chmod(tmp, 0o600);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Best-effort: Windows has no POSIX modes.
|
|
58
|
+
}
|
|
59
|
+
await rename(tmp, AUTH_PATH);
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
await unlink(tmp).catch(() => { });
|
|
63
|
+
throw e;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Tokens for the current base URL, or null if not connected. */
|
|
67
|
+
export async function loadTokens() {
|
|
68
|
+
const file = await readFileOrNull();
|
|
69
|
+
if (!file)
|
|
70
|
+
return null;
|
|
71
|
+
return file.byBaseUrl[getBaseUrl()] ?? null;
|
|
72
|
+
}
|
|
73
|
+
/** Save tokens for the current base URL, preserving entries for other hosts. */
|
|
74
|
+
export async function saveTokens(tokens) {
|
|
75
|
+
const file = (await readFileOrNull()) ?? { schemaVersion: 1, byBaseUrl: {} };
|
|
76
|
+
file.byBaseUrl[getBaseUrl()] = tokens;
|
|
77
|
+
await writeAtomic(file);
|
|
78
|
+
}
|
|
79
|
+
/** Forget tokens for the current base URL. Does not revoke server-side. */
|
|
80
|
+
export async function clearTokens() {
|
|
81
|
+
const file = await readFileOrNull();
|
|
82
|
+
if (!file)
|
|
83
|
+
return;
|
|
84
|
+
delete file.byBaseUrl[getBaseUrl()];
|
|
85
|
+
await writeAtomic(file);
|
|
86
|
+
}
|
|
87
|
+
/** Where the auth file lives — surfaced in help/debug output. */
|
|
88
|
+
export function authFilePath() {
|
|
89
|
+
return AUTH_PATH;
|
|
90
|
+
}
|
|
91
|
+
/** Is the access token still usable? 30s safety margin for clock skew + flight time. */
|
|
92
|
+
export function accessTokenIsFresh(tokens) {
|
|
93
|
+
return tokens.expiresAt - Date.now() > 30_000;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile-time configuration for the Viddy MCP server.
|
|
3
|
+
*
|
|
4
|
+
* CLIENT_ID is a public, hardcoded value that must match the `viddy-mcp` row
|
|
5
|
+
* seeded into the oauth_client table by the Viddy web app's migration. It is
|
|
6
|
+
* not a secret — this is a public PKCE client (RFC 8252), where the proof of
|
|
7
|
+
* possession is the code verifier, not a client secret.
|
|
8
|
+
*/
|
|
9
|
+
export declare const CLIENT_ID = "viddy-mcp";
|
|
10
|
+
export declare const DEFAULT_BASE_URL = "https://viddy.lol";
|
|
11
|
+
/** Resolve the Viddy base URL: VIDDY_BASE_URL override → production default. */
|
|
12
|
+
export declare function getBaseUrl(): string;
|
|
13
|
+
/**
|
|
14
|
+
* Scopes requested at install. Must be a subset of the scopes registered on
|
|
15
|
+
* the oauth_client row. Changing this list means existing users have to
|
|
16
|
+
* re-consent on their next connect.
|
|
17
|
+
*/
|
|
18
|
+
export declare const REQUESTED_SCOPES: readonly ["videos.read", "transcripts.read", "annotations.read"];
|
|
19
|
+
export declare const PACKAGE_NAME = "@unpuzzle/viddy-mcp";
|
|
20
|
+
/** Sent in the MCP `serverInfo`. Keep in sync with package.json `version`. */
|
|
21
|
+
export declare const PACKAGE_VERSION = "0.1.0";
|
|
22
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,eAAO,MAAM,SAAS,cAAc,CAAC;AAErC,eAAO,MAAM,gBAAgB,sBAAsB,CAAC;AAEpD,gFAAgF;AAChF,wBAAgB,UAAU,IAAI,MAAM,CAInC;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,kEAInB,CAAC;AAEX,eAAO,MAAM,YAAY,wBAAwB,CAAC;AAElD,8EAA8E;AAC9E,eAAO,MAAM,eAAe,UAAU,CAAC"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile-time configuration for the Viddy MCP server.
|
|
3
|
+
*
|
|
4
|
+
* CLIENT_ID is a public, hardcoded value that must match the `viddy-mcp` row
|
|
5
|
+
* seeded into the oauth_client table by the Viddy web app's migration. It is
|
|
6
|
+
* not a secret — this is a public PKCE client (RFC 8252), where the proof of
|
|
7
|
+
* possession is the code verifier, not a client secret.
|
|
8
|
+
*/
|
|
9
|
+
export const CLIENT_ID = "viddy-mcp";
|
|
10
|
+
export const DEFAULT_BASE_URL = "https://viddy.lol";
|
|
11
|
+
/** Resolve the Viddy base URL: VIDDY_BASE_URL override → production default. */
|
|
12
|
+
export function getBaseUrl() {
|
|
13
|
+
const fromEnv = process.env["VIDDY_BASE_URL"];
|
|
14
|
+
if (fromEnv && fromEnv.length > 0)
|
|
15
|
+
return fromEnv.replace(/\/+$/, "");
|
|
16
|
+
return DEFAULT_BASE_URL;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Scopes requested at install. Must be a subset of the scopes registered on
|
|
20
|
+
* the oauth_client row. Changing this list means existing users have to
|
|
21
|
+
* re-consent on their next connect.
|
|
22
|
+
*/
|
|
23
|
+
export const REQUESTED_SCOPES = [
|
|
24
|
+
"videos.read",
|
|
25
|
+
"transcripts.read",
|
|
26
|
+
"annotations.read",
|
|
27
|
+
];
|
|
28
|
+
export const PACKAGE_NAME = "@unpuzzle/viddy-mcp";
|
|
29
|
+
/** Sent in the MCP `serverInfo`. Keep in sync with package.json `version`. */
|
|
30
|
+
export const PACKAGE_VERSION = "0.1.0";
|
|
31
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,CAAC,MAAM,SAAS,GAAG,WAAW,CAAC;AAErC,MAAM,CAAC,MAAM,gBAAgB,GAAG,mBAAmB,CAAC;AAEpD,gFAAgF;AAChF,MAAM,UAAU,UAAU;IACxB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC9C,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACtE,OAAO,gBAAgB,CAAC;AAC1B,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,aAAa;IACb,kBAAkB;IAClB,kBAAkB;CACV,CAAC;AAEX,MAAM,CAAC,MAAM,YAAY,GAAG,qBAAqB,CAAC;AAElD,8EAA8E;AAC9E,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @unpuzzle/viddy-mcp — MCP server for Viddy, spoken over stdio.
|
|
4
|
+
*
|
|
5
|
+
* The host (Claude Code, Claude Desktop, Cursor) spawns this process and talks
|
|
6
|
+
* to it on stdin/stdout. Every diagnostic goes to stderr via ./log.js, because
|
|
7
|
+
* anything on stdout corrupts the protocol framing.
|
|
8
|
+
*
|
|
9
|
+
* Tool definitions live in @viddy/mcp-core so the hosted /mcp endpoint on
|
|
10
|
+
* viddy.lol serves exactly the same surface. The three connection tools below
|
|
11
|
+
* (connect/disconnect/whoami) are package-only: they manage tokens on this
|
|
12
|
+
* machine, which a remote Connector has no equivalent of.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* npm install -g @unpuzzle/viddy-mcp
|
|
16
|
+
* claude mcp add viddy -- viddy-mcp
|
|
17
|
+
*/
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;GAeG"}
|