@praise25/meta-mcp-server 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.
Files changed (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +292 -0
  3. package/dist/config.d.ts +15 -0
  4. package/dist/config.js +46 -0
  5. package/dist/constants.d.ts +21 -0
  6. package/dist/constants.js +28 -0
  7. package/dist/context.d.ts +9 -0
  8. package/dist/context.js +8 -0
  9. package/dist/errors.d.ts +41 -0
  10. package/dist/errors.js +90 -0
  11. package/dist/helpers/cache.d.ts +6 -0
  12. package/dist/helpers/cache.js +28 -0
  13. package/dist/helpers/format.d.ts +17 -0
  14. package/dist/helpers/format.js +28 -0
  15. package/dist/helpers/graph-client.d.ts +56 -0
  16. package/dist/helpers/graph-client.js +169 -0
  17. package/dist/helpers/schema.d.ts +30 -0
  18. package/dist/helpers/schema.js +69 -0
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.js +36 -0
  21. package/dist/logger.d.ts +3 -0
  22. package/dist/logger.js +18 -0
  23. package/dist/server.d.ts +7 -0
  24. package/dist/server.js +14 -0
  25. package/dist/tools/ads/get-account.d.ts +29 -0
  26. package/dist/tools/ads/get-account.js +45 -0
  27. package/dist/tools/ads/get-creative.d.ts +29 -0
  28. package/dist/tools/ads/get-creative.js +37 -0
  29. package/dist/tools/ads/get-insights.d.ts +129 -0
  30. package/dist/tools/ads/get-insights.js +151 -0
  31. package/dist/tools/ads/list-accounts.d.ts +54 -0
  32. package/dist/tools/ads/list-accounts.js +59 -0
  33. package/dist/tools/ads/list-ads.d.ts +53 -0
  34. package/dist/tools/ads/list-ads.js +59 -0
  35. package/dist/tools/ads/list-adsets.d.ts +49 -0
  36. package/dist/tools/ads/list-adsets.js +54 -0
  37. package/dist/tools/ads/list-campaigns.d.ts +45 -0
  38. package/dist/tools/ads/list-campaigns.js +64 -0
  39. package/dist/tools/ads/list-custom-audiences.d.ts +41 -0
  40. package/dist/tools/ads/list-custom-audiences.js +41 -0
  41. package/dist/tools/business/list-assets.d.ts +37 -0
  42. package/dist/tools/business/list-assets.js +136 -0
  43. package/dist/tools/business/list-businesses.d.ts +37 -0
  44. package/dist/tools/business/list-businesses.js +81 -0
  45. package/dist/tools/business/list-system-users.d.ts +41 -0
  46. package/dist/tools/business/list-system-users.js +73 -0
  47. package/dist/tools/catalog/get-diagnostics.d.ts +29 -0
  48. package/dist/tools/catalog/get-diagnostics.js +26 -0
  49. package/dist/tools/catalog/list-products.d.ts +45 -0
  50. package/dist/tools/catalog/list-products.js +49 -0
  51. package/dist/tools/catalog/list.d.ts +54 -0
  52. package/dist/tools/catalog/list.js +48 -0
  53. package/dist/tools/instagram/get-account.d.ts +29 -0
  54. package/dist/tools/instagram/get-account.js +34 -0
  55. package/dist/tools/instagram/get-audience-demographics.d.ts +41 -0
  56. package/dist/tools/instagram/get-audience-demographics.js +46 -0
  57. package/dist/tools/instagram/get-media-insights.d.ts +29 -0
  58. package/dist/tools/instagram/get-media-insights.js +43 -0
  59. package/dist/tools/instagram/list-accounts.d.ts +33 -0
  60. package/dist/tools/instagram/list-accounts.js +63 -0
  61. package/dist/tools/instagram/list-media.d.ts +41 -0
  62. package/dist/tools/instagram/list-media.js +42 -0
  63. package/dist/tools/meta/graph-read.d.ts +33 -0
  64. package/dist/tools/meta/graph-read.js +71 -0
  65. package/dist/tools/overview/business-overview.d.ts +49 -0
  66. package/dist/tools/overview/business-overview.js +188 -0
  67. package/dist/tools/pages/get-insights.d.ts +41 -0
  68. package/dist/tools/pages/get-insights.js +49 -0
  69. package/dist/tools/pages/get-post-insights.d.ts +29 -0
  70. package/dist/tools/pages/get-post-insights.js +36 -0
  71. package/dist/tools/pages/get.d.ts +29 -0
  72. package/dist/tools/pages/get.js +50 -0
  73. package/dist/tools/pages/list-posts.d.ts +53 -0
  74. package/dist/tools/pages/list-posts.js +54 -0
  75. package/dist/tools/pages/list-reviews.d.ts +41 -0
  76. package/dist/tools/pages/list-reviews.js +37 -0
  77. package/dist/tools/pages/list-videos.d.ts +41 -0
  78. package/dist/tools/pages/list-videos.js +40 -0
  79. package/dist/tools/pages/list.d.ts +41 -0
  80. package/dist/tools/pages/list.js +39 -0
  81. package/dist/tools/pixels/get-stats.d.ts +41 -0
  82. package/dist/tools/pixels/get-stats.js +34 -0
  83. package/dist/tools/pixels/list.d.ts +41 -0
  84. package/dist/tools/pixels/list.js +38 -0
  85. package/dist/tools/register.d.ts +3 -0
  86. package/dist/tools/register.js +82 -0
  87. package/dist/tools/shared.d.ts +20 -0
  88. package/dist/tools/shared.js +55 -0
  89. package/dist/tools/token/health.d.ts +17 -0
  90. package/dist/tools/token/health.js +59 -0
  91. package/dist/tools/token/inspect.d.ts +26 -0
  92. package/dist/tools/token/inspect.js +88 -0
  93. package/dist/tools/whatsapp/get-analytics.d.ts +57 -0
  94. package/dist/tools/whatsapp/get-analytics.js +66 -0
  95. package/dist/tools/whatsapp/list-phone-numbers.d.ts +41 -0
  96. package/dist/tools/whatsapp/list-phone-numbers.js +35 -0
  97. package/dist/tools/whatsapp/list-templates.d.ts +45 -0
  98. package/dist/tools/whatsapp/list-templates.js +44 -0
  99. package/dist/tools/whatsapp/list-wabas.d.ts +54 -0
  100. package/dist/tools/whatsapp/list-wabas.js +48 -0
  101. package/dist/types/meta.d.ts +46 -0
  102. package/dist/types/meta.js +1 -0
  103. package/package.json +74 -0
@@ -0,0 +1,26 @@
1
+ import { z } from "zod";
2
+ import type { ToolContext } from "../../context.js";
3
+ import { ResponseFormat } from "../../helpers/format.js";
4
+ export declare const inputSchema: z.ZodObject<{
5
+ response_format: z.ZodDefault<z.ZodNativeEnum<typeof ResponseFormat>>;
6
+ }, "strict", z.ZodTypeAny, {
7
+ response_format: ResponseFormat;
8
+ }, {
9
+ response_format?: ResponseFormat | undefined;
10
+ }>;
11
+ export type Input = z.infer<typeof inputSchema>;
12
+ export declare const definition: {
13
+ readonly name: "meta_token_inspect";
14
+ readonly title: "Inspect Meta access token";
15
+ readonly description: "Decodes the configured META_ACCESS_TOKEN via Graph API /debug_token.\n\nReturns:\n- app_id + application name\n- token type (system-user / user / page)\n- is_valid\n- expires_at + data_access_expires_at (unix seconds; 0 = never expires)\n- scopes + granular_scopes (per-asset targeting)\n\nUse this first when troubleshooting — almost every other tool failure traces back to a missing scope or an asset not assigned to the token.\n\nNever logs the token itself.";
16
+ readonly inputSchema: {
17
+ response_format: z.ZodDefault<z.ZodNativeEnum<typeof ResponseFormat>>;
18
+ };
19
+ readonly annotations: {
20
+ readonly readOnlyHint: true;
21
+ readonly destructiveHint: false;
22
+ readonly idempotentHint: true;
23
+ readonly openWorldHint: true;
24
+ };
25
+ };
26
+ export declare function handler(input: Input, ctx: ToolContext): Promise<import("../../helpers/format.js").ToolTextResult>;
@@ -0,0 +1,88 @@
1
+ import { z } from "zod";
2
+ import { MetaError } from "../../errors.js";
3
+ import { jsonBlock, ResponseFormat, toolError, toolResult } from "../../helpers/format.js";
4
+ import { responseFormatShape } from "../../helpers/schema.js";
5
+ export const inputSchema = z
6
+ .object({
7
+ ...responseFormatShape,
8
+ })
9
+ .strict();
10
+ export const definition = {
11
+ name: "meta_token_inspect",
12
+ title: "Inspect Meta access token",
13
+ description: `Decodes the configured META_ACCESS_TOKEN via Graph API /debug_token.
14
+
15
+ Returns:
16
+ - app_id + application name
17
+ - token type (system-user / user / page)
18
+ - is_valid
19
+ - expires_at + data_access_expires_at (unix seconds; 0 = never expires)
20
+ - scopes + granular_scopes (per-asset targeting)
21
+
22
+ Use this first when troubleshooting — almost every other tool failure traces back to a missing scope or an asset not assigned to the token.
23
+
24
+ Never logs the token itself.`,
25
+ inputSchema: inputSchema.shape,
26
+ annotations: {
27
+ readOnlyHint: true,
28
+ destructiveHint: false,
29
+ idempotentHint: true,
30
+ openWorldHint: true,
31
+ },
32
+ };
33
+ export async function handler(input, ctx) {
34
+ try {
35
+ const data = await ctx.graph.get({
36
+ path: "debug_token",
37
+ params: { input_token: ctx.config.accessToken },
38
+ noCache: true,
39
+ });
40
+ const debug = data.data ?? {};
41
+ const now = Math.floor(Date.now() / 1000);
42
+ const expiresIn = debug.expires_at && debug.expires_at > 0 ? debug.expires_at - now : null;
43
+ const dataAccessExpiresIn = debug.data_access_expires_at && debug.data_access_expires_at > 0
44
+ ? debug.data_access_expires_at - now
45
+ : null;
46
+ const structured = {
47
+ ...debug,
48
+ expires_in_seconds: expiresIn,
49
+ data_access_expires_in_seconds: dataAccessExpiresIn,
50
+ never_expires: debug.expires_at === 0,
51
+ app_secret_proof_enabled: Boolean(ctx.config.appSecret),
52
+ };
53
+ const text = input.response_format === ResponseFormat.MARKDOWN
54
+ ? renderMarkdown(structured)
55
+ : jsonBlock(structured);
56
+ return toolResult(structured, text);
57
+ }
58
+ catch (err) {
59
+ const e = err instanceof MetaError ? err : new MetaError(err.message);
60
+ return toolError(e.message, e.hint, {
61
+ code: e.code,
62
+ subcode: e.subcode,
63
+ fbtrace_id: e.fbtraceId,
64
+ http_status: e.httpStatus,
65
+ });
66
+ }
67
+ }
68
+ function renderMarkdown(d) {
69
+ const lines = ["# Meta Token Inspection", ""];
70
+ lines.push(`- **Valid**: ${d.is_valid ?? "unknown"}`);
71
+ lines.push(`- **App**: ${d.application ?? "?"} (${d.app_id ?? "?"})`);
72
+ if (d.type)
73
+ lines.push(`- **Type**: ${d.type}`);
74
+ if (d.user_id)
75
+ lines.push(`- **User / System User ID**: ${d.user_id}`);
76
+ lines.push(`- **Expires**: ${d.never_expires ? "never" : d.expires_in_seconds != null ? `in ${d.expires_in_seconds}s` : "unknown"}`);
77
+ lines.push(`- **appsecret_proof**: ${d.app_secret_proof_enabled ? "enabled" : "disabled (recommend enabling)"}`);
78
+ if (d.scopes?.length) {
79
+ lines.push("", "## Scopes", ...d.scopes.map((s) => `- ${s}`));
80
+ }
81
+ if (d.granular_scopes?.length) {
82
+ lines.push("", "## Granular scopes");
83
+ for (const g of d.granular_scopes) {
84
+ lines.push(`- **${g.scope}** → ${g.target_ids?.length ? g.target_ids.join(", ") : "all"}`);
85
+ }
86
+ }
87
+ return lines.join("\n");
88
+ }
@@ -0,0 +1,57 @@
1
+ import { z } from "zod";
2
+ import type { ToolContext } from "../../context.js";
3
+ export declare const inputSchema: z.ZodObject<{
4
+ waba_id: z.ZodString;
5
+ flavor: z.ZodDefault<z.ZodEnum<["analytics", "conversation_analytics"]>>;
6
+ start: z.ZodNumber;
7
+ end: z.ZodNumber;
8
+ granularity: z.ZodDefault<z.ZodEnum<["HALF_HOUR", "DAY", "MONTH"]>>;
9
+ phone_numbers: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
10
+ country_codes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
11
+ conversation_categories: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
12
+ dimensions: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
13
+ }, "strict", z.ZodTypeAny, {
14
+ waba_id: string;
15
+ flavor: "analytics" | "conversation_analytics";
16
+ start: number;
17
+ end: number;
18
+ granularity: "HALF_HOUR" | "DAY" | "MONTH";
19
+ dimensions: string[];
20
+ phone_numbers?: string[] | undefined;
21
+ country_codes?: string[] | undefined;
22
+ conversation_categories?: string[] | undefined;
23
+ }, {
24
+ waba_id: string;
25
+ start: number;
26
+ end: number;
27
+ flavor?: "analytics" | "conversation_analytics" | undefined;
28
+ granularity?: "HALF_HOUR" | "DAY" | "MONTH" | undefined;
29
+ phone_numbers?: string[] | undefined;
30
+ country_codes?: string[] | undefined;
31
+ conversation_categories?: string[] | undefined;
32
+ dimensions?: string[] | undefined;
33
+ }>;
34
+ export type Input = z.infer<typeof inputSchema>;
35
+ export declare const definition: {
36
+ readonly name: "meta_whatsapp_get_analytics";
37
+ readonly title: "Get WhatsApp Business analytics";
38
+ readonly description: "Fetches WABA analytics — either the legacy 'analytics' field (message counts) or the richer 'conversation_analytics' (per-conversation pricing, categories: AUTHENTICATION/MARKETING/SERVICE/UTILITY).\n\nPass start + end as Unix seconds. Use granularity DAY for most reports. Filter by phone_numbers, country_codes, or conversation_categories to narrow.";
39
+ readonly inputSchema: {
40
+ waba_id: z.ZodString;
41
+ flavor: z.ZodDefault<z.ZodEnum<["analytics", "conversation_analytics"]>>;
42
+ start: z.ZodNumber;
43
+ end: z.ZodNumber;
44
+ granularity: z.ZodDefault<z.ZodEnum<["HALF_HOUR", "DAY", "MONTH"]>>;
45
+ phone_numbers: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
46
+ country_codes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
47
+ conversation_categories: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
48
+ dimensions: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
49
+ };
50
+ readonly annotations: {
51
+ readonly readOnlyHint: true;
52
+ readonly destructiveHint: false;
53
+ readonly idempotentHint: true;
54
+ readonly openWorldHint: true;
55
+ };
56
+ };
57
+ export declare function handler(input: Input, ctx: ToolContext): Promise<import("../../helpers/format.js").ToolTextResult>;
@@ -0,0 +1,66 @@
1
+ import { z } from "zod";
2
+ import { metaIdSchema } from "../../helpers/schema.js";
3
+ import { runGet } from "../shared.js";
4
+ const GRANULARITY = z.enum(["HALF_HOUR", "DAY", "MONTH"]);
5
+ export const inputSchema = z
6
+ .object({
7
+ waba_id: metaIdSchema,
8
+ flavor: z
9
+ .enum(["analytics", "conversation_analytics"])
10
+ .default("conversation_analytics")
11
+ .describe("'analytics' = legacy message-count metric. 'conversation_analytics' = per-conversation pricing + category metrics (preferred)."),
12
+ start: z.number().int().describe("Unix timestamp (seconds) — window start."),
13
+ end: z.number().int().describe("Unix timestamp (seconds) — window end."),
14
+ granularity: GRANULARITY.default("DAY"),
15
+ phone_numbers: z
16
+ .array(z.string())
17
+ .optional()
18
+ .describe("Optional filter: list of display phone numbers ('+15551234567' form)."),
19
+ country_codes: z.array(z.string()).optional().describe("Optional filter: ISO country codes."),
20
+ conversation_categories: z
21
+ .array(z.string())
22
+ .optional()
23
+ .describe("AUTHENTICATION, MARKETING, SERVICE, UTILITY."),
24
+ dimensions: z
25
+ .array(z.string())
26
+ .default(["CONVERSATION_CATEGORY", "COUNTRY", "PHONE"])
27
+ .describe("Breakdown dimensions."),
28
+ })
29
+ .strict();
30
+ export const definition = {
31
+ name: "meta_whatsapp_get_analytics",
32
+ title: "Get WhatsApp Business analytics",
33
+ description: `Fetches WABA analytics — either the legacy 'analytics' field (message counts) or the richer 'conversation_analytics' (per-conversation pricing, categories: AUTHENTICATION/MARKETING/SERVICE/UTILITY).
34
+
35
+ Pass start + end as Unix seconds. Use granularity DAY for most reports. Filter by phone_numbers, country_codes, or conversation_categories to narrow.`,
36
+ inputSchema: inputSchema.shape,
37
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
38
+ };
39
+ export async function handler(input, ctx) {
40
+ const dims = input.dimensions.join(",");
41
+ let inner;
42
+ if (input.flavor === "conversation_analytics") {
43
+ const parts = [
44
+ `start(${input.start})`,
45
+ `end(${input.end})`,
46
+ `granularity(${input.granularity})`,
47
+ `dimensions([${dims}])`,
48
+ ];
49
+ if (input.phone_numbers?.length)
50
+ parts.push(`phone_numbers([${input.phone_numbers.join(",")}])`);
51
+ if (input.country_codes?.length)
52
+ parts.push(`country_codes([${input.country_codes.join(",")}])`);
53
+ if (input.conversation_categories?.length)
54
+ parts.push(`conversation_categories([${input.conversation_categories.join(",")}])`);
55
+ inner = `conversation_analytics.${parts.join(".")}`;
56
+ }
57
+ else {
58
+ const parts = [`start(${input.start})`, `end(${input.end})`, `granularity(${input.granularity})`];
59
+ if (input.phone_numbers?.length)
60
+ parts.push(`phone_numbers([${input.phone_numbers.join(",")}])`);
61
+ if (input.country_codes?.length)
62
+ parts.push(`country_codes([${input.country_codes.join(",")}])`);
63
+ inner = `analytics.${parts.join(".")}`;
64
+ }
65
+ return runGet(ctx, { path: input.waba_id, params: { fields: inner } }, { waba_id: input.waba_id, flavor: input.flavor });
66
+ }
@@ -0,0 +1,41 @@
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
+ waba_id: z.ZodString;
8
+ fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
9
+ }, "strict", z.ZodTypeAny, {
10
+ fields: string[];
11
+ limit: number;
12
+ auto_paginate: boolean;
13
+ waba_id: string;
14
+ after?: string | undefined;
15
+ }, {
16
+ waba_id: string;
17
+ fields?: string[] | undefined;
18
+ limit?: number | undefined;
19
+ after?: string | undefined;
20
+ auto_paginate?: boolean | undefined;
21
+ }>;
22
+ export type Input = z.infer<typeof inputSchema>;
23
+ export declare const definition: {
24
+ readonly name: "meta_whatsapp_list_phone_numbers";
25
+ readonly title: "List phone numbers under a WABA";
26
+ readonly description: "Lists the phone numbers attached to a WABA — display number, verified name, quality rating (GREEN/YELLOW/RED), messaging tier (throughput limits).";
27
+ readonly inputSchema: {
28
+ limit: z.ZodDefault<z.ZodNumber>;
29
+ after: z.ZodOptional<z.ZodString>;
30
+ auto_paginate: z.ZodDefault<z.ZodBoolean>;
31
+ waba_id: z.ZodString;
32
+ fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
33
+ };
34
+ readonly annotations: {
35
+ readonly readOnlyHint: true;
36
+ readonly destructiveHint: false;
37
+ readonly idempotentHint: true;
38
+ readonly openWorldHint: true;
39
+ };
40
+ };
41
+ export declare function handler(input: Input, ctx: ToolContext): Promise<import("../../helpers/format.js").ToolTextResult>;
@@ -0,0 +1,35 @@
1
+ import { z } from "zod";
2
+ import { metaIdSchema, paginationShape } from "../../helpers/schema.js";
3
+ import { runList } from "../shared.js";
4
+ export const inputSchema = z
5
+ .object({
6
+ waba_id: metaIdSchema,
7
+ fields: z
8
+ .array(z.string())
9
+ .default([
10
+ "id",
11
+ "display_phone_number",
12
+ "verified_name",
13
+ "code_verification_status",
14
+ "quality_rating",
15
+ "platform_type",
16
+ "throughput",
17
+ "messaging_limit_tier",
18
+ ])
19
+ .describe("Phone number fields."),
20
+ ...paginationShape,
21
+ })
22
+ .strict();
23
+ export const definition = {
24
+ name: "meta_whatsapp_list_phone_numbers",
25
+ title: "List phone numbers under a WABA",
26
+ description: `Lists the phone numbers attached to a WABA — display number, verified name, quality rating (GREEN/YELLOW/RED), messaging tier (throughput limits).`,
27
+ inputSchema: inputSchema.shape,
28
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
29
+ };
30
+ export async function handler(input, ctx) {
31
+ return runList(ctx, {
32
+ path: `${input.waba_id}/phone_numbers`,
33
+ params: { fields: input.fields.join(","), limit: input.limit, after: input.after },
34
+ }, { auto_paginate: input.auto_paginate, after: input.after, limit: input.limit }, { waba_id: input.waba_id });
35
+ }
@@ -0,0 +1,45 @@
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
+ waba_id: z.ZodString;
8
+ status: z.ZodOptional<z.ZodEnum<["APPROVED", "PENDING", "REJECTED", "DISABLED", "IN_APPEAL", "DELETED", "PAUSED"]>>;
9
+ fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
10
+ }, "strict", z.ZodTypeAny, {
11
+ fields: string[];
12
+ limit: number;
13
+ auto_paginate: boolean;
14
+ waba_id: string;
15
+ status?: "PAUSED" | "DELETED" | "APPROVED" | "PENDING" | "REJECTED" | "DISABLED" | "IN_APPEAL" | undefined;
16
+ after?: string | undefined;
17
+ }, {
18
+ waba_id: string;
19
+ status?: "PAUSED" | "DELETED" | "APPROVED" | "PENDING" | "REJECTED" | "DISABLED" | "IN_APPEAL" | undefined;
20
+ fields?: string[] | undefined;
21
+ limit?: number | undefined;
22
+ after?: string | undefined;
23
+ auto_paginate?: boolean | undefined;
24
+ }>;
25
+ export type Input = z.infer<typeof inputSchema>;
26
+ export declare const definition: {
27
+ readonly name: "meta_whatsapp_list_templates";
28
+ readonly title: "List WhatsApp message templates";
29
+ readonly description: "Lists approved/pending/rejected message templates for a WABA. Read-only — does not send messages. Useful for spotting rejected templates and pre-flight checks.";
30
+ readonly inputSchema: {
31
+ limit: z.ZodDefault<z.ZodNumber>;
32
+ after: z.ZodOptional<z.ZodString>;
33
+ auto_paginate: z.ZodDefault<z.ZodBoolean>;
34
+ waba_id: z.ZodString;
35
+ status: z.ZodOptional<z.ZodEnum<["APPROVED", "PENDING", "REJECTED", "DISABLED", "IN_APPEAL", "DELETED", "PAUSED"]>>;
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<import("../../helpers/format.js").ToolTextResult>;
@@ -0,0 +1,44 @@
1
+ import { z } from "zod";
2
+ import { metaIdSchema, paginationShape } from "../../helpers/schema.js";
3
+ import { runList } from "../shared.js";
4
+ export const inputSchema = z
5
+ .object({
6
+ waba_id: metaIdSchema,
7
+ status: z
8
+ .enum(["APPROVED", "PENDING", "REJECTED", "DISABLED", "IN_APPEAL", "DELETED", "PAUSED"])
9
+ .optional()
10
+ .describe("Filter by template status."),
11
+ fields: z
12
+ .array(z.string())
13
+ .default([
14
+ "id",
15
+ "name",
16
+ "status",
17
+ "category",
18
+ "language",
19
+ "components",
20
+ "quality_score",
21
+ "rejected_reason",
22
+ ])
23
+ .describe("Template fields."),
24
+ ...paginationShape,
25
+ })
26
+ .strict();
27
+ export const definition = {
28
+ name: "meta_whatsapp_list_templates",
29
+ title: "List WhatsApp message templates",
30
+ description: `Lists approved/pending/rejected message templates for a WABA. Read-only — does not send messages. Useful for spotting rejected templates and pre-flight checks.`,
31
+ inputSchema: inputSchema.shape,
32
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
33
+ };
34
+ export async function handler(input, ctx) {
35
+ return runList(ctx, {
36
+ path: `${input.waba_id}/message_templates`,
37
+ params: {
38
+ fields: input.fields.join(","),
39
+ limit: input.limit,
40
+ after: input.after,
41
+ status: input.status,
42
+ },
43
+ }, { auto_paginate: input.auto_paginate, after: input.after, limit: input.limit }, { waba_id: input.waba_id });
44
+ }
@@ -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
+ limit: number;
13
+ auto_paginate: boolean;
14
+ business_id: string;
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_whatsapp_list_wabas";
28
+ readonly title: "List WhatsApp Business Accounts under a business";
29
+ readonly description: "Lists WhatsApp Business Accounts (WABAs). Needs WhatsApp Business Platform product on the app and 'whatsapp_business_management' token scope.";
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,48 @@
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,
8
+ scope: z.enum(["owned", "client", "both"]).default("both"),
9
+ fields: z
10
+ .array(z.string())
11
+ .default([
12
+ "id",
13
+ "name",
14
+ "currency",
15
+ "timezone_id",
16
+ "business_verification_status",
17
+ "message_template_namespace",
18
+ "status",
19
+ ])
20
+ .describe("WABA fields."),
21
+ ...paginationShape,
22
+ })
23
+ .strict();
24
+ export const definition = {
25
+ name: "meta_whatsapp_list_wabas",
26
+ title: "List WhatsApp Business Accounts under a business",
27
+ description: `Lists WhatsApp Business Accounts (WABAs). Needs WhatsApp Business Platform product on the app and 'whatsapp_business_management' token scope.`,
28
+ inputSchema: inputSchema.shape,
29
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
30
+ };
31
+ export async function handler(input, ctx) {
32
+ assertAllowed("business", input.business_id, ctx.config);
33
+ const edges = input.scope === "both"
34
+ ? ["owned_whatsapp_business_accounts", "client_whatsapp_business_accounts"]
35
+ : [`${input.scope}_whatsapp_business_accounts`];
36
+ const sections = [];
37
+ for (const edge of edges) {
38
+ const r = await runList(ctx, {
39
+ path: `${input.business_id}/${edge}`,
40
+ params: { fields: input.fields.join(","), limit: input.limit, after: input.after },
41
+ }, { auto_paginate: input.auto_paginate, after: input.after, limit: input.limit }, { edge });
42
+ sections.push(r.structuredContent ?? { edge, error: "no data" });
43
+ }
44
+ return {
45
+ content: [{ type: "text", text: JSON.stringify({ business_id: input.business_id, sections }, null, 2) }],
46
+ structuredContent: { business_id: input.business_id, sections },
47
+ };
48
+ }
@@ -0,0 +1,46 @@
1
+ /** Shape of a Business owned/client asset edge. */
2
+ export interface BusinessAsset {
3
+ id: string;
4
+ name?: string;
5
+ category?: string;
6
+ /** Only present for ad accounts. */
7
+ account_id?: string;
8
+ currency?: string;
9
+ timezone_name?: string;
10
+ username?: string;
11
+ }
12
+ export interface Business {
13
+ id: string;
14
+ name: string;
15
+ verification_status?: string;
16
+ primary_page?: {
17
+ id: string;
18
+ name?: string;
19
+ };
20
+ created_time?: string;
21
+ updated_time?: string;
22
+ timezone_id?: number;
23
+ }
24
+ export interface SystemUser {
25
+ id: string;
26
+ name: string;
27
+ role?: string;
28
+ finance_role?: string;
29
+ ip_role?: string;
30
+ }
31
+ export interface TokenDebug {
32
+ app_id?: string;
33
+ type?: string;
34
+ application?: string;
35
+ data_access_expires_at?: number;
36
+ expires_at?: number;
37
+ is_valid?: boolean;
38
+ issued_at?: number;
39
+ scopes?: string[];
40
+ granular_scopes?: {
41
+ scope: string;
42
+ target_ids?: string[];
43
+ }[];
44
+ user_id?: string;
45
+ profile_id?: string;
46
+ }
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@praise25/meta-mcp-server",
3
+ "description": "Read-only Model Context Protocol server for Meta Business Manager — Pages, Instagram, Ads insights, Pixels, Catalog, WhatsApp.",
4
+ "version": "0.1.0",
5
+ "author": "Stephen A.",
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/feladeveloper/meta-mcp-server#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/feladeveloper/meta-mcp-server.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/feladeveloper/meta-mcp-server/issues"
14
+ },
15
+ "keywords": [
16
+ "mcp",
17
+ "model-context-protocol",
18
+ "meta",
19
+ "facebook",
20
+ "instagram",
21
+ "marketing-api",
22
+ "ads",
23
+ "ads-insights",
24
+ "pixels",
25
+ "catalog",
26
+ "whatsapp",
27
+ "business-manager",
28
+ "read-only",
29
+ "ai-tools",
30
+ "claude"
31
+ ],
32
+ "type": "module",
33
+ "main": "dist/index.js",
34
+ "bin": {
35
+ "meta-business-manager-mcp-server": "dist/index.js"
36
+ },
37
+ "files": [
38
+ "dist",
39
+ "README.md",
40
+ "LICENSE"
41
+ ],
42
+ "engines": {
43
+ "node": ">=20.10.0"
44
+ },
45
+ "scripts": {
46
+ "build:clean": "rm -rf dist",
47
+ "build:compile": "tsc --project tsconfig.build.json",
48
+ "build:chmod": "chmod +x dist/index.js || true",
49
+ "build": "npm run build:clean && npm run build:compile && npm run build:chmod",
50
+ "start": "node dist/index.js",
51
+ "dev": "tsx src/index.ts",
52
+ "inspect": "npm run build && npx @modelcontextprotocol/inspector dist/index.js",
53
+ "check:types": "tsc --noEmit --project tsconfig.json",
54
+ "test:readonly": "npm run build && node tests/read-only-guard.mjs",
55
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
56
+ "prepublishOnly": "npm run check:types && npm run test:readonly"
57
+ },
58
+ "dependencies": {
59
+ "@modelcontextprotocol/sdk": "^1.11.2",
60
+ "axios": "^1.7.7",
61
+ "lru-cache": "^11.1.0",
62
+ "pino": "^9.5.0",
63
+ "zod": "^3.23.8"
64
+ },
65
+ "devDependencies": {
66
+ "@jest/globals": "^30.0.0",
67
+ "@types/jest": "^30.0.0",
68
+ "@types/node": "^22.0.0",
69
+ "jest": "^30.0.0",
70
+ "ts-jest": "^29.2.0",
71
+ "tsx": "^4.19.0",
72
+ "typescript": "^5.6.0"
73
+ }
74
+ }