@cashtokenai/meta-mcp-server 1.0.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 +294 -0
- package/dist/config.d.ts +39 -0
- package/dist/config.js +60 -0
- package/dist/constants.d.ts +21 -0
- package/dist/constants.js +28 -0
- package/dist/context.d.ts +9 -0
- package/dist/context.js +8 -0
- package/dist/errors.d.ts +41 -0
- package/dist/errors.js +120 -0
- package/dist/helpers/cache.d.ts +6 -0
- package/dist/helpers/cache.js +28 -0
- package/dist/helpers/format.d.ts +17 -0
- package/dist/helpers/format.js +28 -0
- package/dist/helpers/graph-client.d.ts +90 -0
- package/dist/helpers/graph-client.js +219 -0
- package/dist/helpers/schema.d.ts +30 -0
- package/dist/helpers/schema.js +69 -0
- package/dist/helpers/validate.d.ts +26 -0
- package/dist/helpers/validate.js +125 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +36 -0
- package/dist/logger.d.ts +3 -0
- package/dist/logger.js +18 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.js +34 -0
- package/dist/tools/ads/get-account.d.ts +29 -0
- package/dist/tools/ads/get-account.js +45 -0
- package/dist/tools/ads/get-creative.d.ts +29 -0
- package/dist/tools/ads/get-creative.js +37 -0
- package/dist/tools/ads/get-insights.d.ts +129 -0
- package/dist/tools/ads/get-insights.js +153 -0
- package/dist/tools/ads/list-accounts.d.ts +54 -0
- package/dist/tools/ads/list-accounts.js +59 -0
- package/dist/tools/ads/list-ads.d.ts +53 -0
- package/dist/tools/ads/list-ads.js +59 -0
- package/dist/tools/ads/list-adsets.d.ts +49 -0
- package/dist/tools/ads/list-adsets.js +54 -0
- package/dist/tools/ads/list-campaigns.d.ts +45 -0
- package/dist/tools/ads/list-campaigns.js +64 -0
- package/dist/tools/ads/list-custom-audiences.d.ts +41 -0
- package/dist/tools/ads/list-custom-audiences.js +41 -0
- package/dist/tools/business/list-assets.d.ts +37 -0
- package/dist/tools/business/list-assets.js +136 -0
- package/dist/tools/business/list-businesses.d.ts +37 -0
- package/dist/tools/business/list-businesses.js +81 -0
- package/dist/tools/business/list-system-users.d.ts +41 -0
- package/dist/tools/business/list-system-users.js +73 -0
- package/dist/tools/catalog/get-diagnostics.d.ts +29 -0
- package/dist/tools/catalog/get-diagnostics.js +26 -0
- package/dist/tools/catalog/list-products.d.ts +45 -0
- package/dist/tools/catalog/list-products.js +49 -0
- package/dist/tools/catalog/list.d.ts +54 -0
- package/dist/tools/catalog/list.js +48 -0
- package/dist/tools/instagram/get-account.d.ts +29 -0
- package/dist/tools/instagram/get-account.js +34 -0
- package/dist/tools/instagram/get-audience-demographics.d.ts +45 -0
- package/dist/tools/instagram/get-audience-demographics.js +58 -0
- package/dist/tools/instagram/get-media-insights.d.ts +29 -0
- package/dist/tools/instagram/get-media-insights.js +49 -0
- package/dist/tools/instagram/list-accounts.d.ts +33 -0
- package/dist/tools/instagram/list-accounts.js +63 -0
- package/dist/tools/instagram/list-media.d.ts +41 -0
- package/dist/tools/instagram/list-media.js +42 -0
- package/dist/tools/meta/graph-read.d.ts +33 -0
- package/dist/tools/meta/graph-read.js +71 -0
- package/dist/tools/overview/business-overview.d.ts +49 -0
- package/dist/tools/overview/business-overview.js +235 -0
- package/dist/tools/overview/content-report.d.ts +57 -0
- package/dist/tools/overview/content-report.js +344 -0
- package/dist/tools/overview/latest-posts-summary.d.ts +41 -0
- package/dist/tools/overview/latest-posts-summary.js +189 -0
- package/dist/tools/pages/get-insights.d.ts +41 -0
- package/dist/tools/pages/get-insights.js +49 -0
- package/dist/tools/pages/get-post-insights.d.ts +33 -0
- package/dist/tools/pages/get-post-insights.js +47 -0
- package/dist/tools/pages/get.d.ts +29 -0
- package/dist/tools/pages/get.js +50 -0
- package/dist/tools/pages/list-posts.d.ts +53 -0
- package/dist/tools/pages/list-posts.js +62 -0
- package/dist/tools/pages/list-reviews.d.ts +41 -0
- package/dist/tools/pages/list-reviews.js +37 -0
- package/dist/tools/pages/list-videos.d.ts +41 -0
- package/dist/tools/pages/list-videos.js +40 -0
- package/dist/tools/pages/list.d.ts +41 -0
- package/dist/tools/pages/list.js +39 -0
- package/dist/tools/pixels/get-stats.d.ts +41 -0
- package/dist/tools/pixels/get-stats.js +34 -0
- package/dist/tools/pixels/list.d.ts +41 -0
- package/dist/tools/pixels/list.js +41 -0
- package/dist/tools/register.d.ts +3 -0
- package/dist/tools/register.js +95 -0
- package/dist/tools/shared.d.ts +45 -0
- package/dist/tools/shared.js +137 -0
- package/dist/tools/token/health.d.ts +17 -0
- package/dist/tools/token/health.js +65 -0
- package/dist/tools/token/inspect.d.ts +26 -0
- package/dist/tools/token/inspect.js +88 -0
- package/dist/tools/whatsapp/get-analytics.d.ts +57 -0
- package/dist/tools/whatsapp/get-analytics.js +66 -0
- package/dist/tools/whatsapp/list-phone-numbers.d.ts +41 -0
- package/dist/tools/whatsapp/list-phone-numbers.js +35 -0
- package/dist/tools/whatsapp/list-templates.d.ts +45 -0
- package/dist/tools/whatsapp/list-templates.js +44 -0
- package/dist/tools/whatsapp/list-wabas.d.ts +54 -0
- package/dist/tools/whatsapp/list-wabas.js +48 -0
- package/dist/types/meta.d.ts +46 -0
- package/dist/types/meta.js +1 -0
- package/package.json +77 -0
package/dist/logger.d.ts
ADDED
package/dist/logger.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import pino from "pino";
|
|
2
|
+
export function createLogger(level) {
|
|
3
|
+
return pino({
|
|
4
|
+
name: "meta-mcp",
|
|
5
|
+
level,
|
|
6
|
+
redact: {
|
|
7
|
+
paths: [
|
|
8
|
+
"access_token",
|
|
9
|
+
"*.access_token",
|
|
10
|
+
"params.access_token",
|
|
11
|
+
"headers.authorization",
|
|
12
|
+
"appsecret_proof",
|
|
13
|
+
"*.appsecret_proof",
|
|
14
|
+
],
|
|
15
|
+
censor: "[redacted]",
|
|
16
|
+
},
|
|
17
|
+
}, pino.destination(2));
|
|
18
|
+
}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { Config } from "./config.js";
|
|
3
|
+
import type { Logger } from "./logger.js";
|
|
4
|
+
export declare function buildServer(config: Config, logger: Logger): {
|
|
5
|
+
server: McpServer;
|
|
6
|
+
ctx: import("./context.js").ToolContext;
|
|
7
|
+
};
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { createContext } from "./context.js";
|
|
3
|
+
import { SERVER_NAME, SERVER_VERSION } from "./constants.js";
|
|
4
|
+
import { registerTools } from "./tools/register.js";
|
|
5
|
+
/**
|
|
6
|
+
* Server-level routing guidance surfaced to MCP clients via the SDK's
|
|
7
|
+
* `instructions` field. Gateways that inject server instructions into the
|
|
8
|
+
* model's system prompt use this to pick the right tool without the user
|
|
9
|
+
* having to fine-tune their phrasing. Keep it short, imperative, and focused
|
|
10
|
+
* on the question-classes users actually ask.
|
|
11
|
+
*/
|
|
12
|
+
const SERVER_INSTRUCTIONS = `This server exposes READ-ONLY Meta Business Manager analytics (Facebook Pages, Instagram, ads, pixels, catalog, WhatsApp).
|
|
13
|
+
|
|
14
|
+
TOOL ROUTING — pick the broadest matching tool and CALL IT. Never tell the user to rephrase, and never ask for IDs the server can discover itself:
|
|
15
|
+
|
|
16
|
+
- "How is my marketing doing / give me an overview / snapshot" → meta_business_overview (no business_id needed; it auto-discovers).
|
|
17
|
+
- "How did my LATEST / most recent post do" → meta_latest_posts_summary.
|
|
18
|
+
- "How many posts in the last week / this month, by type, performance comparison, content report, trend" → meta_content_report (takes a date window).
|
|
19
|
+
- "Audience demographics / age / gender / country / city breakdown" → meta_ig_get_audience_demographics.
|
|
20
|
+
- "Ad spend / campaign performance / CTR / CPC / ROAS / which ad/campaign" → meta_ads_get_insights (level=account|campaign|adset|ad, with breakdowns).
|
|
21
|
+
- "What assets / pages / accounts do I have" → meta_business_list_assets.
|
|
22
|
+
- Unsure which asset ID to use → call a discovery/report tool first (they auto-discover); do NOT guess placeholder IDs.
|
|
23
|
+
|
|
24
|
+
If a tool returns a (#10)/(#200) permission error, relay its 'hint' to the user verbatim — it names the exact Meta App Review or scope action needed. Prefer the report/overview tools (one call) over chaining several low-level tools.`;
|
|
25
|
+
export function buildServer(config, logger) {
|
|
26
|
+
const server = new McpServer({
|
|
27
|
+
name: SERVER_NAME,
|
|
28
|
+
version: SERVER_VERSION,
|
|
29
|
+
}, { instructions: SERVER_INSTRUCTIONS });
|
|
30
|
+
const ctx = createContext(config, logger);
|
|
31
|
+
const toolNames = registerTools(server, ctx);
|
|
32
|
+
logger.info({ tools: toolNames, count: toolNames.length }, "tools registered");
|
|
33
|
+
return { server, ctx };
|
|
34
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { ToolContext } from "../../context.js";
|
|
3
|
+
export declare const inputSchema: z.ZodObject<{
|
|
4
|
+
ad_account_id: z.ZodString;
|
|
5
|
+
fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
6
|
+
}, "strict", z.ZodTypeAny, {
|
|
7
|
+
fields: string[];
|
|
8
|
+
ad_account_id: string;
|
|
9
|
+
}, {
|
|
10
|
+
ad_account_id: string;
|
|
11
|
+
fields?: string[] | undefined;
|
|
12
|
+
}>;
|
|
13
|
+
export type Input = z.infer<typeof inputSchema>;
|
|
14
|
+
export declare const definition: {
|
|
15
|
+
readonly name: "meta_ads_get_account";
|
|
16
|
+
readonly title: "Get ad account details";
|
|
17
|
+
readonly description: "Reads account-level details for a single ad account: status, currency, spend cap, current balance, amount_spent lifetime, funding source, capabilities, business owner. Use to verify an account is active and has headroom before drilling into insights.";
|
|
18
|
+
readonly inputSchema: {
|
|
19
|
+
ad_account_id: z.ZodString;
|
|
20
|
+
fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
21
|
+
};
|
|
22
|
+
readonly annotations: {
|
|
23
|
+
readonly readOnlyHint: true;
|
|
24
|
+
readonly destructiveHint: false;
|
|
25
|
+
readonly idempotentHint: true;
|
|
26
|
+
readonly openWorldHint: true;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
export declare function handler(input: Input, ctx: ToolContext): Promise<import("../../helpers/format.js").ToolTextResult>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { assertAllowed } from "../../config.js";
|
|
3
|
+
import { adAccountIdSchema } from "../../helpers/schema.js";
|
|
4
|
+
import { runGet } from "../shared.js";
|
|
5
|
+
export const inputSchema = z
|
|
6
|
+
.object({
|
|
7
|
+
ad_account_id: adAccountIdSchema,
|
|
8
|
+
fields: z
|
|
9
|
+
.array(z.string())
|
|
10
|
+
.default([
|
|
11
|
+
"id",
|
|
12
|
+
"account_id",
|
|
13
|
+
"name",
|
|
14
|
+
"currency",
|
|
15
|
+
"timezone_name",
|
|
16
|
+
"account_status",
|
|
17
|
+
"disable_reason",
|
|
18
|
+
"spend_cap",
|
|
19
|
+
"balance",
|
|
20
|
+
"amount_spent",
|
|
21
|
+
"funding_source_details",
|
|
22
|
+
"business",
|
|
23
|
+
"capabilities",
|
|
24
|
+
"age",
|
|
25
|
+
"created_time",
|
|
26
|
+
])
|
|
27
|
+
.describe("Fields to return."),
|
|
28
|
+
})
|
|
29
|
+
.strict();
|
|
30
|
+
export const definition = {
|
|
31
|
+
name: "meta_ads_get_account",
|
|
32
|
+
title: "Get ad account details",
|
|
33
|
+
description: `Reads account-level details for a single ad account: status, currency, spend cap, current balance, amount_spent lifetime, funding source, capabilities, business owner. Use to verify an account is active and has headroom before drilling into insights.`,
|
|
34
|
+
inputSchema: inputSchema.shape,
|
|
35
|
+
annotations: {
|
|
36
|
+
readOnlyHint: true,
|
|
37
|
+
destructiveHint: false,
|
|
38
|
+
idempotentHint: true,
|
|
39
|
+
openWorldHint: true,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
export async function handler(input, ctx) {
|
|
43
|
+
assertAllowed("ad_account", input.ad_account_id, ctx.config);
|
|
44
|
+
return runGet(ctx, { path: input.ad_account_id, params: { fields: input.fields.join(",") } });
|
|
45
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { ToolContext } from "../../context.js";
|
|
3
|
+
export declare const inputSchema: z.ZodObject<{
|
|
4
|
+
creative_id: z.ZodString;
|
|
5
|
+
fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
6
|
+
}, "strict", z.ZodTypeAny, {
|
|
7
|
+
fields: string[];
|
|
8
|
+
creative_id: string;
|
|
9
|
+
}, {
|
|
10
|
+
creative_id: string;
|
|
11
|
+
fields?: string[] | undefined;
|
|
12
|
+
}>;
|
|
13
|
+
export type Input = z.infer<typeof inputSchema>;
|
|
14
|
+
export declare const definition: {
|
|
15
|
+
readonly name: "meta_ads_get_creative";
|
|
16
|
+
readonly title: "Get ad creative details";
|
|
17
|
+
readonly description: "Reads ad creative body: headline, copy, image or video, CTA, link URL, attached object story. Use alongside insights to explain why a particular ad performed well.";
|
|
18
|
+
readonly inputSchema: {
|
|
19
|
+
creative_id: z.ZodString;
|
|
20
|
+
fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
21
|
+
};
|
|
22
|
+
readonly annotations: {
|
|
23
|
+
readonly readOnlyHint: true;
|
|
24
|
+
readonly destructiveHint: false;
|
|
25
|
+
readonly idempotentHint: true;
|
|
26
|
+
readonly openWorldHint: true;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
export declare function handler(input: Input, ctx: ToolContext): Promise<import("../../helpers/format.js").ToolTextResult>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { metaIdSchema } from "../../helpers/schema.js";
|
|
3
|
+
import { runGet } from "../shared.js";
|
|
4
|
+
export const inputSchema = z
|
|
5
|
+
.object({
|
|
6
|
+
creative_id: metaIdSchema.describe("Creative ID (as returned by meta_ads_list_ads.creative.id)."),
|
|
7
|
+
fields: z
|
|
8
|
+
.array(z.string())
|
|
9
|
+
.default([
|
|
10
|
+
"id",
|
|
11
|
+
"name",
|
|
12
|
+
"title",
|
|
13
|
+
"body",
|
|
14
|
+
"object_story_id",
|
|
15
|
+
"object_type",
|
|
16
|
+
"image_url",
|
|
17
|
+
"thumbnail_url",
|
|
18
|
+
"video_id",
|
|
19
|
+
"call_to_action_type",
|
|
20
|
+
"link_url",
|
|
21
|
+
"effective_object_story_id",
|
|
22
|
+
"instagram_permalink_url",
|
|
23
|
+
"object_story_spec",
|
|
24
|
+
])
|
|
25
|
+
.describe("Creative fields."),
|
|
26
|
+
})
|
|
27
|
+
.strict();
|
|
28
|
+
export const definition = {
|
|
29
|
+
name: "meta_ads_get_creative",
|
|
30
|
+
title: "Get ad creative details",
|
|
31
|
+
description: `Reads ad creative body: headline, copy, image or video, CTA, link URL, attached object story. Use alongside insights to explain why a particular ad performed well.`,
|
|
32
|
+
inputSchema: inputSchema.shape,
|
|
33
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
34
|
+
};
|
|
35
|
+
export async function handler(input, ctx) {
|
|
36
|
+
return runGet(ctx, { path: input.creative_id, params: { fields: input.fields.join(",") } });
|
|
37
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { ToolContext } from "../../context.js";
|
|
3
|
+
export declare const inputSchema: z.ZodObject<{
|
|
4
|
+
limit: z.ZodDefault<z.ZodNumber>;
|
|
5
|
+
after: z.ZodOptional<z.ZodString>;
|
|
6
|
+
auto_paginate: z.ZodDefault<z.ZodBoolean>;
|
|
7
|
+
object_id: z.ZodUnion<[z.ZodString, z.ZodString]>;
|
|
8
|
+
level: z.ZodDefault<z.ZodEnum<["account", "campaign", "adset", "ad"]>>;
|
|
9
|
+
fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
10
|
+
date_preset: z.ZodDefault<z.ZodEnum<["today", "yesterday", "this_month", "last_month", "this_quarter", "maximum", "last_3d", "last_7d", "last_14d", "last_28d", "last_30d", "last_90d", "last_week_mon_sun", "last_week_sun_sat", "last_quarter", "last_year", "this_week_mon_today", "this_week_sun_today", "this_year"]>>;
|
|
11
|
+
time_range: z.ZodOptional<z.ZodObject<{
|
|
12
|
+
since: z.ZodString;
|
|
13
|
+
until: z.ZodString;
|
|
14
|
+
}, "strip", z.ZodTypeAny, {
|
|
15
|
+
since: string;
|
|
16
|
+
until: string;
|
|
17
|
+
}, {
|
|
18
|
+
since: string;
|
|
19
|
+
until: string;
|
|
20
|
+
}>>;
|
|
21
|
+
breakdowns: z.ZodOptional<z.ZodArray<z.ZodEnum<["age", "gender", "country", "region", "dma", "impression_device", "publisher_platform", "platform_position", "device_platform", "product_id", "hourly_stats_aggregated_by_advertiser_time_zone"]>, "many">>;
|
|
22
|
+
action_breakdowns: z.ZodOptional<z.ZodArray<z.ZodEnum<["action_type", "action_destination", "action_device", "action_target_id", "action_video_sound", "action_video_type", "action_reaction"]>, "many">>;
|
|
23
|
+
time_increment: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"monthly">, z.ZodLiteral<"all_days">, z.ZodNumber]>>;
|
|
24
|
+
filtering: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
25
|
+
field: z.ZodString;
|
|
26
|
+
operator: z.ZodString;
|
|
27
|
+
value: z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "many">]>;
|
|
28
|
+
}, "strip", z.ZodTypeAny, {
|
|
29
|
+
value: string | number | (string | number)[];
|
|
30
|
+
field: string;
|
|
31
|
+
operator: string;
|
|
32
|
+
}, {
|
|
33
|
+
value: string | number | (string | number)[];
|
|
34
|
+
field: string;
|
|
35
|
+
operator: string;
|
|
36
|
+
}>, "many">>;
|
|
37
|
+
sort: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
38
|
+
}, "strict", z.ZodTypeAny, {
|
|
39
|
+
fields: string[];
|
|
40
|
+
object_id: string;
|
|
41
|
+
limit: number;
|
|
42
|
+
auto_paginate: boolean;
|
|
43
|
+
level: "account" | "campaign" | "adset" | "ad";
|
|
44
|
+
date_preset: "today" | "yesterday" | "this_month" | "last_month" | "this_quarter" | "maximum" | "last_3d" | "last_7d" | "last_14d" | "last_28d" | "last_30d" | "last_90d" | "last_week_mon_sun" | "last_week_sun_sat" | "last_quarter" | "last_year" | "this_week_mon_today" | "this_week_sun_today" | "this_year";
|
|
45
|
+
sort?: string[] | undefined;
|
|
46
|
+
after?: string | undefined;
|
|
47
|
+
time_range?: {
|
|
48
|
+
since: string;
|
|
49
|
+
until: string;
|
|
50
|
+
} | undefined;
|
|
51
|
+
breakdowns?: ("age" | "gender" | "country" | "region" | "dma" | "impression_device" | "publisher_platform" | "platform_position" | "device_platform" | "product_id" | "hourly_stats_aggregated_by_advertiser_time_zone")[] | undefined;
|
|
52
|
+
action_breakdowns?: ("action_type" | "action_destination" | "action_device" | "action_target_id" | "action_video_sound" | "action_video_type" | "action_reaction")[] | undefined;
|
|
53
|
+
time_increment?: number | "monthly" | "all_days" | undefined;
|
|
54
|
+
filtering?: {
|
|
55
|
+
value: string | number | (string | number)[];
|
|
56
|
+
field: string;
|
|
57
|
+
operator: string;
|
|
58
|
+
}[] | undefined;
|
|
59
|
+
}, {
|
|
60
|
+
object_id: string;
|
|
61
|
+
fields?: string[] | undefined;
|
|
62
|
+
sort?: string[] | undefined;
|
|
63
|
+
limit?: number | undefined;
|
|
64
|
+
after?: string | undefined;
|
|
65
|
+
auto_paginate?: boolean | undefined;
|
|
66
|
+
level?: "account" | "campaign" | "adset" | "ad" | undefined;
|
|
67
|
+
date_preset?: "today" | "yesterday" | "this_month" | "last_month" | "this_quarter" | "maximum" | "last_3d" | "last_7d" | "last_14d" | "last_28d" | "last_30d" | "last_90d" | "last_week_mon_sun" | "last_week_sun_sat" | "last_quarter" | "last_year" | "this_week_mon_today" | "this_week_sun_today" | "this_year" | undefined;
|
|
68
|
+
time_range?: {
|
|
69
|
+
since: string;
|
|
70
|
+
until: string;
|
|
71
|
+
} | undefined;
|
|
72
|
+
breakdowns?: ("age" | "gender" | "country" | "region" | "dma" | "impression_device" | "publisher_platform" | "platform_position" | "device_platform" | "product_id" | "hourly_stats_aggregated_by_advertiser_time_zone")[] | undefined;
|
|
73
|
+
action_breakdowns?: ("action_type" | "action_destination" | "action_device" | "action_target_id" | "action_video_sound" | "action_video_type" | "action_reaction")[] | undefined;
|
|
74
|
+
time_increment?: number | "monthly" | "all_days" | undefined;
|
|
75
|
+
filtering?: {
|
|
76
|
+
value: string | number | (string | number)[];
|
|
77
|
+
field: string;
|
|
78
|
+
operator: string;
|
|
79
|
+
}[] | undefined;
|
|
80
|
+
}>;
|
|
81
|
+
export type Input = z.infer<typeof inputSchema>;
|
|
82
|
+
export declare const definition: {
|
|
83
|
+
readonly name: "meta_ads_get_insights";
|
|
84
|
+
readonly title: "Get Marketing API insights (the workhorse)";
|
|
85
|
+
readonly description: "Fetch Marketing API insights for any ad object: ad account ('act_<id>'), campaign, ad set, or ad.\n\nUSE FOR ad/paid questions: \"how much did I spend\", \"ad spend last 7/30 days\", \"campaign performance\", \"which campaign/ad performed best\", \"CTR / CPC / CPM / ROAS / cost per result\", \"conversions from ads\", \"ad performance comparison\", \"spend by placement / age / gender / country\". This is PAID-ads analytics. For organic post/content performance use meta_content_report; for the latest organic post use meta_latest_posts_summary.\n\nReturns spend, impressions, clicks, CPC/CPM/CTR, reach, frequency, actions (conversions), and video metrics — with optional demographic / placement / device breakdowns.\n\nTips for the AI consuming this:\n- Default date_preset is last_30d. Override via date_preset or time_range.\n- Set level to 'ad' and breakdowns=['publisher_platform','platform_position'] to see which placements convert.\n- Use action_breakdowns=['action_type'] to split purchases vs. add_to_cart vs. lead.\n- If you get code 613 or 17, you hit rate limits — narrow the date range or drop breakdowns.\n\nRequires 'ads_read' token scope.";
|
|
86
|
+
readonly inputSchema: {
|
|
87
|
+
limit: z.ZodDefault<z.ZodNumber>;
|
|
88
|
+
after: z.ZodOptional<z.ZodString>;
|
|
89
|
+
auto_paginate: z.ZodDefault<z.ZodBoolean>;
|
|
90
|
+
object_id: z.ZodUnion<[z.ZodString, z.ZodString]>;
|
|
91
|
+
level: z.ZodDefault<z.ZodEnum<["account", "campaign", "adset", "ad"]>>;
|
|
92
|
+
fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
93
|
+
date_preset: z.ZodDefault<z.ZodEnum<["today", "yesterday", "this_month", "last_month", "this_quarter", "maximum", "last_3d", "last_7d", "last_14d", "last_28d", "last_30d", "last_90d", "last_week_mon_sun", "last_week_sun_sat", "last_quarter", "last_year", "this_week_mon_today", "this_week_sun_today", "this_year"]>>;
|
|
94
|
+
time_range: z.ZodOptional<z.ZodObject<{
|
|
95
|
+
since: z.ZodString;
|
|
96
|
+
until: z.ZodString;
|
|
97
|
+
}, "strip", z.ZodTypeAny, {
|
|
98
|
+
since: string;
|
|
99
|
+
until: string;
|
|
100
|
+
}, {
|
|
101
|
+
since: string;
|
|
102
|
+
until: string;
|
|
103
|
+
}>>;
|
|
104
|
+
breakdowns: z.ZodOptional<z.ZodArray<z.ZodEnum<["age", "gender", "country", "region", "dma", "impression_device", "publisher_platform", "platform_position", "device_platform", "product_id", "hourly_stats_aggregated_by_advertiser_time_zone"]>, "many">>;
|
|
105
|
+
action_breakdowns: z.ZodOptional<z.ZodArray<z.ZodEnum<["action_type", "action_destination", "action_device", "action_target_id", "action_video_sound", "action_video_type", "action_reaction"]>, "many">>;
|
|
106
|
+
time_increment: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"monthly">, z.ZodLiteral<"all_days">, z.ZodNumber]>>;
|
|
107
|
+
filtering: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
108
|
+
field: z.ZodString;
|
|
109
|
+
operator: z.ZodString;
|
|
110
|
+
value: z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "many">]>;
|
|
111
|
+
}, "strip", z.ZodTypeAny, {
|
|
112
|
+
value: string | number | (string | number)[];
|
|
113
|
+
field: string;
|
|
114
|
+
operator: string;
|
|
115
|
+
}, {
|
|
116
|
+
value: string | number | (string | number)[];
|
|
117
|
+
field: string;
|
|
118
|
+
operator: string;
|
|
119
|
+
}>, "many">>;
|
|
120
|
+
sort: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
121
|
+
};
|
|
122
|
+
readonly annotations: {
|
|
123
|
+
readonly readOnlyHint: true;
|
|
124
|
+
readonly destructiveHint: false;
|
|
125
|
+
readonly idempotentHint: true;
|
|
126
|
+
readonly openWorldHint: true;
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
export declare function handler(input: Input, ctx: ToolContext): Promise<import("../../helpers/format.js").ToolTextResult>;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { assertAllowed } from "../../config.js";
|
|
3
|
+
import { adAccountIdSchema, datePresetSchema, metaIdSchema, paginationShape, timeRangeSchema, } from "../../helpers/schema.js";
|
|
4
|
+
import { runList } from "../shared.js";
|
|
5
|
+
const LEVEL = z.enum(["account", "campaign", "adset", "ad"]);
|
|
6
|
+
const BREAKDOWN = z.enum([
|
|
7
|
+
"age",
|
|
8
|
+
"gender",
|
|
9
|
+
"country",
|
|
10
|
+
"region",
|
|
11
|
+
"dma",
|
|
12
|
+
"impression_device",
|
|
13
|
+
"publisher_platform",
|
|
14
|
+
"platform_position",
|
|
15
|
+
"device_platform",
|
|
16
|
+
"product_id",
|
|
17
|
+
"hourly_stats_aggregated_by_advertiser_time_zone",
|
|
18
|
+
]);
|
|
19
|
+
const ACTION_BREAKDOWN = z.enum([
|
|
20
|
+
"action_type",
|
|
21
|
+
"action_destination",
|
|
22
|
+
"action_device",
|
|
23
|
+
"action_target_id",
|
|
24
|
+
"action_video_sound",
|
|
25
|
+
"action_video_type",
|
|
26
|
+
"action_reaction",
|
|
27
|
+
]);
|
|
28
|
+
export const inputSchema = z
|
|
29
|
+
.object({
|
|
30
|
+
object_id: z
|
|
31
|
+
.union([adAccountIdSchema, metaIdSchema])
|
|
32
|
+
.describe("ID to read insights from: ad account 'act_<digits>', campaign ID, ad-set ID, or ad ID. Must match level."),
|
|
33
|
+
level: LEVEL.default("account").describe("Aggregation level. 'account' returns one row per account per breakdown combo; 'ad' returns one row per ad."),
|
|
34
|
+
fields: z
|
|
35
|
+
.array(z.string())
|
|
36
|
+
.default([
|
|
37
|
+
"date_start",
|
|
38
|
+
"date_stop",
|
|
39
|
+
"account_id",
|
|
40
|
+
"account_name",
|
|
41
|
+
"campaign_id",
|
|
42
|
+
"campaign_name",
|
|
43
|
+
"adset_id",
|
|
44
|
+
"adset_name",
|
|
45
|
+
"ad_id",
|
|
46
|
+
"ad_name",
|
|
47
|
+
"impressions",
|
|
48
|
+
"reach",
|
|
49
|
+
"frequency",
|
|
50
|
+
"clicks",
|
|
51
|
+
"unique_clicks",
|
|
52
|
+
"spend",
|
|
53
|
+
"cpc",
|
|
54
|
+
"cpm",
|
|
55
|
+
"cpp",
|
|
56
|
+
"ctr",
|
|
57
|
+
"unique_ctr",
|
|
58
|
+
"actions",
|
|
59
|
+
"action_values",
|
|
60
|
+
"cost_per_action_type",
|
|
61
|
+
"cost_per_unique_click",
|
|
62
|
+
"objective",
|
|
63
|
+
"conversions",
|
|
64
|
+
"conversion_values",
|
|
65
|
+
"video_30_sec_watched_actions",
|
|
66
|
+
"video_p25_watched_actions",
|
|
67
|
+
"video_p50_watched_actions",
|
|
68
|
+
"video_p75_watched_actions",
|
|
69
|
+
"video_p100_watched_actions",
|
|
70
|
+
])
|
|
71
|
+
.describe("Fields per row. Start with the defaults and trim if you want a lighter payload. See https://developers.facebook.com/docs/marketing-api/insights for the full list."),
|
|
72
|
+
date_preset: datePresetSchema.default("last_30d"),
|
|
73
|
+
time_range: timeRangeSchema.optional().describe("Overrides date_preset if supplied."),
|
|
74
|
+
breakdowns: z
|
|
75
|
+
.array(BREAKDOWN)
|
|
76
|
+
.optional()
|
|
77
|
+
.describe("Demographic/placement breakdowns. Multiple breakdowns multiply row count."),
|
|
78
|
+
action_breakdowns: z
|
|
79
|
+
.array(ACTION_BREAKDOWN)
|
|
80
|
+
.optional()
|
|
81
|
+
.describe("Split action/conversion metrics by action dimension."),
|
|
82
|
+
time_increment: z
|
|
83
|
+
.union([z.literal("monthly"), z.literal("all_days"), z.number().int().min(1).max(90)])
|
|
84
|
+
.optional()
|
|
85
|
+
.describe("Bucket rows by N days, 'monthly', or 'all_days' (single row per object)."),
|
|
86
|
+
filtering: z
|
|
87
|
+
.array(z.object({
|
|
88
|
+
field: z.string(),
|
|
89
|
+
operator: z.string(),
|
|
90
|
+
value: z.union([z.string(), z.number(), z.array(z.union([z.string(), z.number()]))]),
|
|
91
|
+
}))
|
|
92
|
+
.optional()
|
|
93
|
+
.describe("Advanced filters, e.g. { field: 'spend', operator: 'GREATER_THAN', value: 100 }."),
|
|
94
|
+
sort: z
|
|
95
|
+
.array(z.string())
|
|
96
|
+
.optional()
|
|
97
|
+
.describe("e.g. ['spend_descending'] or ['impressions_ascending']."),
|
|
98
|
+
...paginationShape,
|
|
99
|
+
})
|
|
100
|
+
.strict();
|
|
101
|
+
export const definition = {
|
|
102
|
+
name: "meta_ads_get_insights",
|
|
103
|
+
title: "Get Marketing API insights (the workhorse)",
|
|
104
|
+
description: `Fetch Marketing API insights for any ad object: ad account ('act_<id>'), campaign, ad set, or ad.
|
|
105
|
+
|
|
106
|
+
USE FOR ad/paid questions: "how much did I spend", "ad spend last 7/30 days", "campaign performance", "which campaign/ad performed best", "CTR / CPC / CPM / ROAS / cost per result", "conversions from ads", "ad performance comparison", "spend by placement / age / gender / country". This is PAID-ads analytics. For organic post/content performance use meta_content_report; for the latest organic post use meta_latest_posts_summary.
|
|
107
|
+
|
|
108
|
+
Returns spend, impressions, clicks, CPC/CPM/CTR, reach, frequency, actions (conversions), and video metrics — with optional demographic / placement / device breakdowns.
|
|
109
|
+
|
|
110
|
+
Tips for the AI consuming this:
|
|
111
|
+
- Default date_preset is last_30d. Override via date_preset or time_range.
|
|
112
|
+
- Set level to 'ad' and breakdowns=['publisher_platform','platform_position'] to see which placements convert.
|
|
113
|
+
- Use action_breakdowns=['action_type'] to split purchases vs. add_to_cart vs. lead.
|
|
114
|
+
- If you get code 613 or 17, you hit rate limits — narrow the date range or drop breakdowns.
|
|
115
|
+
|
|
116
|
+
Requires 'ads_read' token scope.`,
|
|
117
|
+
inputSchema: inputSchema.shape,
|
|
118
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
119
|
+
};
|
|
120
|
+
export async function handler(input, ctx) {
|
|
121
|
+
if (input.object_id.startsWith("act_"))
|
|
122
|
+
assertAllowed("ad_account", input.object_id, ctx.config);
|
|
123
|
+
const params = {
|
|
124
|
+
level: input.level,
|
|
125
|
+
fields: input.fields.join(","),
|
|
126
|
+
limit: input.limit,
|
|
127
|
+
after: input.after,
|
|
128
|
+
};
|
|
129
|
+
if (input.time_range) {
|
|
130
|
+
params["time_range"] = JSON.stringify(input.time_range);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
params["date_preset"] = input.date_preset;
|
|
134
|
+
}
|
|
135
|
+
if (input.breakdowns?.length)
|
|
136
|
+
params["breakdowns"] = input.breakdowns.join(",");
|
|
137
|
+
if (input.action_breakdowns?.length)
|
|
138
|
+
params["action_breakdowns"] = input.action_breakdowns.join(",");
|
|
139
|
+
if (input.time_increment !== undefined)
|
|
140
|
+
params["time_increment"] = String(input.time_increment);
|
|
141
|
+
if (input.filtering?.length)
|
|
142
|
+
params["filtering"] = JSON.stringify(input.filtering);
|
|
143
|
+
if (input.sort?.length)
|
|
144
|
+
params["sort"] = input.sort.join(",");
|
|
145
|
+
return runList(ctx, { path: `${input.object_id}/insights`, params }, { auto_paginate: input.auto_paginate, after: input.after, limit: input.limit }, {
|
|
146
|
+
object_id: input.object_id,
|
|
147
|
+
level: input.level,
|
|
148
|
+
date_preset: input.time_range ? undefined : input.date_preset,
|
|
149
|
+
time_range: input.time_range,
|
|
150
|
+
breakdowns: input.breakdowns,
|
|
151
|
+
action_breakdowns: input.action_breakdowns,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { ToolContext } from "../../context.js";
|
|
3
|
+
export declare const inputSchema: z.ZodObject<{
|
|
4
|
+
limit: z.ZodDefault<z.ZodNumber>;
|
|
5
|
+
after: z.ZodOptional<z.ZodString>;
|
|
6
|
+
auto_paginate: z.ZodDefault<z.ZodBoolean>;
|
|
7
|
+
business_id: z.ZodString;
|
|
8
|
+
scope: z.ZodDefault<z.ZodEnum<["owned", "client", "both"]>>;
|
|
9
|
+
fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
10
|
+
}, "strict", z.ZodTypeAny, {
|
|
11
|
+
fields: string[];
|
|
12
|
+
business_id: string;
|
|
13
|
+
limit: number;
|
|
14
|
+
auto_paginate: boolean;
|
|
15
|
+
scope: "owned" | "client" | "both";
|
|
16
|
+
after?: string | undefined;
|
|
17
|
+
}, {
|
|
18
|
+
business_id: string;
|
|
19
|
+
fields?: string[] | undefined;
|
|
20
|
+
limit?: number | undefined;
|
|
21
|
+
after?: string | undefined;
|
|
22
|
+
auto_paginate?: boolean | undefined;
|
|
23
|
+
scope?: "owned" | "client" | "both" | undefined;
|
|
24
|
+
}>;
|
|
25
|
+
export type Input = z.infer<typeof inputSchema>;
|
|
26
|
+
export declare const definition: {
|
|
27
|
+
readonly name: "meta_ads_list_accounts";
|
|
28
|
+
readonly title: "List ad accounts under a business";
|
|
29
|
+
readonly description: "Lists ad accounts the business owns (owned_ad_accounts) and/or has access to (client_ad_accounts).\n\nNeeds token scope 'ads_read' and the system user must have 'View performance' task on each account.\n\nReturns account_id (numeric), currency, timezone, status, balance, spend cap. Use 'act_<account_id>' when feeding downstream tools like meta_ads_get_insights.";
|
|
30
|
+
readonly inputSchema: {
|
|
31
|
+
limit: z.ZodDefault<z.ZodNumber>;
|
|
32
|
+
after: z.ZodOptional<z.ZodString>;
|
|
33
|
+
auto_paginate: z.ZodDefault<z.ZodBoolean>;
|
|
34
|
+
business_id: z.ZodString;
|
|
35
|
+
scope: z.ZodDefault<z.ZodEnum<["owned", "client", "both"]>>;
|
|
36
|
+
fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
37
|
+
};
|
|
38
|
+
readonly annotations: {
|
|
39
|
+
readonly readOnlyHint: true;
|
|
40
|
+
readonly destructiveHint: false;
|
|
41
|
+
readonly idempotentHint: true;
|
|
42
|
+
readonly openWorldHint: true;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
export declare function handler(input: Input, ctx: ToolContext): Promise<{
|
|
46
|
+
content: {
|
|
47
|
+
type: "text";
|
|
48
|
+
text: string;
|
|
49
|
+
}[];
|
|
50
|
+
structuredContent: {
|
|
51
|
+
business_id: string;
|
|
52
|
+
sections: Record<string, unknown>[];
|
|
53
|
+
};
|
|
54
|
+
}>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { assertAllowed } from "../../config.js";
|
|
3
|
+
import { metaIdSchema, paginationShape } from "../../helpers/schema.js";
|
|
4
|
+
import { runList } from "../shared.js";
|
|
5
|
+
export const inputSchema = z
|
|
6
|
+
.object({
|
|
7
|
+
business_id: metaIdSchema.describe("Business Manager ID."),
|
|
8
|
+
scope: z
|
|
9
|
+
.enum(["owned", "client", "both"])
|
|
10
|
+
.default("both")
|
|
11
|
+
.describe("Which ad-account edge: owned_ad_accounts, client_ad_accounts, or both."),
|
|
12
|
+
fields: z
|
|
13
|
+
.array(z.string())
|
|
14
|
+
.default([
|
|
15
|
+
"id",
|
|
16
|
+
"account_id",
|
|
17
|
+
"name",
|
|
18
|
+
"currency",
|
|
19
|
+
"timezone_name",
|
|
20
|
+
"account_status",
|
|
21
|
+
"disable_reason",
|
|
22
|
+
"spend_cap",
|
|
23
|
+
"balance",
|
|
24
|
+
"amount_spent",
|
|
25
|
+
"created_time",
|
|
26
|
+
])
|
|
27
|
+
.describe("Fields per ad account."),
|
|
28
|
+
...paginationShape,
|
|
29
|
+
})
|
|
30
|
+
.strict();
|
|
31
|
+
export const definition = {
|
|
32
|
+
name: "meta_ads_list_accounts",
|
|
33
|
+
title: "List ad accounts under a business",
|
|
34
|
+
description: `Lists ad accounts the business owns (owned_ad_accounts) and/or has access to (client_ad_accounts).
|
|
35
|
+
|
|
36
|
+
Needs token scope 'ads_read' and the system user must have 'View performance' task on each account.
|
|
37
|
+
|
|
38
|
+
Returns account_id (numeric), currency, timezone, status, balance, spend cap. Use 'act_<account_id>' when feeding downstream tools like meta_ads_get_insights.`,
|
|
39
|
+
inputSchema: inputSchema.shape,
|
|
40
|
+
annotations: {
|
|
41
|
+
readOnlyHint: true,
|
|
42
|
+
destructiveHint: false,
|
|
43
|
+
idempotentHint: true,
|
|
44
|
+
openWorldHint: true,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
export async function handler(input, ctx) {
|
|
48
|
+
assertAllowed("business", input.business_id, ctx.config);
|
|
49
|
+
const edges = input.scope === "both" ? ["owned_ad_accounts", "client_ad_accounts"] : [`${input.scope}_ad_accounts`];
|
|
50
|
+
const sections = [];
|
|
51
|
+
for (const edge of edges) {
|
|
52
|
+
const r = await runList(ctx, { path: `${input.business_id}/${edge}`, params: { fields: input.fields.join(","), limit: input.limit, after: input.after } }, { auto_paginate: input.auto_paginate, after: input.after, limit: input.limit }, { edge });
|
|
53
|
+
sections.push(r.structuredContent ?? { edge, error: "no data" });
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
content: [{ type: "text", text: JSON.stringify({ business_id: input.business_id, sections }, null, 2) }],
|
|
57
|
+
structuredContent: { business_id: input.business_id, sections },
|
|
58
|
+
};
|
|
59
|
+
}
|