@thenavidm/facebook-ad-library-mcp 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 (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +359 -0
  3. package/SKILL.md +115 -0
  4. package/dist/adlibrary/harvest.d.ts +35 -0
  5. package/dist/adlibrary/harvest.js +153 -0
  6. package/dist/adlibrary/harvest.js.map +1 -0
  7. package/dist/adlibrary/normalize.d.ts +25 -0
  8. package/dist/adlibrary/normalize.js +221 -0
  9. package/dist/adlibrary/normalize.js.map +1 -0
  10. package/dist/adlibrary/types.d.ts +101 -0
  11. package/dist/adlibrary/types.js +17 -0
  12. package/dist/adlibrary/types.js.map +1 -0
  13. package/dist/adlibrary/url.d.ts +10 -0
  14. package/dist/adlibrary/url.js +29 -0
  15. package/dist/adlibrary/url.js.map +1 -0
  16. package/dist/archive/client.d.ts +45 -0
  17. package/dist/archive/client.js +124 -0
  18. package/dist/archive/client.js.map +1 -0
  19. package/dist/backends/apify.d.ts +37 -0
  20. package/dist/backends/apify.js +152 -0
  21. package/dist/backends/apify.js.map +1 -0
  22. package/dist/backends/browser.d.ts +51 -0
  23. package/dist/backends/browser.js +202 -0
  24. package/dist/backends/browser.js.map +1 -0
  25. package/dist/backends/index.d.ts +8 -0
  26. package/dist/backends/index.js +25 -0
  27. package/dist/backends/index.js.map +1 -0
  28. package/dist/backends/scrapecreators.d.ts +29 -0
  29. package/dist/backends/scrapecreators.js +185 -0
  30. package/dist/backends/scrapecreators.js.map +1 -0
  31. package/dist/config.d.ts +18 -0
  32. package/dist/config.js +33 -0
  33. package/dist/config.js.map +1 -0
  34. package/dist/doctor.d.ts +10 -0
  35. package/dist/doctor.js +131 -0
  36. package/dist/doctor.js.map +1 -0
  37. package/dist/errors.d.ts +16 -0
  38. package/dist/errors.js +25 -0
  39. package/dist/errors.js.map +1 -0
  40. package/dist/format/ads.d.ts +16 -0
  41. package/dist/format/ads.js +97 -0
  42. package/dist/format/ads.js.map +1 -0
  43. package/dist/index.d.ts +9 -0
  44. package/dist/index.js +84 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/server.d.ts +21 -0
  47. package/dist/server.js +156 -0
  48. package/dist/server.js.map +1 -0
  49. package/dist/store/snapshots.d.ts +29 -0
  50. package/dist/store/snapshots.js +83 -0
  51. package/dist/store/snapshots.js.map +1 -0
  52. package/dist/tools/index.d.ts +5 -0
  53. package/dist/tools/index.js +14 -0
  54. package/dist/tools/index.js.map +1 -0
  55. package/dist/tools/kit.d.ts +70 -0
  56. package/dist/tools/kit.js +90 -0
  57. package/dist/tools/kit.js.map +1 -0
  58. package/dist/tools/search.d.ts +5 -0
  59. package/dist/tools/search.js +165 -0
  60. package/dist/tools/search.js.map +1 -0
  61. package/dist/tools/session.d.ts +3 -0
  62. package/dist/tools/session.js +23 -0
  63. package/dist/tools/session.js.map +1 -0
  64. package/dist/tools/track.d.ts +9 -0
  65. package/dist/tools/track.js +68 -0
  66. package/dist/tools/track.js.map +1 -0
  67. package/dist/tools/transparency.d.ts +5 -0
  68. package/dist/tools/transparency.js +53 -0
  69. package/dist/tools/transparency.js.map +1 -0
  70. package/dist/transport/http.d.ts +23 -0
  71. package/dist/transport/http.js +88 -0
  72. package/dist/transport/http.js.map +1 -0
  73. package/package.json +64 -0
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Finding ads and advertisers. The tools people actually reach for.
3
+ */
4
+ import { z } from "zod";
5
+ import { buildUrl } from "../adlibrary/url.js";
6
+ import { detail, summarise } from "../format/ads.js";
7
+ import { defineTool, searchArgs } from "./kit.js";
8
+ const searchAds = defineTool({
9
+ name: "search_ads",
10
+ title: "Search the Ad Library",
11
+ description: "Search Meta's public Ad Library by keyword, or list every ad from one advertiser by " +
12
+ "passing page_id. Returns advertiser, copy, creative count, destination domain, call to " +
13
+ "action, platforms and how long each ad has run. Works for ordinary commercial " +
14
+ "advertisers in any country. Spend and reach are null outside the EU, which is correct " +
15
+ "rather than a failure: that data is only published for EU and political ads.",
16
+ schema: {
17
+ query: z
18
+ .string()
19
+ .optional()
20
+ .describe("Keyword: a brand, product or angle. Optional if page_id is given."),
21
+ page_id: z
22
+ .string()
23
+ .optional()
24
+ .describe("Advertiser Page ID. Returns that Page's ads instead of a keyword search. " +
25
+ "Use list_advertisers first if you only know the brand name."),
26
+ ...searchArgs,
27
+ },
28
+ handler: async (args, ctx) => {
29
+ const result = await ctx.backend.search({
30
+ query: args.query,
31
+ pageId: args.page_id,
32
+ country: args.country,
33
+ activeStatus: args.active_status,
34
+ adType: args.ad_type,
35
+ mediaType: args.media_type,
36
+ limit: args.limit,
37
+ });
38
+ // Record against the advertiser so diff_advertiser has a baseline later.
39
+ if (args.page_id && result.ads.length > 0) {
40
+ await ctx.store.record(args.page_id, result.ads);
41
+ }
42
+ return {
43
+ backend: result.backend,
44
+ count: result.count,
45
+ total_available: result.totalAvailable,
46
+ has_more: result.hasMore,
47
+ cursor: result.cursor,
48
+ url: result.url,
49
+ note: result.note,
50
+ ads: result.ads.map(summarise),
51
+ };
52
+ },
53
+ });
54
+ const listAdvertisers = defineTool({
55
+ name: "list_advertisers",
56
+ title: "Find advertisers",
57
+ description: "Resolve a brand name to the advertiser Pages actually running ads for it, ranked by how " +
58
+ "many ads each is running. Use this first when you know the brand but not its Page ID, " +
59
+ "then pass that ID to search_ads to get everything they run.",
60
+ schema: {
61
+ query: z.string().describe("Brand or company name to look up."),
62
+ country: z.string().length(2).optional().describe("Two-letter country code. Default US."),
63
+ },
64
+ handler: async (args, ctx) => {
65
+ const advertisers = await ctx.backend.listAdvertisers(args.query, args.country ?? "US");
66
+ return {
67
+ count: advertisers.length,
68
+ advertisers: advertisers.map((a) => ({
69
+ page_id: a.pageId,
70
+ name: a.pageName,
71
+ url: a.pageUrl,
72
+ likes: a.pageLikes,
73
+ verified: a.verified,
74
+ category: a.category,
75
+ ads_seen: a.adCount,
76
+ })),
77
+ note: advertisers.length === 0
78
+ ? "No advertisers found running ads for that term in this country. Try a broader " +
79
+ "term, or active_status 'all' via search_ads."
80
+ : undefined,
81
+ };
82
+ },
83
+ });
84
+ const getAd = defineTool({
85
+ name: "get_ad",
86
+ title: "Get one ad in full",
87
+ description: "Fetch a single ad by its Ad Library ID, with every creative, all copy variants, the " +
88
+ "full destination URL and any transparency data. Use this after search_ads when one ad " +
89
+ "is worth studying properly.",
90
+ schema: {
91
+ library_id: z.string().describe("The Ad Library ID, the long number in an ad's URL."),
92
+ country: z.string().length(2).optional().describe("Two-letter country code. Default US."),
93
+ },
94
+ handler: async (args, ctx) => {
95
+ const ad = await ctx.backend.getAd(args.library_id, args.country ?? "US");
96
+ if (!ad) {
97
+ return {
98
+ found: false,
99
+ library_id: args.library_id,
100
+ note: "No ad with that ID came back. It may have been taken down, or the ID may be wrong.",
101
+ };
102
+ }
103
+ return { found: true, ad: detail(ad) };
104
+ },
105
+ });
106
+ const transcribeAd = defineTool({
107
+ name: "transcribe_ad",
108
+ title: "Transcribe a video ad",
109
+ description: "Speech to text for a video ad, so its spoken script becomes readable. Only the " +
110
+ "scrapecreators backend can do this. On any other backend it explains that rather than " +
111
+ "failing silently.",
112
+ schema: {
113
+ library_id: z.string().describe("The Ad Library ID of a video ad."),
114
+ },
115
+ handler: async (args, ctx) => {
116
+ if (!ctx.backend.transcribe) {
117
+ return {
118
+ available: false,
119
+ backend: ctx.backend.name,
120
+ note: "Transcription is only available on the scrapecreators backend. Set " +
121
+ "SCRAPECREATORS_API_KEY and FBADS_BACKEND=scrapecreators to use it. The video URLs " +
122
+ "from get_ad can be transcribed with your own tooling on any backend.",
123
+ };
124
+ }
125
+ const transcript = await ctx.backend.transcribe(args.library_id);
126
+ return {
127
+ available: true,
128
+ library_id: args.library_id,
129
+ transcript,
130
+ note: transcript ? undefined : "No transcript came back. The ad may not be a video.",
131
+ };
132
+ },
133
+ });
134
+ const adLibraryUrl = defineTool({
135
+ name: "ad_library_url",
136
+ title: "Build an Ad Library URL",
137
+ description: "Turn a set of filters into the public Ad Library URL, so a person can open the same " +
138
+ "search in a browser and check the results independently. Useful when a search comes " +
139
+ "back empty and you need to tell 'no results' apart from 'blocked'.",
140
+ schema: {
141
+ query: z.string().optional().describe("Keyword to search for."),
142
+ page_id: z.string().optional().describe("Advertiser Page ID, instead of a keyword."),
143
+ country: z.string().length(2).optional().describe("Two-letter country code. Default US."),
144
+ active_status: z.enum(["active", "inactive", "all"]).optional(),
145
+ media_type: z.enum(["all", "image", "meme", "video", "none"]).optional(),
146
+ },
147
+ touchesNetwork: false,
148
+ handler: async (args) => ({
149
+ url: buildUrl({
150
+ query: args.query,
151
+ pageId: args.page_id,
152
+ country: args.country,
153
+ activeStatus: args.active_status,
154
+ mediaType: args.media_type,
155
+ }),
156
+ }),
157
+ });
158
+ export const SEARCH_TOOLS = [
159
+ searchAds,
160
+ listAdvertisers,
161
+ getAd,
162
+ transcribeAd,
163
+ adLibraryUrl,
164
+ ];
165
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ /** What this server is configured to do right now. */
2
+ import { type AnyToolSpec } from "./kit.js";
3
+ export declare const SESSION_TOOLS: AnyToolSpec[];
@@ -0,0 +1,23 @@
1
+ /** What this server is configured to do right now. */
2
+ import { defineTool } from "./kit.js";
3
+ const backendStatus = defineTool({
4
+ name: "backend_status",
5
+ title: "Backend status",
6
+ description: "Which backend is serving requests, whether it costs money per call, and which optional " +
7
+ "features are available in this configuration. Call this when a tool reports something " +
8
+ "unavailable, before telling the user it cannot be done.",
9
+ schema: {},
10
+ touchesNetwork: false,
11
+ handler: async (_args, ctx) => ({
12
+ backend: ctx.backend.name,
13
+ needs_api_key: ctx.backend.needsKey,
14
+ costs_money_per_request: ctx.backend.needsKey,
15
+ transcription_available: typeof ctx.backend.transcribe === "function",
16
+ eu_transparency_available: Boolean(ctx.config.archiveToken),
17
+ tracked_advertisers: await ctx.store.trackedPages(),
18
+ note: "The browser backend is free and drives Chromium locally, so it cannot run in a " +
19
+ "serverless function. Provider backends bill per ad returned and run anywhere.",
20
+ }),
21
+ });
22
+ export const SESSION_TOOLS = [backendStatus];
23
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/tools/session.ts"],"names":[],"mappings":"AAAA,sDAAsD;AAEtD,OAAO,EAAE,UAAU,EAAoB,MAAM,UAAU,CAAC;AAExD,MAAM,aAAa,GAAG,UAAU,CAAC;IAC/B,IAAI,EAAE,gBAAgB;IACtB,KAAK,EAAE,gBAAgB;IACvB,WAAW,EACT,yFAAyF;QACzF,wFAAwF;QACxF,yDAAyD;IAC3D,MAAM,EAAE,EAAE;IACV,cAAc,EAAE,KAAK;IACrB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;QAC9B,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI;QACzB,aAAa,EAAE,GAAG,CAAC,OAAO,CAAC,QAAQ;QACnC,uBAAuB,EAAE,GAAG,CAAC,OAAO,CAAC,QAAQ;QAC7C,uBAAuB,EAAE,OAAO,GAAG,CAAC,OAAO,CAAC,UAAU,KAAK,UAAU;QACrE,yBAAyB,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC;QAC3D,mBAAmB,EAAE,MAAM,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE;QACnD,IAAI,EACF,iFAAiF;YACjF,+EAA+E;KAClF,CAAC;CACH,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,aAAa,GAAkB,CAAC,aAAa,CAAkB,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Change over time.
3
+ *
4
+ * Every other tool answers "what is running". This one answers "what changed",
5
+ * which is the question worth asking about a competitor and the one no snapshot
6
+ * can reach. It needs a baseline, so the first call records one.
7
+ */
8
+ import { type AnyToolSpec } from "./kit.js";
9
+ export declare const TRACK_TOOLS: AnyToolSpec[];
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Change over time.
3
+ *
4
+ * Every other tool answers "what is running". This one answers "what changed",
5
+ * which is the question worth asking about a competitor and the one no snapshot
6
+ * can reach. It needs a baseline, so the first call records one.
7
+ */
8
+ import { z } from "zod";
9
+ import { summarise } from "../format/ads.js";
10
+ import { defineTool } from "./kit.js";
11
+ const diffAdvertiser = defineTool({
12
+ name: "diff_advertiser",
13
+ title: "What changed for an advertiser",
14
+ description: "Compare an advertiser's ads now against the last time this server looked, and report " +
15
+ "what they started and stopped running. The first call on a Page records a baseline and " +
16
+ "reports nothing changed, which is expected: call it again later to see movement. This " +
17
+ "is the only tool here that answers 'what changed' rather than 'what is running'.",
18
+ schema: {
19
+ page_id: z.string().describe("Advertiser Page ID to compare. Get it from list_advertisers."),
20
+ country: z.string().length(2).optional().describe("Two-letter country code. Default US."),
21
+ limit: z
22
+ .number()
23
+ .int()
24
+ .min(1)
25
+ .max(200)
26
+ .optional()
27
+ .describe("How many ads to pull for the comparison. Keep it consistent between calls."),
28
+ },
29
+ handler: async (args, ctx) => {
30
+ const before = await ctx.store.snapshot(args.page_id);
31
+ const result = await ctx.backend.search({
32
+ pageId: args.page_id,
33
+ country: args.country,
34
+ activeStatus: "all",
35
+ limit: args.limit ?? 60,
36
+ });
37
+ await ctx.store.record(args.page_id, result.ads);
38
+ if (before.size === 0) {
39
+ return {
40
+ baseline_created: true,
41
+ page_id: args.page_id,
42
+ tracked_now: result.count,
43
+ note: "No earlier snapshot existed, so this call recorded a baseline. Run it again later " +
44
+ "to see what started and stopped.",
45
+ };
46
+ }
47
+ const now = new Map(result.ads.map((ad) => [ad.libraryId, ad]));
48
+ const started = result.ads.filter((ad) => !before.has(ad.libraryId));
49
+ const stopped = [...before.values()].filter((ad) => !now.has(ad.libraryId));
50
+ return {
51
+ baseline_created: false,
52
+ page_id: args.page_id,
53
+ previously_tracked: before.size,
54
+ running_now: now.size,
55
+ started_running: started.map(summarise),
56
+ no_longer_seen: stopped.map((ad) => ({
57
+ ...summarise(ad),
58
+ first_seen: new Date(ad.firstSeen).toISOString().slice(0, 10),
59
+ last_seen: new Date(ad.lastSeen).toISOString().slice(0, 10),
60
+ })),
61
+ note: "'no_longer_seen' means the ad was absent from this result set. That usually means it " +
62
+ "stopped, but a smaller limit or a different country explains it too. Keep both " +
63
+ "consistent between calls for a clean comparison.",
64
+ };
65
+ },
66
+ });
67
+ export const TRACK_TOOLS = [diffAdvertiser];
68
+ //# sourceMappingURL=track.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"track.js","sourceRoot":"","sources":["../../src/tools/track.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Meta's official transparency data, layered on top of whichever backend is active.
3
+ */
4
+ import { type AnyToolSpec } from "./kit.js";
5
+ export declare const TRANSPARENCY_TOOLS: AnyToolSpec[];
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Meta's official transparency data, layered on top of whichever backend is active.
3
+ */
4
+ import { z } from "zod";
5
+ import { ArchiveClient } from "../archive/client.js";
6
+ import { AdLibraryError } from "../errors.js";
7
+ import { defineTool } from "./kit.js";
8
+ const getEuTransparency = defineTool({
9
+ name: "get_eu_transparency",
10
+ title: "EU spend and reach",
11
+ description: "Spend, impressions, reach and demographic breakdown from Meta's official Ad Library API. " +
12
+ "Covers ads delivered in the EU and political or issue ads anywhere. Returns nothing for " +
13
+ "an ordinary US commercial ad because that data is not published, which is the correct " +
14
+ "answer rather than an error. Needs META_ADS_ARCHIVE_TOKEN, which is free.",
15
+ schema: {
16
+ query: z.string().optional().describe("Brand or advertiser name to look up."),
17
+ page_ids: z
18
+ .string()
19
+ .optional()
20
+ .describe("Comma-separated Page IDs, as a more precise alternative to query."),
21
+ country: z
22
+ .string()
23
+ .length(2)
24
+ .optional()
25
+ .describe("Two-letter EU country code, e.g. DE, FR, IE. Default DE."),
26
+ limit: z.number().int().min(1).max(100).optional().describe("Maximum ads to return."),
27
+ },
28
+ handler: async (args, ctx) => {
29
+ if (!ctx.config.archiveToken) {
30
+ throw new AdLibraryError("No Meta Ad Library API token is configured.", {
31
+ hint: "Set META_ADS_ARCHIVE_TOKEN. It is free: create a Meta app and generate a token. " +
32
+ "Every other tool works without it, they just cannot return spend or reach.",
33
+ });
34
+ }
35
+ const client = new ArchiveClient(ctx.config.archiveToken);
36
+ const result = await client.search({
37
+ query: args.query,
38
+ pageIds: args.page_ids?.split(",").map((id) => id.trim()).filter(Boolean),
39
+ country: args.country,
40
+ limit: args.limit,
41
+ });
42
+ return {
43
+ count: result.count,
44
+ country: result.country,
45
+ fields_returned: result.fieldsReturned,
46
+ next: result.next,
47
+ note: result.note,
48
+ ads: result.ads,
49
+ };
50
+ },
51
+ });
52
+ export const TRANSPARENCY_TOOLS = [getEuTransparency];
53
+ //# sourceMappingURL=transparency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transparency.js","sourceRoot":"","sources":["../../src/tools/transparency.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAoB,MAAM,UAAU,CAAC;AAExD,MAAM,iBAAiB,GAAG,UAAU,CAAC;IACnC,IAAI,EAAE,qBAAqB;IAC3B,KAAK,EAAE,oBAAoB;IAC3B,WAAW,EACT,2FAA2F;QAC3F,0FAA0F;QAC1F,wFAAwF;QACxF,2EAA2E;IAC7E,MAAM,EAAE;QACN,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,sCAAsC,CAAC;QAC7E,QAAQ,EAAE,CAAC;aACR,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,mEAAmE,CAAC;QAChF,OAAO,EAAE,CAAC;aACP,MAAM,EAAE;aACR,MAAM,CAAC,CAAC,CAAC;aACT,QAAQ,EAAE;aACV,QAAQ,CAAC,0DAA0D,CAAC;QACvE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC;KACtF;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QAC3B,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;YAC7B,MAAM,IAAI,cAAc,CAAC,6CAA6C,EAAE;gBACtE,IAAI,EACF,kFAAkF;oBAClF,4EAA4E;aAC/E,CAAC,CAAC;QACL,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC;YACjC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;YACzE,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC,CAAC;QACH,OAAO;YACL,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,eAAe,EAAE,MAAM,CAAC,cAAc;YACtC,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,GAAG,EAAE,MAAM,CAAC,GAAG;SAChB,CAAC;IACJ,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAkB,CAAC,iBAAiB,CAAkB,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * HTTP transport, for running the server somewhere always on.
3
+ *
4
+ * Streamable HTTP per the 2025-03-26 spec, stateless: every request builds its
5
+ * own transport and tears it down. No session map means no session leak, which
6
+ * matters far more than the reconnect support a stateful server would buy.
7
+ *
8
+ * Bound to 127.0.0.1 by default. This server reads a public archive, so the risk
9
+ * is not stolen data: it is that binding 0.0.0.0 hands anyone on the network a
10
+ * free browser to drive, or a provider key to spend. FBADS_HTTP_TOKEN adds bearer
11
+ * auth, and FBADS_HTTP_HOST is there for people who mean it.
12
+ */
13
+ import type { BuiltServer } from "../server.js";
14
+ export type HttpOptions = {
15
+ port: number;
16
+ host: string;
17
+ /** When set, every request must send `Authorization: Bearer <token>`. */
18
+ token?: string;
19
+ };
20
+ export declare function httpOptionsFromEnv(argv?: string[]): HttpOptions;
21
+ export declare function startHttpServer(built: BuiltServer, options: HttpOptions): Promise<{
22
+ close: () => Promise<void>;
23
+ }>;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * HTTP transport, for running the server somewhere always on.
3
+ *
4
+ * Streamable HTTP per the 2025-03-26 spec, stateless: every request builds its
5
+ * own transport and tears it down. No session map means no session leak, which
6
+ * matters far more than the reconnect support a stateful server would buy.
7
+ *
8
+ * Bound to 127.0.0.1 by default. This server reads a public archive, so the risk
9
+ * is not stolen data: it is that binding 0.0.0.0 hands anyone on the network a
10
+ * free browser to drive, or a provider key to spend. FBADS_HTTP_TOKEN adds bearer
11
+ * auth, and FBADS_HTTP_HOST is there for people who mean it.
12
+ */
13
+ import { createServer } from "node:http";
14
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
15
+ export function httpOptionsFromEnv(argv = []) {
16
+ const flag = argv.find((a) => a.startsWith("--port="));
17
+ const port = Number(flag?.split("=")[1] ?? process.env.FBADS_HTTP_PORT ?? 8787);
18
+ return {
19
+ port: Number.isFinite(port) && port > 0 ? port : 8787,
20
+ host: process.env.FBADS_HTTP_HOST || "127.0.0.1",
21
+ token: process.env.FBADS_HTTP_TOKEN || undefined,
22
+ };
23
+ }
24
+ async function readBody(req) {
25
+ const chunks = [];
26
+ for await (const chunk of req)
27
+ chunks.push(chunk);
28
+ const raw = Buffer.concat(chunks).toString("utf8");
29
+ if (!raw)
30
+ return undefined;
31
+ try {
32
+ return JSON.parse(raw);
33
+ }
34
+ catch {
35
+ return undefined;
36
+ }
37
+ }
38
+ export async function startHttpServer(built, options) {
39
+ const http = createServer((req, res) => {
40
+ void handle(built, options, req, res).catch((error) => {
41
+ if (!res.headersSent)
42
+ res.writeHead(500, { "content-type": "application/json" });
43
+ res.end(JSON.stringify({
44
+ jsonrpc: "2.0",
45
+ error: { code: -32603, message: error?.message ?? "internal error" },
46
+ id: null,
47
+ }));
48
+ });
49
+ });
50
+ await new Promise((resolve) => http.listen(options.port, options.host, resolve));
51
+ process.stderr.write(`[facebook-ad-library-mcp] listening on http://${options.host}:${options.port}/mcp${options.token ? " (bearer token required)" : ""}\n`);
52
+ return {
53
+ close: () => new Promise((resolve) => {
54
+ http.close(() => resolve());
55
+ }),
56
+ };
57
+ }
58
+ async function handle(built, options, req, res) {
59
+ if (req.url === "/health") {
60
+ res.writeHead(200, { "content-type": "application/json" });
61
+ res.end(JSON.stringify({
62
+ ok: true,
63
+ tools: built.toolCount,
64
+ backend: built.backend.name,
65
+ }));
66
+ return;
67
+ }
68
+ if (options.token) {
69
+ const header = req.headers.authorization ?? "";
70
+ if (header !== `Bearer ${options.token}`) {
71
+ res.writeHead(401, { "content-type": "application/json" });
72
+ res.end(JSON.stringify({ error: "unauthorized" }));
73
+ return;
74
+ }
75
+ }
76
+ if (req.method === "DELETE") {
77
+ // Stateless: there is no session to end, and saying so beats a 404.
78
+ res.writeHead(204).end();
79
+ return;
80
+ }
81
+ // A fresh transport per request. `sessionIdGenerator: undefined` is what puts
82
+ // the SDK in stateless mode.
83
+ const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
84
+ res.on("close", () => void transport.close());
85
+ await built.server.connect(transport);
86
+ await transport.handleRequest(req, res, await readBody(req));
87
+ }
88
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/transport/http.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@thenavidm/facebook-ad-library-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for Meta's public Ad Library. Reads the real ad JSON rather than scraping rendered text, so it returns every creative, the true landing URL and the platform list. Free by default with no API key, with optional provider backends. Works with Claude Code, Claude Desktop, Cursor, Windsurf and any MCP-compatible client.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "bin": {
8
+ "facebook-ad-library-mcp": "./dist/index.js"
9
+ },
10
+ "engines": {
11
+ "node": ">=20"
12
+ },
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "dev": "tsc --watch",
16
+ "start": "node dist/index.js",
17
+ "test": "vitest run",
18
+ "test:watch": "vitest",
19
+ "typecheck": "tsc --noEmit",
20
+ "prepublishOnly": "npm run build"
21
+ },
22
+ "dependencies": {
23
+ "@modelcontextprotocol/sdk": "^1.30.0",
24
+ "zod": "^3.25.76"
25
+ },
26
+ "optionalDependencies": {
27
+ "playwright": "^1.62.1"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^22.10.0",
31
+ "typescript": "^5.7.0",
32
+ "vitest": "^2.1.0"
33
+ },
34
+ "license": "MIT",
35
+ "author": "Navid Moazzez <https://navid.me>",
36
+ "homepage": "https://navid.me",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/thenavidm/facebook-ad-library-mcp.git"
40
+ },
41
+ "bugs": {
42
+ "url": "https://github.com/thenavidm/facebook-ad-library-mcp/issues"
43
+ },
44
+ "keywords": [
45
+ "mcp",
46
+ "model-context-protocol",
47
+ "facebook-ads",
48
+ "meta-ad-library",
49
+ "ad-library",
50
+ "competitive-research",
51
+ "ad-intelligence",
52
+ "mcp-server",
53
+ "claude",
54
+ "claude-code",
55
+ "ai-agents",
56
+ "llm-tools"
57
+ ],
58
+ "files": [
59
+ "dist",
60
+ "README.md",
61
+ "SKILL.md",
62
+ "LICENSE"
63
+ ]
64
+ }