-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add TikTok Ads MCP server #116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- Add campaign management tools (list, get, create, update) - Add ad group management tools (list, get, create, update) - Add ad management tools (list, get, create, update) - Add report tools (campaign, adgroup, ad reports, advertiser info) - Implement TikTok Marketing API client with access token auth - Prepare OAuth flow for future TikTok app approval - Add comprehensive TypeScript types for all API entities - Include README with setup instructions and usage examples
🚀 Preview Deployments Ready!Your changes have been deployed to preview environments: 📦
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
6 issues found across 16 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="tiktok-ads/server/main.ts">
<violation number="1" location="tiktok-ads/server/main.ts:23">
P1: Module-level state `lastRedirectUri` creates a race condition in concurrent OAuth flows. If multiple users initiate OAuth simultaneously, the redirect URI from one user will overwrite another's, causing authentication failures or security issues. Consider passing the redirect_uri through the OAuth state parameter instead, or using a request-scoped storage mechanism.</violation>
<violation number="2" location="tiktok-ads/server/main.ts:68">
P2: `cleanRedirectUri` is validated but never used in the token exchange request. If TikTok requires `redirect_uri` in the token exchange, this will cause OAuth to fail. Either add it to the request body or remove the unnecessary validation.</violation>
</file>
<file name="tiktok-ads/server/lib/env.ts">
<violation number="1" location="tiktok-ads/server/lib/env.ts:18">
P2: Use the `env` argument’s `TIKTOK_ACCESS_TOKEN` instead of directly reading `process.env`, otherwise the helper crashes on non-Node runtimes and ignores tokens supplied by the caller.</violation>
</file>
<file name="tiktok-ads/server/tools/adgroups.ts">
<violation number="1" location="tiktok-ads/server/tools/adgroups.ts:114">
P2: The update-adgroup tool doesn’t require at least one mutable field, so it can send empty updates that TikTok rejects.</violation>
</file>
<file name="tiktok-ads/server/lib/tiktok-client.ts">
<violation number="1" location="tiktok-ads/server/lib/tiktok-client.ts:469">
P2: `getReport` exposes the entire row (including the nested `dimensions` object) as `metrics`, so callers receive non-numeric data where they expect only numeric metrics, breaking the advertised return shape.</violation>
</file>
<file name="tiktok-ads/tsconfig.json">
<violation number="1" location="tiktok-ads/tsconfig.json:22">
P1: `compilerOptions.paths` was added without `compilerOptions.baseUrl`, making the tsconfig invalid (tsc errors with “Option 'paths' cannot be used without specifying '--baseUrl' option”).</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| ].join(","); | ||
|
|
||
| // Store the last used redirect_uri for token exchange | ||
| let lastRedirectUri: string | null = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1: Module-level state lastRedirectUri creates a race condition in concurrent OAuth flows. If multiple users initiate OAuth simultaneously, the redirect URI from one user will overwrite another's, causing authentication failures or security issues. Consider passing the redirect_uri through the OAuth state parameter instead, or using a request-scoped storage mechanism.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tiktok-ads/server/main.ts, line 23:
<comment>Module-level state `lastRedirectUri` creates a race condition in concurrent OAuth flows. If multiple users initiate OAuth simultaneously, the redirect URI from one user will overwrite another's, causing authentication failures or security issues. Consider passing the redirect_uri through the OAuth state parameter instead, or using a request-scoped storage mechanism.</comment>
<file context>
@@ -0,0 +1,122 @@
+].join(",");
+
+// Store the last used redirect_uri for token exchange
+let lastRedirectUri: string | null = null;
+
+const runtime = withRuntime<Env>({
</file context>
| "declaration": true, | ||
| "declarationMap": true, | ||
| "sourceMap": true, | ||
| "paths": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1: compilerOptions.paths was added without compilerOptions.baseUrl, making the tsconfig invalid (tsc errors with “Option 'paths' cannot be used without specifying '--baseUrl' option”).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tiktok-ads/tsconfig.json, line 22:
<comment>`compilerOptions.paths` was added without `compilerOptions.baseUrl`, making the tsconfig invalid (tsc errors with “Option 'paths' cannot be used without specifying '--baseUrl' option”).</comment>
<file context>
@@ -0,0 +1,36 @@
+ "declaration": true,
+ "declarationMap": true,
+ "sourceMap": true,
+ "paths": {
+ "@decocms/mcps-shared/*": ["../shared/*"]
+ },
</file context>
| "paths": { | |
| "baseUrl": ".", | |
| "paths": { | |
| "@decocms/mcps-shared/*": ["../shared/*"] | |
| }, |
| code_challenge_method: _code_challenge_method, | ||
| }: any) => { | ||
| // Use the stored redirect_uri from authorizationUrl | ||
| const cleanRedirectUri = lastRedirectUri; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: cleanRedirectUri is validated but never used in the token exchange request. If TikTok requires redirect_uri in the token exchange, this will cause OAuth to fail. Either add it to the request body or remove the unnecessary validation.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tiktok-ads/server/main.ts, line 68:
<comment>`cleanRedirectUri` is validated but never used in the token exchange request. If TikTok requires `redirect_uri` in the token exchange, this will cause OAuth to fail. Either add it to the request body or remove the unnecessary validation.</comment>
<file context>
@@ -0,0 +1,122 @@
+ code_challenge_method: _code_challenge_method,
+ }: any) => {
+ // Use the stored redirect_uri from authorizationUrl
+ const cleanRedirectUri = lastRedirectUri;
+
+ if (!cleanRedirectUri) {
</file context>
| } | ||
|
|
||
| // Fall back to direct token from environment variable | ||
| const directToken = process.env.TIKTOK_ACCESS_TOKEN; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Use the env argument’s TIKTOK_ACCESS_TOKEN instead of directly reading process.env, otherwise the helper crashes on non-Node runtimes and ignores tokens supplied by the caller.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tiktok-ads/server/lib/env.ts, line 18:
<comment>Use the `env` argument’s `TIKTOK_ACCESS_TOKEN` instead of directly reading `process.env`, otherwise the helper crashes on non-Node runtimes and ignores tokens supplied by the caller.</comment>
<file context>
@@ -0,0 +1,26 @@
+ }
+
+ // Fall back to direct token from environment variable
+ const directToken = process.env.TIKTOK_ACCESS_TOKEN;
+ if (directToken) {
+ return directToken;
</file context>
| const directToken = process.env.TIKTOK_ACCESS_TOKEN; | |
| const directToken = env.TIKTOK_ACCESS_TOKEN; |
| .max(1000) | ||
| .optional() | ||
| .describe("Items per page (default: 50, max: 1000)"), | ||
| }), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: The update-adgroup tool doesn’t require at least one mutable field, so it can send empty updates that TikTok rejects.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tiktok-ads/server/tools/adgroups.ts, line 114:
<comment>The update-adgroup tool doesn’t require at least one mutable field, so it can send empty updates that TikTok rejects.</comment>
<file context>
@@ -0,0 +1,334 @@
+ .max(1000)
+ .optional()
+ .describe("Items per page (default: 50, max: 1000)"),
+ }),
+ outputSchema: z.object({
+ adgroups: z.array(AdGroupSchema).describe("List of ad groups"),
</file context>
| metrics: row as unknown as Record<string, number>, | ||
| })), | ||
| page_info: result.data.page_info, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: getReport exposes the entire row (including the nested dimensions object) as metrics, so callers receive non-numeric data where they expect only numeric metrics, breaking the advertised return shape.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tiktok-ads/server/lib/tiktok-client.ts, line 469:
<comment>`getReport` exposes the entire row (including the nested `dimensions` object) as `metrics`, so callers receive non-numeric data where they expect only numeric metrics, breaking the advertised return shape.</comment>
<file context>
@@ -0,0 +1,516 @@
+ return {
+ rows: result.data.list.map((row) => ({
+ dimensions: row.dimensions,
+ metrics: row as unknown as Record<string, number>,
+ })),
+ page_info: result.data.page_info,
</file context>
| metrics: row as unknown as Record<string, number>, | |
| })), | |
| page_info: result.data.page_info, | |
| rows: result.data.list.map((row) => { | |
| const { dimensions, ...metrics } = row; | |
| return { | |
| dimensions, | |
| metrics: metrics as Record<string, number>, | |
| }; | |
| }), |
Summary by cubic
Adds a TikTok Ads MCP server to manage campaigns, ad groups, and ads, and to pull performance reports via the TikTok Marketing API. Includes token-based auth today and OAuth scaffolding for future app approval.
New Features
Migration
Written for commit 4222905. Summary will update on new commits.