@liorium/youtube-omni-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/.env.example +11 -0
- package/README.md +86 -0
- package/dist/src/config.d.ts +10 -0
- package/dist/src/config.js +15 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/core/envelope.d.ts +40 -0
- package/dist/src/core/envelope.js +31 -0
- package/dist/src/core/envelope.js.map +1 -0
- package/dist/src/core/errors.d.ts +44 -0
- package/dist/src/core/errors.js +25 -0
- package/dist/src/core/errors.js.map +1 -0
- package/dist/src/core/provider-registry.d.ts +6 -0
- package/dist/src/core/provider-registry.js +14 -0
- package/dist/src/core/provider-registry.js.map +1 -0
- package/dist/src/core/quota.d.ts +2 -0
- package/dist/src/core/quota.js +15 -0
- package/dist/src/core/quota.js.map +1 -0
- package/dist/src/core/redaction.d.ts +1 -0
- package/dist/src/core/redaction.js +23 -0
- package/dist/src/core/redaction.js.map +1 -0
- package/dist/src/core/risk.d.ts +9 -0
- package/dist/src/core/risk.js +31 -0
- package/dist/src/core/risk.js.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +50 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/providers/mock-provider.d.ts +150 -0
- package/dist/src/providers/mock-provider.js +61 -0
- package/dist/src/providers/mock-provider.js.map +1 -0
- package/dist/src/providers/transcript-provider.d.ts +9 -0
- package/dist/src/providers/transcript-provider.js +9 -0
- package/dist/src/providers/transcript-provider.js.map +1 -0
- package/dist/src/providers/types.d.ts +85 -0
- package/dist/src/providers/types.js +2 -0
- package/dist/src/providers/types.js.map +1 -0
- package/dist/src/providers/youtube-data-api.d.ts +11 -0
- package/dist/src/providers/youtube-data-api.js +13 -0
- package/dist/src/providers/youtube-data-api.js.map +1 -0
- package/dist/src/schemas/channel.d.ts +19 -0
- package/dist/src/schemas/channel.js +11 -0
- package/dist/src/schemas/channel.js.map +1 -0
- package/dist/src/schemas/comments.d.ts +20 -0
- package/dist/src/schemas/comments.js +16 -0
- package/dist/src/schemas/comments.js.map +1 -0
- package/dist/src/schemas/common.d.ts +11 -0
- package/dist/src/schemas/common.js +8 -0
- package/dist/src/schemas/common.js.map +1 -0
- package/dist/src/schemas/discovery.d.ts +58 -0
- package/dist/src/schemas/discovery.js +23 -0
- package/dist/src/schemas/discovery.js.map +1 -0
- package/dist/src/schemas/index.d.ts +7 -0
- package/dist/src/schemas/index.js +8 -0
- package/dist/src/schemas/index.js.map +1 -0
- package/dist/src/schemas/planning.d.ts +9 -0
- package/dist/src/schemas/planning.js +11 -0
- package/dist/src/schemas/planning.js.map +1 -0
- package/dist/src/schemas/transcript.d.ts +18 -0
- package/dist/src/schemas/transcript.js +10 -0
- package/dist/src/schemas/transcript.js.map +1 -0
- package/dist/src/schemas/video.d.ts +14 -0
- package/dist/src/schemas/video.js +9 -0
- package/dist/src/schemas/video.js.map +1 -0
- package/dist/src/server.d.ts +78 -0
- package/dist/src/server.js +47 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/tools/channel.d.ts +8 -0
- package/dist/src/tools/channel.js +15 -0
- package/dist/src/tools/channel.js.map +1 -0
- package/dist/src/tools/comments.d.ts +15 -0
- package/dist/src/tools/comments.js +15 -0
- package/dist/src/tools/comments.js.map +1 -0
- package/dist/src/tools/discovery.d.ts +8 -0
- package/dist/src/tools/discovery.js +15 -0
- package/dist/src/tools/discovery.js.map +1 -0
- package/dist/src/tools/planning.d.ts +13 -0
- package/dist/src/tools/planning.js +9 -0
- package/dist/src/tools/planning.js.map +1 -0
- package/dist/src/tools/safety.d.ts +10 -0
- package/dist/src/tools/safety.js +10 -0
- package/dist/src/tools/safety.js.map +1 -0
- package/dist/src/tools/transcript.d.ts +17 -0
- package/dist/src/tools/transcript.js +21 -0
- package/dist/src/tools/transcript.js.map +1 -0
- package/dist/src/tools/video.d.ts +10 -0
- package/dist/src/tools/video.js +16 -0
- package/dist/src/tools/video.js.map +1 -0
- package/docs/architecture.md +11 -0
- package/docs/provider-capability-map.md +5 -0
- package/docs/safety-policy.md +5 -0
- package/docs/tool-schema.md +5 -0
- package/docs/v0-spec.md +5 -0
- package/package.json +51 -0
package/.env.example
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Optional for public YouTube Data API reads
|
|
2
|
+
YOUTUBE_API_KEY=
|
|
3
|
+
|
|
4
|
+
# Optional cache/outlier provider, disabled by default
|
|
5
|
+
YOUTUBE_OMNI_ENABLE_OUTLIER=false
|
|
6
|
+
YOUTUBE_OMNI_MONGO_URI=
|
|
7
|
+
|
|
8
|
+
# Safety defaults
|
|
9
|
+
YOUTUBE_OMNI_MAX_RESULTS=50
|
|
10
|
+
YOUTUBE_OMNI_ENABLE_WRITE_TOOLS=false
|
|
11
|
+
YOUTUBE_OMNI_ENABLE_DOWNLOAD_TOOLS=false
|
package/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# YouTube Omni MCP
|
|
2
|
+
|
|
3
|
+
`youtube-omni` is a full-scope YouTube Creator Intelligence + Operations + Analytics + Production MCP project.
|
|
4
|
+
|
|
5
|
+
Current implementation phase: **v0 read-only foundation**.
|
|
6
|
+
|
|
7
|
+
## v0 boundary
|
|
8
|
+
|
|
9
|
+
Enabled in v0:
|
|
10
|
+
|
|
11
|
+
- public discovery/search
|
|
12
|
+
- video details/comparison
|
|
13
|
+
- channel statistics/top videos
|
|
14
|
+
- transcript segments/full text/structure analysis
|
|
15
|
+
- comments read/summarize
|
|
16
|
+
- research brief drafts
|
|
17
|
+
- safety/capability reporting
|
|
18
|
+
|
|
19
|
+
Locked for later phases, but part of final scope:
|
|
20
|
+
|
|
21
|
+
- upload/scheduling
|
|
22
|
+
- metadata/thumbnail/playlist writes
|
|
23
|
+
- comments/community writes
|
|
24
|
+
- YouTube Analytics/Reporting/revenue/retention
|
|
25
|
+
- yt-dlp download/clip/audio/frame extraction
|
|
26
|
+
- Shorts production
|
|
27
|
+
- YouTube Music private/library features
|
|
28
|
+
- governed automation
|
|
29
|
+
|
|
30
|
+
## Safety defaults
|
|
31
|
+
|
|
32
|
+
- No write tools are exposed in v0.
|
|
33
|
+
- No download tools are exposed in v0.
|
|
34
|
+
- No Hermes MCP registration is performed by this repository setup.
|
|
35
|
+
- Secrets must be supplied via environment variables and are never committed.
|
|
36
|
+
|
|
37
|
+
## Development
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install
|
|
41
|
+
npm test
|
|
42
|
+
npm run build
|
|
43
|
+
npm run validate:v0
|
|
44
|
+
npm run validate:secrets
|
|
45
|
+
npm run smoke:mcp
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Environment
|
|
49
|
+
|
|
50
|
+
See `.env.example`.
|
|
51
|
+
|
|
52
|
+
## Use with npx / Hermes MCP
|
|
53
|
+
|
|
54
|
+
The package is intended to run as a stdio MCP server via `npx`:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npx -y @liorium/youtube-omni-mcp
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Hermes config example:
|
|
61
|
+
|
|
62
|
+
```yaml
|
|
63
|
+
mcp_servers:
|
|
64
|
+
youtube-omni:
|
|
65
|
+
command: "npx"
|
|
66
|
+
args:
|
|
67
|
+
- "-y"
|
|
68
|
+
- "@liorium/youtube-omni-mcp"
|
|
69
|
+
timeout: 300
|
|
70
|
+
connect_timeout: 60
|
|
71
|
+
env:
|
|
72
|
+
YOUTUBE_API_KEY: "..."
|
|
73
|
+
YOUTUBE_OMNI_ENABLE_WRITE_TOOLS: "false"
|
|
74
|
+
YOUTUBE_OMNI_ENABLE_DOWNLOAD_TOOLS: "false"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
For local development before publishing, use a packed tarball:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npm pack
|
|
81
|
+
npx -y ./liorium-youtube-omni-mcp-0.1.0.tgz
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Research and specs
|
|
85
|
+
|
|
86
|
+
All research/specification documents are under `research/`. Implementation docs copied for convenience are under `docs/`.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface YoutubeOmniConfig {
|
|
2
|
+
youtubeApiKey?: string;
|
|
3
|
+
enableOutlier: boolean;
|
|
4
|
+
mongoUri?: string;
|
|
5
|
+
maxResults: number;
|
|
6
|
+
enableWriteTools: boolean;
|
|
7
|
+
enableDownloadTools: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function loadConfig(env?: NodeJS.ProcessEnv): YoutubeOmniConfig;
|
|
10
|
+
export declare function redactedConfig(config: YoutubeOmniConfig): Record<string, unknown>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { redactSecrets } from './core/redaction.js';
|
|
2
|
+
export function loadConfig(env = process.env) {
|
|
3
|
+
return {
|
|
4
|
+
youtubeApiKey: env.YOUTUBE_API_KEY,
|
|
5
|
+
enableOutlier: env.YOUTUBE_OMNI_ENABLE_OUTLIER === 'true',
|
|
6
|
+
mongoUri: env.YOUTUBE_OMNI_MONGO_URI,
|
|
7
|
+
maxResults: Number(env.YOUTUBE_OMNI_MAX_RESULTS ?? 50),
|
|
8
|
+
enableWriteTools: env.YOUTUBE_OMNI_ENABLE_WRITE_TOOLS === 'true',
|
|
9
|
+
enableDownloadTools: env.YOUTUBE_OMNI_ENABLE_DOWNLOAD_TOOLS === 'true'
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export function redactedConfig(config) {
|
|
13
|
+
return JSON.parse(redactSecrets(JSON.stringify(config)));
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAWpD,MAAM,UAAU,UAAU,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC7D,OAAO;QACL,aAAa,EAAE,GAAG,CAAC,eAAe;QAClC,aAAa,EAAE,GAAG,CAAC,2BAA2B,KAAK,MAAM;QACzD,QAAQ,EAAE,GAAG,CAAC,sBAAsB;QACpC,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,wBAAwB,IAAI,EAAE,CAAC;QACtD,gBAAgB,EAAE,GAAG,CAAC,+BAA+B,KAAK,MAAM;QAChE,mBAAmB,EAAE,GAAG,CAAC,kCAAkC,KAAK,MAAM;KACvE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAyB;IACtD,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { YoutubeOmniErrorCode } from './errors.js';
|
|
2
|
+
export type AuthTier = 'none' | 'api_key' | 'oauth_readonly' | 'oauth_write' | 'hosted' | 'experimental';
|
|
3
|
+
export interface ToolEnvelope<T> {
|
|
4
|
+
ok: boolean;
|
|
5
|
+
tool: string;
|
|
6
|
+
timestamp: string;
|
|
7
|
+
source: {
|
|
8
|
+
provider: string;
|
|
9
|
+
authTier: AuthTier;
|
|
10
|
+
quotaCost?: number;
|
|
11
|
+
cacheHit?: boolean;
|
|
12
|
+
};
|
|
13
|
+
data?: T;
|
|
14
|
+
warnings: string[];
|
|
15
|
+
nextActions?: string[];
|
|
16
|
+
error?: {
|
|
17
|
+
code: YoutubeOmniErrorCode;
|
|
18
|
+
message: string;
|
|
19
|
+
retryable: boolean;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export declare function makeEnvelope<T>(input: {
|
|
23
|
+
tool: string;
|
|
24
|
+
provider: string;
|
|
25
|
+
authTier: AuthTier;
|
|
26
|
+
data: T;
|
|
27
|
+
warnings?: string[];
|
|
28
|
+
nextActions?: string[];
|
|
29
|
+
quotaCost?: number;
|
|
30
|
+
cacheHit?: boolean;
|
|
31
|
+
}): ToolEnvelope<T>;
|
|
32
|
+
export declare function makeErrorEnvelope(input: {
|
|
33
|
+
tool: string;
|
|
34
|
+
provider: string;
|
|
35
|
+
authTier: AuthTier;
|
|
36
|
+
code: YoutubeOmniErrorCode;
|
|
37
|
+
message: string;
|
|
38
|
+
retryable: boolean;
|
|
39
|
+
warnings?: string[];
|
|
40
|
+
}): ToolEnvelope<never>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function makeEnvelope(input) {
|
|
2
|
+
return {
|
|
3
|
+
ok: true,
|
|
4
|
+
tool: input.tool,
|
|
5
|
+
timestamp: new Date().toISOString(),
|
|
6
|
+
source: {
|
|
7
|
+
provider: input.provider,
|
|
8
|
+
authTier: input.authTier,
|
|
9
|
+
quotaCost: input.quotaCost,
|
|
10
|
+
cacheHit: input.cacheHit
|
|
11
|
+
},
|
|
12
|
+
data: input.data,
|
|
13
|
+
warnings: input.warnings ?? [],
|
|
14
|
+
nextActions: input.nextActions ?? []
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export function makeErrorEnvelope(input) {
|
|
18
|
+
return {
|
|
19
|
+
ok: false,
|
|
20
|
+
tool: input.tool,
|
|
21
|
+
timestamp: new Date().toISOString(),
|
|
22
|
+
source: { provider: input.provider, authTier: input.authTier },
|
|
23
|
+
warnings: input.warnings ?? [],
|
|
24
|
+
error: {
|
|
25
|
+
code: input.code,
|
|
26
|
+
message: input.message,
|
|
27
|
+
retryable: input.retryable
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=envelope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.js","sourceRoot":"","sources":["../../../src/core/envelope.ts"],"names":[],"mappings":"AAwBA,MAAM,UAAU,YAAY,CAAI,KAS/B;IACC,OAAO;QACL,EAAE,EAAE,IAAI;QACR,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,MAAM,EAAE;YACN,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACzB;QACD,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,EAAE;QAC9B,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,EAAE;KACrC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAQjC;IACC,OAAO;QACL,EAAE,EAAE,KAAK;QACT,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE;QAC9D,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,EAAE;QAC9B,KAAK,EAAE;YACL,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,SAAS,EAAE,KAAK,CAAC,SAAS;SAC3B;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export declare const YOUTUBE_OMNI_ERRORS: {
|
|
2
|
+
readonly INVALID_ARGUMENT: {
|
|
3
|
+
readonly retryable: false;
|
|
4
|
+
};
|
|
5
|
+
readonly PROVIDER_NOT_CONFIGURED: {
|
|
6
|
+
readonly retryable: false;
|
|
7
|
+
};
|
|
8
|
+
readonly AUTH_REQUIRED: {
|
|
9
|
+
readonly retryable: false;
|
|
10
|
+
};
|
|
11
|
+
readonly QUOTA_EXCEEDED: {
|
|
12
|
+
readonly retryable: true;
|
|
13
|
+
};
|
|
14
|
+
readonly RATE_LIMITED: {
|
|
15
|
+
readonly retryable: true;
|
|
16
|
+
};
|
|
17
|
+
readonly VIDEO_NOT_FOUND: {
|
|
18
|
+
readonly retryable: false;
|
|
19
|
+
};
|
|
20
|
+
readonly CHANNEL_NOT_FOUND: {
|
|
21
|
+
readonly retryable: false;
|
|
22
|
+
};
|
|
23
|
+
readonly TRANSCRIPT_UNAVAILABLE: {
|
|
24
|
+
readonly retryable: false;
|
|
25
|
+
};
|
|
26
|
+
readonly COMMENTS_DISABLED: {
|
|
27
|
+
readonly retryable: false;
|
|
28
|
+
};
|
|
29
|
+
readonly UNSUPPORTED_REGION: {
|
|
30
|
+
readonly retryable: false;
|
|
31
|
+
};
|
|
32
|
+
readonly PROVIDER_ERROR: {
|
|
33
|
+
readonly retryable: true;
|
|
34
|
+
};
|
|
35
|
+
readonly SAFETY_BLOCKED: {
|
|
36
|
+
readonly retryable: false;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
export type YoutubeOmniErrorCode = keyof typeof YOUTUBE_OMNI_ERRORS;
|
|
40
|
+
export declare class YoutubeOmniError extends Error {
|
|
41
|
+
readonly code: YoutubeOmniErrorCode;
|
|
42
|
+
readonly retryable: boolean;
|
|
43
|
+
constructor(code: YoutubeOmniErrorCode, message: string);
|
|
44
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export const YOUTUBE_OMNI_ERRORS = {
|
|
2
|
+
INVALID_ARGUMENT: { retryable: false },
|
|
3
|
+
PROVIDER_NOT_CONFIGURED: { retryable: false },
|
|
4
|
+
AUTH_REQUIRED: { retryable: false },
|
|
5
|
+
QUOTA_EXCEEDED: { retryable: true },
|
|
6
|
+
RATE_LIMITED: { retryable: true },
|
|
7
|
+
VIDEO_NOT_FOUND: { retryable: false },
|
|
8
|
+
CHANNEL_NOT_FOUND: { retryable: false },
|
|
9
|
+
TRANSCRIPT_UNAVAILABLE: { retryable: false },
|
|
10
|
+
COMMENTS_DISABLED: { retryable: false },
|
|
11
|
+
UNSUPPORTED_REGION: { retryable: false },
|
|
12
|
+
PROVIDER_ERROR: { retryable: true },
|
|
13
|
+
SAFETY_BLOCKED: { retryable: false }
|
|
14
|
+
};
|
|
15
|
+
export class YoutubeOmniError extends Error {
|
|
16
|
+
code;
|
|
17
|
+
retryable;
|
|
18
|
+
constructor(code, message) {
|
|
19
|
+
super(`${code}: ${message}`);
|
|
20
|
+
this.name = 'YoutubeOmniError';
|
|
21
|
+
this.code = code;
|
|
22
|
+
this.retryable = YOUTUBE_OMNI_ERRORS[code].retryable;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/core/errors.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,gBAAgB,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;IACtC,uBAAuB,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;IAC7C,aAAa,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;IACnC,cAAc,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE;IACnC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE;IACjC,eAAe,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;IACrC,iBAAiB,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;IACvC,sBAAsB,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;IAC5C,iBAAiB,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;IACvC,kBAAkB,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;IACxC,cAAc,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE;IACnC,cAAc,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;CAC5B,CAAC;AAIX,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAChC,IAAI,CAAuB;IAC3B,SAAS,CAAU;IAE5B,YAAY,IAA0B,EAAE,OAAe;QACrD,KAAK,CAAC,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,SAAS,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC;IACvD,CAAC;CACF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { YoutubeOmniError } from './errors.js';
|
|
2
|
+
export class ProviderRegistry {
|
|
3
|
+
providers;
|
|
4
|
+
constructor(providers) {
|
|
5
|
+
this.providers = providers;
|
|
6
|
+
}
|
|
7
|
+
requireCapability(capability) {
|
|
8
|
+
const provider = this.providers.find((candidate) => candidate.isConfigured() && candidate.capabilities.includes(capability));
|
|
9
|
+
if (!provider)
|
|
10
|
+
throw new YoutubeOmniError('PROVIDER_NOT_CONFIGURED', `No provider configured for capability: ${capability}`);
|
|
11
|
+
return provider;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=provider-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-registry.js","sourceRoot":"","sources":["../../../src/core/provider-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C,MAAM,OAAO,gBAAgB;IAClB,SAAS,CAAwB;IAE1C,YAAY,SAAgC;QAC1C,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAED,iBAAiB,CAAC,UAAkB;QAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,SAAS,CAAC,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;QAC7H,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,gBAAgB,CAAC,yBAAyB,EAAE,0CAA0C,UAAU,EAAE,CAAC,CAAC;QAC7H,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function estimateQuotaCost(toolName) {
|
|
2
|
+
if (toolName.includes('search'))
|
|
3
|
+
return 100;
|
|
4
|
+
if (toolName.includes('get_video_categories'))
|
|
5
|
+
return 1;
|
|
6
|
+
if (toolName.includes('comments'))
|
|
7
|
+
return 1;
|
|
8
|
+
if (toolName.includes('transcript'))
|
|
9
|
+
return 0;
|
|
10
|
+
return 1;
|
|
11
|
+
}
|
|
12
|
+
export function enforceMaxResults(value, hardCap = 50) {
|
|
13
|
+
return Math.min(Math.max(1, value), hardCap);
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=quota.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quota.js","sourceRoot":"","sources":["../../../src/core/quota.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,IAAI,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,GAAG,CAAC;IAC5C,IAAI,QAAQ,CAAC,QAAQ,CAAC,sBAAsB,CAAC;QAAE,OAAO,CAAC,CAAC;IACxD,IAAI,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC;QAAE,OAAO,CAAC,CAAC;IAC5C,IAAI,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC;QAAE,OAAO,CAAC,CAAC;IAC9C,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAa,EAAE,OAAO,GAAG,EAAE;IAC3D,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function redactSecrets(value: string): string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const SECRET_PATTERNS = [
|
|
2
|
+
/AIza[0-9A-Za-z_\-]+/g,
|
|
3
|
+
/(Authorization:\s*Bearer\s+)[^\s;]+/gi,
|
|
4
|
+
/(Bearer\s+)[A-Za-z0-9._\-]+/g,
|
|
5
|
+
/(token\s*=\s*)[^\s;]+/gi,
|
|
6
|
+
/(key\s*=\s*)[^\s;]+/gi,
|
|
7
|
+
/(api[_-]?key\s*=\s*)[^\s;]+/gi,
|
|
8
|
+
/(password\s*=\s*)[^\s;]+/gi,
|
|
9
|
+
/(secret\s*=\s*)[^\s;]+/gi,
|
|
10
|
+
/(Cookie:\s*)[^\n]+/gi,
|
|
11
|
+
/([A-Z0-9_]*TOKEN[A-Z0-9_]*\s*=\s*)[^\s;]+/gi,
|
|
12
|
+
/(YOUTUBE_API_KEY\s*=\s*)[^\s;]+/gi
|
|
13
|
+
];
|
|
14
|
+
export function redactSecrets(value) {
|
|
15
|
+
return SECRET_PATTERNS.reduce((acc, pattern) => {
|
|
16
|
+
return acc.replace(pattern, (match, prefix) => {
|
|
17
|
+
if (typeof prefix === 'string' && match.startsWith(prefix))
|
|
18
|
+
return `${prefix}[REDACTED]`;
|
|
19
|
+
return '[REDACTED]';
|
|
20
|
+
});
|
|
21
|
+
}, value);
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=redaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redaction.js","sourceRoot":"","sources":["../../../src/core/redaction.ts"],"names":[],"mappings":"AAAA,MAAM,eAAe,GAAa;IAChC,sBAAsB;IACtB,uCAAuC;IACvC,8BAA8B;IAC9B,yBAAyB;IACzB,uBAAuB;IACvB,+BAA+B;IAC/B,4BAA4B;IAC5B,0BAA0B;IAC1B,sBAAsB;IACtB,6CAA6C;IAC7C,mCAAmC;CACpC,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,eAAe,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE;QAC7C,OAAO,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;YAC5C,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC;gBAAE,OAAO,GAAG,MAAM,YAAY,CAAC;YACzF,OAAO,YAAY,CAAC;QACtB,CAAC,CAAC,CAAC;IACL,CAAC,EAAE,KAAK,CAAC,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const V0_LOCKED_PATTERNS: readonly ["upload", "update_video", "delete", "set_thumbnail", "reply_to_comment", "moderate", "publish", "download_video", "clip_segment", "revenue"];
|
|
2
|
+
export interface ActionRisk {
|
|
3
|
+
riskTier: 0 | 1 | 2 | 3 | 4 | 5;
|
|
4
|
+
authTier: 'none' | 'api_key' | 'oauth_readonly' | 'oauth_write' | 'hosted' | 'experimental';
|
|
5
|
+
allowedInV0: boolean;
|
|
6
|
+
requiredControls: string[];
|
|
7
|
+
}
|
|
8
|
+
export declare function isAllowedInV0(action: string): boolean;
|
|
9
|
+
export declare function classifyActionRisk(action: string): ActionRisk;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const V0_LOCKED_PATTERNS = [
|
|
2
|
+
'upload',
|
|
3
|
+
'update_video',
|
|
4
|
+
'delete',
|
|
5
|
+
'set_thumbnail',
|
|
6
|
+
'reply_to_comment',
|
|
7
|
+
'moderate',
|
|
8
|
+
'publish',
|
|
9
|
+
'download_video',
|
|
10
|
+
'clip_segment',
|
|
11
|
+
'revenue'
|
|
12
|
+
];
|
|
13
|
+
export function isAllowedInV0(action) {
|
|
14
|
+
return !V0_LOCKED_PATTERNS.some((pattern) => action.includes(pattern));
|
|
15
|
+
}
|
|
16
|
+
export function classifyActionRisk(action) {
|
|
17
|
+
if (!isAllowedInV0(action)) {
|
|
18
|
+
const destructive = ['delete', 'download_video', 'clip_segment', 'revenue'].some((pattern) => action.includes(pattern));
|
|
19
|
+
return {
|
|
20
|
+
riskTier: destructive ? 5 : 4,
|
|
21
|
+
authTier: action.includes('download') || action.includes('clip') ? 'experimental' : 'oauth_write',
|
|
22
|
+
allowedInV0: false,
|
|
23
|
+
requiredControls: destructive ? ['approval', 'double_confirmation', 'audit'] : ['approval', 'preview', 'audit']
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (action.includes('transcript') || action.includes('comments') || action.includes('planning')) {
|
|
27
|
+
return { riskTier: 1, authTier: 'api_key', allowedInV0: true, requiredControls: ['rate_limit', 'privacy_warning'] };
|
|
28
|
+
}
|
|
29
|
+
return { riskTier: 0, authTier: 'api_key', allowedInV0: true, requiredControls: ['rate_limit'] };
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=risk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"risk.js","sourceRoot":"","sources":["../../../src/core/risk.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,QAAQ;IACR,cAAc;IACd,QAAQ;IACR,eAAe;IACf,kBAAkB;IAClB,UAAU;IACV,SAAS;IACT,gBAAgB;IAChB,cAAc;IACd,SAAS;CACD,CAAC;AASX,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAO,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AACzE,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAc;IAC/C,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,MAAM,WAAW,GAAG,CAAC,QAAQ,EAAE,gBAAgB,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QACxH,OAAO;YACL,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7B,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,aAAa;YACjG,WAAW,EAAE,KAAK;YAClB,gBAAgB,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,qBAAqB,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,SAAS,EAAE,OAAO,CAAC;SAChH,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QAChG,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,YAAY,EAAE,iBAAiB,CAAC,EAAE,CAAC;IACtH,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC;AACnG,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createInterface } from 'node:readline';
|
|
3
|
+
import { createToolHandlers, V0_TOOL_NAMES } from './server.js';
|
|
4
|
+
import { redactSecrets } from './core/redaction.js';
|
|
5
|
+
const handlers = createToolHandlers();
|
|
6
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout, terminal: false });
|
|
7
|
+
process.stdout.on('error', (caught) => {
|
|
8
|
+
if (caught.code === 'EPIPE')
|
|
9
|
+
process.exit(0);
|
|
10
|
+
throw caught;
|
|
11
|
+
});
|
|
12
|
+
function respond(id, result) {
|
|
13
|
+
process.stdout.write(`${JSON.stringify({ jsonrpc: '2.0', id, result })}\n`);
|
|
14
|
+
}
|
|
15
|
+
function error(id, code, message) {
|
|
16
|
+
process.stdout.write(`${JSON.stringify({ jsonrpc: '2.0', id, error: { code, message: redactSecrets(message) } })}\n`);
|
|
17
|
+
}
|
|
18
|
+
rl.on('line', async (line) => {
|
|
19
|
+
if (!line.trim())
|
|
20
|
+
return;
|
|
21
|
+
try {
|
|
22
|
+
const request = JSON.parse(line);
|
|
23
|
+
if (request.method === 'initialize') {
|
|
24
|
+
respond(request.id, { protocolVersion: '2024-11-05', capabilities: { tools: {} }, serverInfo: { name: 'youtube-omni-mcp', version: '0.1.0' } });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (request.method === 'tools/list') {
|
|
28
|
+
respond(request.id, { tools: V0_TOOL_NAMES.map((name) => ({ name, description: `YouTube Omni v0 read-only tool: ${name}`, inputSchema: { type: 'object' } })) });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (request.method === 'tools/call') {
|
|
32
|
+
const name = request.params?.name;
|
|
33
|
+
const handler = handlers[name];
|
|
34
|
+
if (!handler) {
|
|
35
|
+
error(request.id, -32601, `Unknown tool: ${String(name)}`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const result = await handler(request.params?.arguments ?? {});
|
|
39
|
+
respond(request.id, { content: [{ type: 'text', text: JSON.stringify(result) }] });
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (request.method === 'notifications/initialized')
|
|
43
|
+
return;
|
|
44
|
+
error(request.id, -32601, `Unknown method: ${request.method}`);
|
|
45
|
+
}
|
|
46
|
+
catch (caught) {
|
|
47
|
+
error(null, -32700, caught instanceof Error ? caught.message : String(caught));
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type { ProviderHealth, YoutubeOmniProvider } from './types.js';
|
|
2
|
+
export declare class MockYoutubeProvider implements YoutubeOmniProvider {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
authTier: "none";
|
|
6
|
+
capabilities: string[];
|
|
7
|
+
isConfigured(): boolean;
|
|
8
|
+
healthCheck(): Promise<ProviderHealth>;
|
|
9
|
+
searchVideos(input: {
|
|
10
|
+
query: string;
|
|
11
|
+
maxResults: number;
|
|
12
|
+
}): Promise<{
|
|
13
|
+
videos: {
|
|
14
|
+
videoId: string;
|
|
15
|
+
title: string;
|
|
16
|
+
channelId: string;
|
|
17
|
+
channelTitle: string;
|
|
18
|
+
publishedAt: string;
|
|
19
|
+
descriptionSnippet: string;
|
|
20
|
+
url: string;
|
|
21
|
+
}[];
|
|
22
|
+
}>;
|
|
23
|
+
searchChannels(input: {
|
|
24
|
+
query: string;
|
|
25
|
+
maxResults: number;
|
|
26
|
+
}): Promise<{
|
|
27
|
+
channels: {
|
|
28
|
+
channelId: string;
|
|
29
|
+
title: string;
|
|
30
|
+
descriptionSnippet: string;
|
|
31
|
+
url: string;
|
|
32
|
+
}[];
|
|
33
|
+
}>;
|
|
34
|
+
getTrendingVideos(input: {
|
|
35
|
+
maxResults: number;
|
|
36
|
+
}): Promise<{
|
|
37
|
+
videos: {
|
|
38
|
+
videoId: string;
|
|
39
|
+
title: string;
|
|
40
|
+
channelId: string;
|
|
41
|
+
channelTitle: string;
|
|
42
|
+
publishedAt: string;
|
|
43
|
+
descriptionSnippet: string;
|
|
44
|
+
url: string;
|
|
45
|
+
}[];
|
|
46
|
+
}>;
|
|
47
|
+
getVideoCategories(input: {
|
|
48
|
+
regionCode: string;
|
|
49
|
+
}): Promise<{
|
|
50
|
+
regionCode: string;
|
|
51
|
+
categories: {
|
|
52
|
+
id: string;
|
|
53
|
+
title: string;
|
|
54
|
+
assignable: boolean;
|
|
55
|
+
}[];
|
|
56
|
+
}>;
|
|
57
|
+
getVideoDetails(input: {
|
|
58
|
+
videoIds: string[];
|
|
59
|
+
includeTags?: boolean;
|
|
60
|
+
}): Promise<{
|
|
61
|
+
videos: {
|
|
62
|
+
videoId: string;
|
|
63
|
+
title: string;
|
|
64
|
+
channelId: string;
|
|
65
|
+
channelTitle: string;
|
|
66
|
+
publishedAt: string;
|
|
67
|
+
duration: string;
|
|
68
|
+
categoryId: string;
|
|
69
|
+
statistics: {
|
|
70
|
+
viewCount: number;
|
|
71
|
+
likeCount: number;
|
|
72
|
+
commentCount: number;
|
|
73
|
+
};
|
|
74
|
+
tags: string[];
|
|
75
|
+
description: string;
|
|
76
|
+
}[];
|
|
77
|
+
}>;
|
|
78
|
+
getChannelStatistics(input: {
|
|
79
|
+
channelIds: string[];
|
|
80
|
+
}): Promise<{
|
|
81
|
+
channels: {
|
|
82
|
+
channelId: string;
|
|
83
|
+
title: string;
|
|
84
|
+
subscriberCount: number;
|
|
85
|
+
viewCount: number;
|
|
86
|
+
videoCount: number;
|
|
87
|
+
publishedAt: string;
|
|
88
|
+
}[];
|
|
89
|
+
}>;
|
|
90
|
+
getChannelTopVideos(input: {
|
|
91
|
+
channelId: string;
|
|
92
|
+
maxResults: number;
|
|
93
|
+
}): Promise<{
|
|
94
|
+
channelId: string;
|
|
95
|
+
videos: {
|
|
96
|
+
videoId: string;
|
|
97
|
+
title: string;
|
|
98
|
+
channelId: string;
|
|
99
|
+
channelTitle: string;
|
|
100
|
+
publishedAt: string;
|
|
101
|
+
duration: string;
|
|
102
|
+
categoryId: string;
|
|
103
|
+
statistics: {
|
|
104
|
+
viewCount: number;
|
|
105
|
+
likeCount: number;
|
|
106
|
+
commentCount: number;
|
|
107
|
+
};
|
|
108
|
+
tags: string[];
|
|
109
|
+
description: string;
|
|
110
|
+
}[];
|
|
111
|
+
}>;
|
|
112
|
+
getTranscriptSegments(input: {
|
|
113
|
+
videoIds: string[];
|
|
114
|
+
lang: string;
|
|
115
|
+
format: 'key_segments';
|
|
116
|
+
}): Promise<{
|
|
117
|
+
transcripts: {
|
|
118
|
+
videoId: string;
|
|
119
|
+
language: string;
|
|
120
|
+
format: "key_segments";
|
|
121
|
+
intro: string;
|
|
122
|
+
outro: string;
|
|
123
|
+
availability: string;
|
|
124
|
+
}[];
|
|
125
|
+
}>;
|
|
126
|
+
getTranscriptFullText(input: {
|
|
127
|
+
videoId: string;
|
|
128
|
+
lang: string;
|
|
129
|
+
maxChars: number;
|
|
130
|
+
}): Promise<{
|
|
131
|
+
videoId: string;
|
|
132
|
+
language: string;
|
|
133
|
+
text: string;
|
|
134
|
+
truncated: boolean;
|
|
135
|
+
}>;
|
|
136
|
+
getVideoComments(input: {
|
|
137
|
+
videoId: string;
|
|
138
|
+
maxResults: number;
|
|
139
|
+
maxReplies: number;
|
|
140
|
+
}): Promise<{
|
|
141
|
+
videoId: string;
|
|
142
|
+
comments: {
|
|
143
|
+
commentId: string;
|
|
144
|
+
text: string;
|
|
145
|
+
likeCount: number;
|
|
146
|
+
publishedAt: string;
|
|
147
|
+
replyCount: number;
|
|
148
|
+
}[];
|
|
149
|
+
}>;
|
|
150
|
+
}
|