@praise25/meta-mcp-server 0.1.5 → 0.1.7

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.
@@ -4,7 +4,7 @@ export declare const CHARACTER_LIMIT = 25000;
4
4
  export declare const DEFAULT_PAGE_LIMIT = 25;
5
5
  export declare const MAX_PAGE_LIMIT = 500;
6
6
  export declare const SERVER_NAME = "meta-business-manager-mcp-server";
7
- export declare const SERVER_VERSION = "0.1.4";
7
+ export declare const SERVER_VERSION = "0.1.7";
8
8
  export declare const META_ERROR_CODES: {
9
9
  readonly UNKNOWN: 1;
10
10
  readonly SERVICE_TEMPORARILY_UNAVAILABLE: 2;
package/dist/constants.js CHANGED
@@ -4,7 +4,7 @@ export const CHARACTER_LIMIT = 25_000;
4
4
  export const DEFAULT_PAGE_LIMIT = 25;
5
5
  export const MAX_PAGE_LIMIT = 500;
6
6
  export const SERVER_NAME = "meta-business-manager-mcp-server";
7
- export const SERVER_VERSION = "0.1.4";
7
+ export const SERVER_VERSION = "0.1.7";
8
8
  export const META_ERROR_CODES = {
9
9
  UNKNOWN: 1,
10
10
  SERVICE_TEMPORARILY_UNAVAILABLE: 2,
@@ -42,6 +42,49 @@ export function findPlaceholders(value, path = []) {
42
42
  function formatPath(p) {
43
43
  return p.length ? p.join(".") : "(root)";
44
44
  }
45
+ /**
46
+ * Per-field recovery hints: when the AI passes a placeholder for a specific
47
+ * field, tell it the *exact* discovery tool that produces the right ID. This
48
+ * turns "I need your IG account ID" (which the AI then asks the user) into
49
+ * "call meta_ig_list_accounts (no args) to discover IG accounts" (which the
50
+ * AI can do autonomously).
51
+ *
52
+ * Each entry returns a single-sentence next-step the AI should execute.
53
+ */
54
+ const FIELD_RECOVERY = {
55
+ business_id: "Call `meta_business_list` (no args) to discover Business Manager IDs. The configured business is `133767790806312` (Hodusoft) — use that directly if you already know.",
56
+ ad_account_id: "Call `meta_ads_list_accounts business_id=133767790806312` to discover ad accounts. Returns IDs in the `act_<digits>` form (e.g. `act_146517954996436`).",
57
+ page_id: "Call `meta_page_list` (no args, defaults to source='assigned') to discover Pages assigned to the configured token.",
58
+ ig_user_id: "Call `meta_ig_list_accounts` (no args) to discover Instagram Business accounts linked to your Pages. No parameters required.",
59
+ pixel_id: "Call `meta_pixel_list business_id=133767790806312` to discover pixels.",
60
+ catalog_id: "Call `meta_catalog_list business_id=133767790806312 scope='owned'` to discover catalogs.",
61
+ waba_id: "Call `meta_whatsapp_list_wabas business_id=133767790806312` to discover WhatsApp Business Accounts.",
62
+ media_id: "Call `meta_ig_list_media ig_user_id=<ig_id>` to discover media. Use `meta_ig_list_accounts` first if you don't have ig_id.",
63
+ creative_id: "Call `meta_ads_list_ads ad_account_id=act_<id>` to discover creatives. Each ad's `creative.id` is the value you want.",
64
+ campaign_id: "Call `meta_ads_list_campaigns ad_account_id=act_<id>` to discover campaign IDs.",
65
+ adset_id: "Call `meta_ads_list_adsets ad_account_id=act_<id>` to discover ad-set IDs.",
66
+ post_id: "Call `meta_page_list_posts page_id=<page_id>` to discover post IDs. Post IDs use the `{page_id}_{post_id}` format.",
67
+ object_id: "Pass an ad account (`act_<digits>`), campaign, ad-set, or ad ID. Use `meta_business_overview business_id=133767790806312` to discover all of these in one call.",
68
+ };
69
+ /** Build a numbered recovery action list keyed off the placeholder field names. */
70
+ function buildRecoveryActions(placeholders) {
71
+ const fields = new Set();
72
+ for (const p of placeholders) {
73
+ const top = p.path[0];
74
+ if (typeof top === "string")
75
+ fields.add(top);
76
+ }
77
+ const lines = [];
78
+ for (const f of fields) {
79
+ const recovery = FIELD_RECOVERY[f];
80
+ if (recovery)
81
+ lines.push(`• ${f}: ${recovery}`);
82
+ }
83
+ if (lines.length === 0) {
84
+ return "Replace the placeholder values with real IDs. If you don't know the IDs, call `meta_business_overview business_id=133767790806312` first to discover everything in one call.";
85
+ }
86
+ return `DO NOT ask the user for these IDs — the server can discover them for you. Replace each placeholder by first calling the corresponding discovery tool:\n${lines.join("\n")}\n\nShortcut: a single call to \`meta_business_overview business_id=133767790806312\` returns all Pages, IG accounts, ad accounts, pixels, catalogs, and WABAs at once. Prefer that when several placeholders appear together.`;
87
+ }
45
88
  /**
46
89
  * Defence-in-depth input validator. Wraps a Zod object schema and:
47
90
  * 1. Detects placeholder strings (e.g. literal "<YOUR_PAGE_ID>") with a
@@ -62,7 +105,7 @@ export function validateInput(schema, args) {
62
105
  .join("; ");
63
106
  return {
64
107
  ok: false,
65
- error: toolError(`Input contains placeholder values that were not substituted: ${summary}`, `Replace placeholders with real IDs / values before retrying. Examples for this server: business_id "133767790806312"; ad_account_id "act_146517954996436"; page_id "138368686823692". If you don't know the correct ID, call meta_business_list_assets first to discover available assets.`, { placeholders }),
108
+ error: toolError(`Input contains placeholder values that were not substituted: ${summary}`, buildRecoveryActions(placeholders), { placeholders, recovery_actions: buildRecoveryActions(placeholders) }),
66
109
  };
67
110
  }
68
111
  const parsed = schema.safeParse(args);
@@ -37,9 +37,9 @@ export declare const inputSchema: z.ZodObject<{
37
37
  sort: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
38
38
  }, "strict", z.ZodTypeAny, {
39
39
  fields: string[];
40
+ object_id: string;
40
41
  limit: number;
41
42
  auto_paginate: boolean;
42
- object_id: string;
43
43
  level: "account" | "campaign" | "adset" | "ad";
44
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
45
  sort?: string[] | undefined;
@@ -9,9 +9,9 @@ export declare const inputSchema: z.ZodObject<{
9
9
  fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
10
10
  }, "strict", z.ZodTypeAny, {
11
11
  fields: string[];
12
+ business_id: string;
12
13
  limit: number;
13
14
  auto_paginate: boolean;
14
- business_id: string;
15
15
  scope: "owned" | "client" | "both";
16
16
  after?: string | undefined;
17
17
  }, {
@@ -11,22 +11,22 @@ export declare const inputSchema: z.ZodObject<{
11
11
  fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
12
12
  }, "strict", z.ZodTypeAny, {
13
13
  fields: string[];
14
+ ad_account_id: string;
14
15
  limit: number;
15
16
  auto_paginate: boolean;
16
- ad_account_id: string;
17
- after?: string | undefined;
18
- effective_status?: string[] | undefined;
19
17
  campaign_id?: string | undefined;
20
18
  adset_id?: string | undefined;
19
+ after?: string | undefined;
20
+ effective_status?: string[] | undefined;
21
21
  }, {
22
22
  ad_account_id: string;
23
23
  fields?: string[] | undefined;
24
+ campaign_id?: string | undefined;
25
+ adset_id?: string | undefined;
24
26
  limit?: number | undefined;
25
27
  after?: string | undefined;
26
28
  auto_paginate?: boolean | undefined;
27
29
  effective_status?: string[] | undefined;
28
- campaign_id?: string | undefined;
29
- adset_id?: string | undefined;
30
30
  }>;
31
31
  export type Input = z.infer<typeof inputSchema>;
32
32
  export declare const definition: {
@@ -10,20 +10,20 @@ export declare const inputSchema: z.ZodObject<{
10
10
  fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
11
11
  }, "strict", z.ZodTypeAny, {
12
12
  fields: string[];
13
+ ad_account_id: string;
13
14
  limit: number;
14
15
  auto_paginate: boolean;
15
- ad_account_id: string;
16
+ campaign_id?: string | undefined;
16
17
  after?: string | undefined;
17
18
  effective_status?: string[] | undefined;
18
- campaign_id?: string | undefined;
19
19
  }, {
20
20
  ad_account_id: string;
21
21
  fields?: string[] | undefined;
22
+ campaign_id?: string | undefined;
22
23
  limit?: number | undefined;
23
24
  after?: string | undefined;
24
25
  auto_paginate?: boolean | undefined;
25
26
  effective_status?: string[] | undefined;
26
- campaign_id?: string | undefined;
27
27
  }>;
28
28
  export type Input = z.infer<typeof inputSchema>;
29
29
  export declare const definition: {
@@ -9,9 +9,9 @@ export declare const inputSchema: z.ZodObject<{
9
9
  fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
10
10
  }, "strict", z.ZodTypeAny, {
11
11
  fields: string[];
12
+ ad_account_id: string;
12
13
  limit: number;
13
14
  auto_paginate: boolean;
14
- ad_account_id: string;
15
15
  after?: string | undefined;
16
16
  effective_status?: ("ACTIVE" | "PAUSED" | "DELETED" | "PENDING_REVIEW" | "DISAPPROVED" | "PREAPPROVED" | "PENDING_BILLING_INFO" | "CAMPAIGN_PAUSED" | "ARCHIVED" | "IN_PROCESS" | "WITH_ISSUES")[] | undefined;
17
17
  }, {
@@ -8,9 +8,9 @@ export declare const inputSchema: z.ZodObject<{
8
8
  fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
9
9
  }, "strict", z.ZodTypeAny, {
10
10
  fields: string[];
11
+ ad_account_id: string;
11
12
  limit: number;
12
13
  auto_paginate: boolean;
13
- ad_account_id: string;
14
14
  after?: string | undefined;
15
15
  }, {
16
16
  ad_account_id: string;
@@ -8,9 +8,9 @@ export declare const inputSchema: z.ZodObject<{
8
8
  business_id: z.ZodString;
9
9
  }, "strict", z.ZodTypeAny, {
10
10
  fields: string[];
11
+ business_id: string;
11
12
  limit: number;
12
13
  auto_paginate: boolean;
13
- business_id: string;
14
14
  after?: string | undefined;
15
15
  }, {
16
16
  business_id: string;
@@ -9,9 +9,9 @@ export declare const inputSchema: z.ZodObject<{
9
9
  filter: z.ZodOptional<z.ZodString>;
10
10
  }, "strict", z.ZodTypeAny, {
11
11
  fields: string[];
12
+ catalog_id: string;
12
13
  limit: number;
13
14
  auto_paginate: boolean;
14
- catalog_id: string;
15
15
  filter?: string | undefined;
16
16
  after?: string | undefined;
17
17
  }, {
@@ -9,9 +9,9 @@ export declare const inputSchema: z.ZodObject<{
9
9
  fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
10
10
  }, "strict", z.ZodTypeAny, {
11
11
  fields: string[];
12
+ business_id: string;
12
13
  limit: number;
13
14
  auto_paginate: boolean;
14
- business_id: string;
15
15
  scope: "owned" | "client" | "both";
16
16
  after?: string | undefined;
17
17
  }, {
@@ -7,8 +7,8 @@ export declare const inputSchema: z.ZodObject<{
7
7
  metric_type: z.ZodDefault<z.ZodEnum<["total_value", "time_series"]>>;
8
8
  timeframe: z.ZodDefault<z.ZodEnum<["last_14_days", "last_30_days", "last_90_days", "prev_month", "this_month", "this_week"]>>;
9
9
  }, "strict", z.ZodTypeAny, {
10
- metric: "follower_demographics" | "engaged_audience_demographics" | "reached_audience_demographics";
11
10
  ig_user_id: string;
11
+ metric: "follower_demographics" | "engaged_audience_demographics" | "reached_audience_demographics";
12
12
  breakdown: "age" | "gender" | "country" | "city" | "audience_city";
13
13
  metric_type: "total_value" | "time_series";
14
14
  timeframe: "this_month" | "last_14_days" | "last_30_days" | "last_90_days" | "prev_month" | "this_week";
@@ -4,8 +4,8 @@ export declare const inputSchema: z.ZodObject<{
4
4
  media_id: z.ZodString;
5
5
  metrics: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
6
6
  }, "strict", z.ZodTypeAny, {
7
- metrics: string[];
8
7
  media_id: string;
8
+ metrics: string[];
9
9
  }, {
10
10
  media_id: string;
11
11
  metrics?: string[] | undefined;
@@ -8,9 +8,9 @@ export declare const inputSchema: z.ZodObject<{
8
8
  fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
9
9
  }, "strict", z.ZodTypeAny, {
10
10
  fields: string[];
11
+ ig_user_id: string;
11
12
  limit: number;
12
13
  auto_paginate: boolean;
13
- ig_user_id: string;
14
14
  after?: string | undefined;
15
15
  }, {
16
16
  ig_user_id: string;
@@ -0,0 +1,41 @@
1
+ import { z } from "zod";
2
+ import type { ToolContext } from "../../context.js";
3
+ export declare const inputSchema: z.ZodObject<{
4
+ include_instagram: z.ZodDefault<z.ZodBoolean>;
5
+ include_facebook: z.ZodDefault<z.ZodBoolean>;
6
+ max_pages: z.ZodDefault<z.ZodNumber>;
7
+ include_post_insights: z.ZodDefault<z.ZodBoolean>;
8
+ include_media_insights: z.ZodDefault<z.ZodBoolean>;
9
+ }, "strict", z.ZodTypeAny, {
10
+ max_pages: number;
11
+ include_instagram: boolean;
12
+ include_facebook: boolean;
13
+ include_post_insights: boolean;
14
+ include_media_insights: boolean;
15
+ }, {
16
+ max_pages?: number | undefined;
17
+ include_instagram?: boolean | undefined;
18
+ include_facebook?: boolean | undefined;
19
+ include_post_insights?: boolean | undefined;
20
+ include_media_insights?: boolean | undefined;
21
+ }>;
22
+ export type Input = z.infer<typeof inputSchema>;
23
+ export declare const definition: {
24
+ readonly name: "meta_latest_posts_summary";
25
+ readonly title: "Latest post performance across every social account";
26
+ readonly description: "Workflow tool that answers \"how did my latest post on each social account perform?\" in a single call.\n\nInternally walks:\n1. /me/assigned_pages → every Facebook Page assigned to this token\n2. For each Page: latest published post + (optionally) its insights (impressions, reach, engaged_users, clicks)\n3. For each Page: linked instagram_business_account\n4. For each IG: latest media + (optionally) its insights (reach, views, likes, comments, shares, saved, total_interactions)\n\nReturns a unified array of '{platform, owner, latest_post, insights}' entries, plus a per-section error map so a single failure doesn't blank the report.\n\n**Use this as the FIRST tool call** when the user asks anything like:\n- \"How are my social media accounts performing?\"\n- \"What's the engagement on my latest posts?\"\n- \"How did my most recent content do?\"\n\nIt removes the need to discover Page / IG IDs separately, and avoids the N+1 sequence of meta_page_list → meta_page_list_posts → meta_page_get_post_insights × pages × posts.";
27
+ readonly inputSchema: {
28
+ include_instagram: z.ZodDefault<z.ZodBoolean>;
29
+ include_facebook: z.ZodDefault<z.ZodBoolean>;
30
+ max_pages: z.ZodDefault<z.ZodNumber>;
31
+ include_post_insights: z.ZodDefault<z.ZodBoolean>;
32
+ include_media_insights: z.ZodDefault<z.ZodBoolean>;
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,195 @@
1
+ import { z } from "zod";
2
+ import { MetaError } from "../../errors.js";
3
+ import { jsonBlock, toolResult } from "../../helpers/format.js";
4
+ export const inputSchema = z
5
+ .object({
6
+ include_instagram: z
7
+ .boolean()
8
+ .default(true)
9
+ .describe("Walk linked Instagram Business accounts via assigned Pages and include their latest media."),
10
+ include_facebook: z
11
+ .boolean()
12
+ .default(true)
13
+ .describe("Walk assigned Facebook Pages and include their latest post."),
14
+ max_pages: z
15
+ .number()
16
+ .int()
17
+ .min(1)
18
+ .max(20)
19
+ .default(10)
20
+ .describe("Cap on Pages walked. Each Page is one extra Graph call to fetch latest post + insights."),
21
+ include_post_insights: z
22
+ .boolean()
23
+ .default(true)
24
+ .describe("Include per-post insights (impressions, reach, engaged users). Disable to halve the Graph calls."),
25
+ include_media_insights: z
26
+ .boolean()
27
+ .default(true)
28
+ .describe("Include per-IG-media insights (reach, views, likes). Disable if your app lacks instagram_manage_insights Standard Access."),
29
+ })
30
+ .strict();
31
+ export const definition = {
32
+ name: "meta_latest_posts_summary",
33
+ title: "Latest post performance across every social account",
34
+ description: `Workflow tool that answers "how did my latest post on each social account perform?" in a single call.
35
+
36
+ Internally walks:
37
+ 1. /me/assigned_pages → every Facebook Page assigned to this token
38
+ 2. For each Page: latest published post + (optionally) its insights (impressions, reach, engaged_users, clicks)
39
+ 3. For each Page: linked instagram_business_account
40
+ 4. For each IG: latest media + (optionally) its insights (reach, views, likes, comments, shares, saved, total_interactions)
41
+
42
+ Returns a unified array of '{platform, owner, latest_post, insights}' entries, plus a per-section error map so a single failure doesn't blank the report.
43
+
44
+ **Use this as the FIRST tool call** when the user asks anything like:
45
+ - "How are my social media accounts performing?"
46
+ - "What's the engagement on my latest posts?"
47
+ - "How did my most recent content do?"
48
+
49
+ It removes the need to discover Page / IG IDs separately, and avoids the N+1 sequence of meta_page_list → meta_page_list_posts → meta_page_get_post_insights × pages × posts.`,
50
+ inputSchema: inputSchema.shape,
51
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
52
+ };
53
+ export async function handler(input, ctx) {
54
+ const started = Date.now();
55
+ const entries = [];
56
+ const errors = {};
57
+ // Step 1: list assigned Pages (used by both Facebook and Instagram paths).
58
+ let pages = [];
59
+ try {
60
+ const pagesResp = await ctx.graph.get({
61
+ path: "me/assigned_pages",
62
+ params: { fields: "id,name", limit: input.max_pages },
63
+ });
64
+ pages = Array.isArray(pagesResp.data) ? pagesResp.data : [];
65
+ }
66
+ catch (err) {
67
+ const e = err instanceof MetaError ? err : new MetaError(err.message);
68
+ errors["assigned_pages"] = { error: e.message, hint: e.hint };
69
+ }
70
+ // Step 2: for each Page, fetch latest published post + insights (in parallel).
71
+ if (input.include_facebook && pages.length > 0) {
72
+ const fbResults = await Promise.all(pages.map(async (p) => {
73
+ try {
74
+ const pageToken = await ctx.graph.getPageAccessToken(p.id);
75
+ const postsPage = await ctx.graph.get({
76
+ path: `${p.id}/published_posts`,
77
+ params: {
78
+ fields: "id,message,created_time,permalink_url,status_type,attachments{media_type,title,url},reactions.summary(true).limit(0),shares,comments.summary(true).limit(0)",
79
+ limit: 1,
80
+ },
81
+ accessTokenOverride: pageToken,
82
+ });
83
+ const latest = postsPage.data?.[0] ?? null;
84
+ let insights = null;
85
+ if (latest && input.include_post_insights) {
86
+ try {
87
+ const insResp = await ctx.graph.get({
88
+ path: `${latest.id}/insights`,
89
+ params: {
90
+ metric: "post_impressions,post_impressions_unique,post_engaged_users,post_clicks,post_reactions_by_type_total",
91
+ },
92
+ accessTokenOverride: pageToken,
93
+ });
94
+ insights = { data: insResp.data ?? [] };
95
+ }
96
+ catch (err) {
97
+ const e = err instanceof MetaError ? err : new MetaError(err.message);
98
+ insights = { error: e.message, hint: e.hint };
99
+ }
100
+ }
101
+ return {
102
+ platform: "facebook",
103
+ page_id: p.id,
104
+ page_name: p.name,
105
+ latest_post: latest,
106
+ insights,
107
+ };
108
+ }
109
+ catch (err) {
110
+ const e = err instanceof MetaError ? err : new MetaError(err.message);
111
+ return {
112
+ platform: "facebook",
113
+ page_id: p.id,
114
+ page_name: p.name,
115
+ latest_post: null,
116
+ insights: { error: e.message, hint: e.hint },
117
+ };
118
+ }
119
+ }));
120
+ entries.push(...fbResults);
121
+ }
122
+ // Step 3: resolve IG via each Page, then fetch latest media + insights.
123
+ if (input.include_instagram && pages.length > 0) {
124
+ const igLinks = await Promise.all(pages.map(async (p) => {
125
+ try {
126
+ const d = await ctx.graph.get({
127
+ path: p.id,
128
+ params: { fields: "id,name,instagram_business_account{id,username,name}" },
129
+ });
130
+ return d.instagram_business_account
131
+ ? { ig: d.instagram_business_account, sourcePage: p }
132
+ : null;
133
+ }
134
+ catch {
135
+ return null;
136
+ }
137
+ }));
138
+ const igAccounts = igLinks.filter((x) => x !== null);
139
+ const igResults = await Promise.all(igAccounts.map(async (link) => {
140
+ try {
141
+ const mediaPage = await ctx.graph.get({
142
+ path: `${link.ig.id}/media`,
143
+ params: {
144
+ fields: "id,media_type,media_product_type,caption,media_url,thumbnail_url,permalink,timestamp,like_count,comments_count",
145
+ limit: 1,
146
+ },
147
+ });
148
+ const latest = mediaPage.data?.[0] ?? null;
149
+ let insights = null;
150
+ if (latest && input.include_media_insights) {
151
+ try {
152
+ const insResp = await ctx.graph.get({
153
+ path: `${latest.id}/insights`,
154
+ params: { metric: "reach,views,likes,comments,shares,saved,total_interactions" },
155
+ });
156
+ insights = { data: insResp.data ?? [] };
157
+ }
158
+ catch (err) {
159
+ const e = err instanceof MetaError ? err : new MetaError(err.message);
160
+ insights = { error: e.message, hint: e.hint };
161
+ }
162
+ }
163
+ return {
164
+ platform: "instagram",
165
+ ig_user_id: link.ig.id,
166
+ ig_username: link.ig.username,
167
+ page_id: link.sourcePage.id,
168
+ page_name: link.sourcePage.name,
169
+ latest_post: latest,
170
+ insights,
171
+ };
172
+ }
173
+ catch (err) {
174
+ const e = err instanceof MetaError ? err : new MetaError(err.message);
175
+ return {
176
+ platform: "instagram",
177
+ ig_user_id: link.ig.id,
178
+ ig_username: link.ig.username,
179
+ latest_post: null,
180
+ insights: { error: e.message, hint: e.hint },
181
+ };
182
+ }
183
+ }));
184
+ entries.push(...igResults);
185
+ }
186
+ const structured = {
187
+ generated_at: new Date().toISOString(),
188
+ latency_ms: Date.now() - started,
189
+ facebook_count: entries.filter((e) => e.platform === "facebook").length,
190
+ instagram_count: entries.filter((e) => e.platform === "instagram").length,
191
+ entries,
192
+ errors: Object.keys(errors).length > 0 ? errors : null,
193
+ };
194
+ return toolResult(structured, jsonBlock(structured));
195
+ }
@@ -11,10 +11,10 @@ export declare const inputSchema: z.ZodObject<{
11
11
  fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
12
12
  }, "strict", z.ZodTypeAny, {
13
13
  fields: string[];
14
+ page_id: string;
14
15
  limit: number;
15
16
  auto_paginate: boolean;
16
17
  edge: "posts" | "published_posts" | "feed";
17
- page_id: string;
18
18
  since?: string | undefined;
19
19
  until?: string | undefined;
20
20
  after?: string | undefined;
@@ -8,9 +8,9 @@ export declare const inputSchema: z.ZodObject<{
8
8
  fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
9
9
  }, "strict", z.ZodTypeAny, {
10
10
  fields: string[];
11
+ page_id: string;
11
12
  limit: number;
12
13
  auto_paginate: boolean;
13
- page_id: string;
14
14
  after?: string | undefined;
15
15
  }, {
16
16
  page_id: string;
@@ -8,9 +8,9 @@ export declare const inputSchema: z.ZodObject<{
8
8
  fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
9
9
  }, "strict", z.ZodTypeAny, {
10
10
  fields: string[];
11
+ page_id: string;
11
12
  limit: number;
12
13
  auto_paginate: boolean;
13
- page_id: string;
14
14
  after?: string | undefined;
15
15
  }, {
16
16
  page_id: string;
@@ -8,9 +8,9 @@ export declare const inputSchema: z.ZodObject<{
8
8
  fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
9
9
  }, "strict", z.ZodTypeAny, {
10
10
  fields: string[];
11
+ business_id: string;
11
12
  limit: number;
12
13
  auto_paginate: boolean;
13
- business_id: string;
14
14
  after?: string | undefined;
15
15
  }, {
16
16
  business_id: string;
@@ -45,6 +45,7 @@ import * as whatsappListTemplates from "./whatsapp/list-templates.js";
45
45
  import * as whatsappGetAnalytics from "./whatsapp/get-analytics.js";
46
46
  // Overview
47
47
  import * as businessOverview from "./overview/business-overview.js";
48
+ import * as latestPostsSummary from "./overview/latest-posts-summary.js";
48
49
  const TOOLS = [
49
50
  // Token + meta (3)
50
51
  tokenInspect, tokenHealth, graphRead,
@@ -64,8 +65,8 @@ const TOOLS = [
64
65
  catalogList, catalogListProducts, catalogGetDiagnostics,
65
66
  // WhatsApp (4)
66
67
  whatsappListWabas, whatsappListPhoneNumbers, whatsappListTemplates, whatsappGetAnalytics,
67
- // Overview (1)
68
- businessOverview,
68
+ // Overview (2)
69
+ businessOverview, latestPostsSummary,
69
70
  ].map((m) => m);
70
71
  export function registerTools(server, ctx) {
71
72
  const names = [];
@@ -8,9 +8,9 @@ export declare const inputSchema: z.ZodObject<{
8
8
  fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
9
9
  }, "strict", z.ZodTypeAny, {
10
10
  fields: string[];
11
+ waba_id: string;
11
12
  limit: number;
12
13
  auto_paginate: boolean;
13
- waba_id: string;
14
14
  after?: string | undefined;
15
15
  }, {
16
16
  waba_id: string;
@@ -9,9 +9,9 @@ export declare const inputSchema: z.ZodObject<{
9
9
  fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
10
10
  }, "strict", z.ZodTypeAny, {
11
11
  fields: string[];
12
+ waba_id: string;
12
13
  limit: number;
13
14
  auto_paginate: boolean;
14
- waba_id: string;
15
15
  status?: "PAUSED" | "DELETED" | "APPROVED" | "PENDING" | "REJECTED" | "DISABLED" | "IN_APPEAL" | undefined;
16
16
  after?: string | undefined;
17
17
  }, {
@@ -9,9 +9,9 @@ export declare const inputSchema: z.ZodObject<{
9
9
  fields: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
10
10
  }, "strict", z.ZodTypeAny, {
11
11
  fields: string[];
12
+ business_id: string;
12
13
  limit: number;
13
14
  auto_paginate: boolean;
14
- business_id: string;
15
15
  scope: "owned" | "client" | "both";
16
16
  after?: string | undefined;
17
17
  }, {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@praise25/meta-mcp-server",
3
3
  "description": "Read-only Model Context Protocol server for Meta Business Manager — Pages, Instagram, Ads insights, Pixels, Catalog, WhatsApp.",
4
- "version": "0.1.5",
4
+ "version": "0.1.7",
5
5
  "author": "Stephen A.",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/feladeveloper/meta-mcp-server#readme",