Skip to content

Conversation

@JonasJesus42
Copy link
Contributor

@JonasJesus42 JonasJesus42 commented Jan 14, 2026

  • 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

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

    • MCP tools for campaigns, ad groups, and ads: list, get, create, update
    • Reporting tools: get_report, get_campaign_report, get_adgroup_report, get_ad_report, get_advertiser_info
    • Typed TikTok API client with access token auth and consistent error handling
    • Full TypeScript types for API entities and responses
    • README with setup and usage examples; build/publish scripts and app manifest
  • Migration

    • Set TIKTOK_ACCESS_TOKEN in the environment to authenticate
    • Optional (for future OAuth): TIKTOK_APP_ID and TIKTOK_APP_SECRET
    • Requires Node >= 22; run bun install and bun run dev to start

Written for commit 4222905. Summary will update on new commits.

- 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
@github-actions
Copy link

🚀 Preview Deployments Ready!

Your changes have been deployed to preview environments:

📦 tiktok-ads

🔗 View Preview

These previews will be automatically updated with new commits to this PR.


Deployed from commit: ef717ea

Copy link

@cubic-dev-ai cubic-dev-ai bot left a 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;
Copy link

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": {
Copy link

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>
Suggested change
"paths": {
"baseUrl": ".",
"paths": {
"@decocms/mcps-shared/*": ["../shared/*"]
},

code_challenge_method: _code_challenge_method,
}: any) => {
// Use the stored redirect_uri from authorizationUrl
const cleanRedirectUri = lastRedirectUri;
Copy link

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;
Copy link

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>
Suggested change
const directToken = process.env.TIKTOK_ACCESS_TOKEN;
const directToken = env.TIKTOK_ACCESS_TOKEN;

.max(1000)
.optional()
.describe("Items per page (default: 50, max: 1000)"),
}),
Copy link

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>

Comment on lines +469 to +471
metrics: row as unknown as Record<string, number>,
})),
page_info: result.data.page_info,
Copy link

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>
Suggested change
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>,
};
}),

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants