meta-ads-mcp 0.6.0__tar.gz → 0.7.1__tar.gz

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 (59) hide show
  1. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/.gitignore +2 -1
  2. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/PKG-INFO +49 -1
  3. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/README.md +48 -0
  4. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/__init__.py +15 -3
  5. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/core/__init__.py +7 -0
  6. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/core/authentication.py +77 -32
  7. meta_ads_mcp-0.7.1/meta_ads_mcp/core/targeting.py +185 -0
  8. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/pyproject.toml +1 -1
  9. meta_ads_mcp-0.7.1/tests/test_targeting.py +439 -0
  10. meta_ads_mcp-0.7.1/tests/test_targeting_search_e2e.py +605 -0
  11. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/.github/workflows/publish.yml +0 -0
  12. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/.github/workflows/test.yml +0 -0
  13. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/CUSTOM_META_APP.md +0 -0
  14. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/Dockerfile +0 -0
  15. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/LICENSE +0 -0
  16. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/LOCAL_INSTALLATION.md +0 -0
  17. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/META_API_NOTES.md +0 -0
  18. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/RELEASE.md +0 -0
  19. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/STREAMABLE_HTTP_SETUP.md +0 -0
  20. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/examples/README.md +0 -0
  21. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/examples/example_http_client.py +0 -0
  22. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/future_improvements.md +0 -0
  23. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/images/meta-ads-example.png +0 -0
  24. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_auth.sh +0 -0
  25. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/__main__.py +0 -0
  26. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/core/accounts.py +0 -0
  27. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/core/ads.py +0 -0
  28. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/core/ads_library.py +0 -0
  29. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/core/adsets.py +0 -0
  30. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/core/api.py +0 -0
  31. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/core/auth.py +0 -0
  32. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/core/budget_schedules.py +0 -0
  33. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/core/callback_server.py +0 -0
  34. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/core/campaigns.py +0 -0
  35. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/core/duplication.py +0 -0
  36. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/core/http_auth_integration.py +0 -0
  37. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/core/insights.py +0 -0
  38. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/core/openai_deep_research.py +0 -0
  39. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/core/pipeboard_auth.py +0 -0
  40. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/core/reports.py +0 -0
  41. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/core/resources.py +0 -0
  42. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/core/server.py +0 -0
  43. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/meta_ads_mcp/core/utils.py +0 -0
  44. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/requirements.txt +0 -0
  45. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/setup.py +0 -0
  46. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/smithery.yaml +0 -0
  47. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/tests/README.md +0 -0
  48. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/tests/README_REGRESSION_TESTS.md +0 -0
  49. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/tests/__init__.py +0 -0
  50. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/tests/conftest.py +0 -0
  51. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/tests/test_account_search.py +0 -0
  52. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/tests/test_duplication.py +0 -0
  53. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/tests/test_duplication_regression.py +0 -0
  54. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/tests/test_get_ad_creatives_fix.py +0 -0
  55. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/tests/test_get_ad_image_regression.py +0 -0
  56. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/tests/test_http_transport.py +0 -0
  57. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/tests/test_integration_openai_mcp.py +0 -0
  58. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/tests/test_openai.py +0 -0
  59. {meta_ads_mcp-0.6.0 → meta_ads_mcp-0.7.1}/tests/test_openai_mcp_deep_research.py +0 -0
@@ -29,4 +29,5 @@ debug/
29
29
  # Keep organized directories but exclude some debug content
30
30
  !debug/README.md
31
31
 
32
- internal/
32
+ internal/
33
+ uv.lock
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meta-ads-mcp
3
- Version: 0.6.0
3
+ Version: 0.7.1
4
4
  Summary: Model Context Protocol (MCP) plugin for interacting with Meta Ads API
5
5
  Project-URL: Homepage, https://github.com/pipeboard-co/meta-ads-mcp
6
6
  Project-URL: Bug Tracker, https://github.com/pipeboard-co/meta-ads-mcp/issues
@@ -331,6 +331,54 @@ For local installation configuration, authentication options, and advanced techn
331
331
  - `access_token` (optional): Meta API access token.
332
332
  - Returns: JSON string with the ID of the created budget schedule or an error message.
333
333
 
334
+ 22. `mcp_meta_ads_search_interests`
335
+ - Search for interest targeting options by keyword
336
+ - Inputs:
337
+ - `access_token` (optional): Meta API access token (will use cached token if not provided)
338
+ - `query`: Search term for interests (e.g., "baseball", "cooking", "travel")
339
+ - `limit`: Maximum number of results to return (default: 25)
340
+ - Returns: Interest data with id, name, audience_size, and path fields
341
+
342
+ 23. `mcp_meta_ads_get_interest_suggestions`
343
+ - Get interest suggestions based on existing interests
344
+ - Inputs:
345
+ - `access_token` (optional): Meta API access token (will use cached token if not provided)
346
+ - `interest_list`: List of interest names to get suggestions for (e.g., ["Basketball", "Soccer"])
347
+ - `limit`: Maximum number of suggestions to return (default: 25)
348
+ - Returns: Suggested interests with id, name, audience_size, and description fields
349
+
350
+ 24. `mcp_meta_ads_validate_interests`
351
+ - Validate interest names or IDs for targeting
352
+ - Inputs:
353
+ - `access_token` (optional): Meta API access token (will use cached token if not provided)
354
+ - `interest_list`: List of interest names to validate (e.g., ["Japan", "Basketball"])
355
+ - `interest_fbid_list`: List of interest IDs to validate (e.g., ["6003700426513"])
356
+ - Returns: Validation results showing valid status and audience_size for each interest
357
+
358
+ 25. `mcp_meta_ads_search_behaviors`
359
+ - Get all available behavior targeting options
360
+ - Inputs:
361
+ - `access_token` (optional): Meta API access token (will use cached token if not provided)
362
+ - `limit`: Maximum number of results to return (default: 50)
363
+ - Returns: Behavior targeting options with id, name, audience_size bounds, path, and description
364
+
365
+ 26. `mcp_meta_ads_search_demographics`
366
+ - Get demographic targeting options
367
+ - Inputs:
368
+ - `access_token` (optional): Meta API access token (will use cached token if not provided)
369
+ - `demographic_class`: Type of demographics ('demographics', 'life_events', 'industries', 'income', 'family_statuses', 'user_device', 'user_os')
370
+ - `limit`: Maximum number of results to return (default: 50)
371
+ - Returns: Demographic targeting options with id, name, audience_size bounds, path, and description
372
+
373
+ 27. `mcp_meta_ads_search_geo_locations`
374
+ - Search for geographic targeting locations
375
+ - Inputs:
376
+ - `access_token` (optional): Meta API access token (will use cached token if not provided)
377
+ - `query`: Search term for locations (e.g., "New York", "California", "Japan")
378
+ - `location_types`: Types of locations to search (['country', 'region', 'city', 'zip', 'geo_market', 'electoral_district'])
379
+ - `limit`: Maximum number of results to return (default: 25)
380
+ - Returns: Location data with key, name, type, and geographic hierarchy information
381
+
334
382
  ## Privacy and Security
335
383
 
336
384
  Meta Ads MCP follows security best practices with secure token management and automatic authentication handling.
@@ -306,6 +306,54 @@ For local installation configuration, authentication options, and advanced techn
306
306
  - `access_token` (optional): Meta API access token.
307
307
  - Returns: JSON string with the ID of the created budget schedule or an error message.
308
308
 
309
+ 22. `mcp_meta_ads_search_interests`
310
+ - Search for interest targeting options by keyword
311
+ - Inputs:
312
+ - `access_token` (optional): Meta API access token (will use cached token if not provided)
313
+ - `query`: Search term for interests (e.g., "baseball", "cooking", "travel")
314
+ - `limit`: Maximum number of results to return (default: 25)
315
+ - Returns: Interest data with id, name, audience_size, and path fields
316
+
317
+ 23. `mcp_meta_ads_get_interest_suggestions`
318
+ - Get interest suggestions based on existing interests
319
+ - Inputs:
320
+ - `access_token` (optional): Meta API access token (will use cached token if not provided)
321
+ - `interest_list`: List of interest names to get suggestions for (e.g., ["Basketball", "Soccer"])
322
+ - `limit`: Maximum number of suggestions to return (default: 25)
323
+ - Returns: Suggested interests with id, name, audience_size, and description fields
324
+
325
+ 24. `mcp_meta_ads_validate_interests`
326
+ - Validate interest names or IDs for targeting
327
+ - Inputs:
328
+ - `access_token` (optional): Meta API access token (will use cached token if not provided)
329
+ - `interest_list`: List of interest names to validate (e.g., ["Japan", "Basketball"])
330
+ - `interest_fbid_list`: List of interest IDs to validate (e.g., ["6003700426513"])
331
+ - Returns: Validation results showing valid status and audience_size for each interest
332
+
333
+ 25. `mcp_meta_ads_search_behaviors`
334
+ - Get all available behavior targeting options
335
+ - Inputs:
336
+ - `access_token` (optional): Meta API access token (will use cached token if not provided)
337
+ - `limit`: Maximum number of results to return (default: 50)
338
+ - Returns: Behavior targeting options with id, name, audience_size bounds, path, and description
339
+
340
+ 26. `mcp_meta_ads_search_demographics`
341
+ - Get demographic targeting options
342
+ - Inputs:
343
+ - `access_token` (optional): Meta API access token (will use cached token if not provided)
344
+ - `demographic_class`: Type of demographics ('demographics', 'life_events', 'industries', 'income', 'family_statuses', 'user_device', 'user_os')
345
+ - `limit`: Maximum number of results to return (default: 50)
346
+ - Returns: Demographic targeting options with id, name, audience_size bounds, path, and description
347
+
348
+ 27. `mcp_meta_ads_search_geo_locations`
349
+ - Search for geographic targeting locations
350
+ - Inputs:
351
+ - `access_token` (optional): Meta API access token (will use cached token if not provided)
352
+ - `query`: Search term for locations (e.g., "New York", "California", "Japan")
353
+ - `location_types`: Types of locations to search (['country', 'region', 'city', 'zip', 'geo_market', 'electoral_district'])
354
+ - `limit`: Maximum number of results to return (default: 25)
355
+ - Returns: Location data with key, name, type, and geographic hierarchy information
356
+
309
357
  ## Privacy and Security
310
358
 
311
359
  Meta Ads MCP follows security best practices with secure token management and automatic authentication handling.
@@ -7,7 +7,7 @@ with the Claude LLM.
7
7
 
8
8
  from meta_ads_mcp.core.server import main
9
9
 
10
- __version__ = "0.6.0"
10
+ __version__ = "0.7.1"
11
11
 
12
12
  __all__ = [
13
13
  'get_ad_accounts',
@@ -26,7 +26,13 @@ __all__ = [
26
26
  'get_insights',
27
27
  'get_login_link',
28
28
  'login_cli',
29
- 'main'
29
+ 'main',
30
+ 'search_interests',
31
+ 'get_interest_suggestions',
32
+ 'validate_interests',
33
+ 'search_behaviors',
34
+ 'search_demographics',
35
+ 'search_geo_locations'
30
36
  ]
31
37
 
32
38
  # Import key functions to make them available at package level
@@ -47,7 +53,13 @@ from .core import (
47
53
  get_insights,
48
54
  get_login_link,
49
55
  login_cli,
50
- main
56
+ main,
57
+ search_interests,
58
+ get_interest_suggestions,
59
+ validate_interests,
60
+ search_behaviors,
61
+ search_demographics,
62
+ search_geo_locations
51
63
  )
52
64
 
53
65
  # Define a main function to be used as a package entry point
@@ -11,6 +11,7 @@ from .server import login_cli, main
11
11
  from .auth import login
12
12
  from .ads_library import search_ads_archive
13
13
  from .budget_schedules import create_budget_schedule
14
+ from .targeting import search_interests, get_interest_suggestions, validate_interests, search_behaviors, search_demographics, search_geo_locations
14
15
  from . import reports # Import module to register conditional tools
15
16
  from . import duplication # Import module to register conditional duplication tools
16
17
  from .openai_deep_research import search, fetch # OpenAI MCP Deep Research tools
@@ -37,6 +38,12 @@ __all__ = [
37
38
  'main',
38
39
  'search_ads_archive',
39
40
  'create_budget_schedule',
41
+ 'search_interests',
42
+ 'get_interest_suggestions',
43
+ 'validate_interests',
44
+ 'search_behaviors',
45
+ 'search_demographics',
46
+ 'search_geo_locations',
40
47
  'search', # OpenAI MCP Deep Research search tool
41
48
  'fetch', # OpenAI MCP Deep Research fetch tool
42
49
  ]
@@ -1,4 +1,27 @@
1
- """Authentication-specific functionality for Meta Ads API."""
1
+ """Authentication-specific functionality for Meta Ads API.
2
+
3
+ The Meta Ads MCP server supports three authentication modes:
4
+
5
+ 1. **Development/Local Mode** (default)
6
+ - Uses local callback server on localhost:8080+ for OAuth redirect
7
+ - Requires META_ADS_DISABLE_CALLBACK_SERVER to NOT be set
8
+ - Best for local development and testing
9
+
10
+ 2. **Production with API Token**
11
+ - Uses PIPEBOARD_API_TOKEN for server-to-server authentication
12
+ - Bypasses OAuth flow entirely
13
+ - Best for server deployments with pre-configured tokens
14
+
15
+ 3. **Production OAuth Flow** (NEW)
16
+ - Uses Pipeboard OAuth endpoints for dynamic client registration
17
+ - Triggered when META_ADS_DISABLE_CALLBACK_SERVER is set but no PIPEBOARD_API_TOKEN
18
+ - Supports MCP clients that implement OAuth 2.0 discovery
19
+
20
+ Environment Variables:
21
+ - PIPEBOARD_API_TOKEN: Enables mode 2 (token-based auth)
22
+ - META_ADS_DISABLE_CALLBACK_SERVER: Disables local server, enables mode 3
23
+ - META_ACCESS_TOKEN: Direct Meta token (fallback)
24
+ """
2
25
 
3
26
  import json
4
27
  import asyncio
@@ -27,41 +50,49 @@ async def get_login_link(access_token: str = None) -> str:
27
50
  """
28
51
  # Check if we're using pipeboard authentication
29
52
  using_pipeboard = bool(os.environ.get("PIPEBOARD_API_TOKEN", ""))
53
+ callback_server_disabled = bool(os.environ.get("META_ADS_DISABLE_CALLBACK_SERVER", ""))
30
54
 
31
55
  if using_pipeboard:
32
- # Handle Pipeboard authentication
33
- # Check if we have a cached token
34
- cached_token = pipeboard_auth_manager.get_access_token()
35
- token_status = "No token" if not cached_token else "Valid token"
36
-
37
- # If we already have a valid token and none was provided, just return success
38
- if cached_token and not access_token:
39
- logger.info("get_login_link called with existing valid Pipeboard token")
40
- return json.dumps({
41
- "message": "Already authenticated with Pipeboard",
42
- "token_status": token_status,
43
- "token_preview": cached_token[:10] + "..." if cached_token else None,
44
- "authentication_method": "pipeboard"
45
- }, indent=2)
46
-
47
- # Initiate the auth flow via Pipeboard
56
+ # Pipeboard token-based authentication
48
57
  try:
49
- auth_data = pipeboard_auth_manager.initiate_auth_flow()
50
- login_url = auth_data.get("loginUrl")
58
+ logger.info("Using Pipeboard token-based authentication")
51
59
 
52
- # Return a special format that helps the LLM format the response properly
53
- response = {
54
- "login_url": login_url,
55
- "token_status": token_status,
56
- "markdown_link": f"[Click here to authenticate with Meta Ads via Pipeboard]({login_url})",
57
- "message": "IMPORTANT: Please use the Markdown link format in your response to allow the user to click it.",
58
- "instructions_for_llm": "You must present this link as clickable Markdown to the user using the markdown_link format provided.",
59
- "authentication_method": "pipeboard",
60
- "token_duration": "Approximately 60 days",
61
- "note": "After authenticating, the token will be automatically saved."
62
- }
60
+ # If an access token was provided, this is likely a test - return success
61
+ if access_token:
62
+ return json.dumps({
63
+ "message": "Manual token provided",
64
+ "token_status": "Provided token",
65
+ "authentication_method": "manual_token"
66
+ }, indent=2)
67
+
68
+ # Check if Pipeboard token is working
69
+ token = pipeboard_auth_manager.get_access_token()
70
+ if token:
71
+ return json.dumps({
72
+ "message": "Already authenticated via Pipeboard",
73
+ "token_status": "Valid Pipeboard token",
74
+ "authentication_method": "pipeboard_token"
75
+ }, indent=2)
63
76
 
64
- return json.dumps(response, indent=2)
77
+ # Start Pipeboard auth flow
78
+ auth_data = pipeboard_auth_manager.initiate_auth_flow()
79
+ login_url = auth_data.get('loginUrl')
80
+
81
+ if login_url:
82
+ return json.dumps({
83
+ "login_url": login_url,
84
+ "markdown_link": f"[Click here to authenticate with Meta Ads via Pipeboard]({login_url})",
85
+ "message": "IMPORTANT: Please use the Markdown link format in your response to allow the user to click it.",
86
+ "instructions_for_llm": "You must present this link as clickable Markdown to the user using the markdown_link format provided.",
87
+ "authentication_method": "pipeboard_oauth",
88
+ "note": "After authenticating, the token will be automatically retrieved from Pipeboard."
89
+ }, indent=2)
90
+ else:
91
+ return json.dumps({
92
+ "error": "No login URL received from Pipeboard",
93
+ "authentication_method": "pipeboard_oauth_failed"
94
+ }, indent=2)
95
+
65
96
  except Exception as e:
66
97
  logger.error(f"Error initiating Pipeboard auth flow: {e}")
67
98
  return json.dumps({
@@ -69,8 +100,22 @@ async def get_login_link(access_token: str = None) -> str:
69
100
  "message": "Please check your PIPEBOARD_API_TOKEN environment variable.",
70
101
  "authentication_method": "pipeboard"
71
102
  }, indent=2)
103
+ elif callback_server_disabled:
104
+ # Production OAuth flow - use Pipeboard OAuth endpoints directly
105
+ logger.info("Production OAuth flow - using Pipeboard OAuth endpoints")
106
+
107
+ return json.dumps({
108
+ "authorization_endpoint": "https://pipeboard.co/oauth/authorize",
109
+ "token_endpoint": "https://pipeboard.co/oauth/token",
110
+ "registration_endpoint": "https://pipeboard.co/oauth/register",
111
+ "discovery_endpoint": "/.well-known/oauth-authorization-server",
112
+ "message": "Production OAuth flow - use dynamic client registration",
113
+ "instructions": "MCP clients should use the OAuth discovery endpoint to get authorization URLs",
114
+ "authentication_method": "production_oauth",
115
+ "note": "For manual authentication, clients need to register with Pipeboard OAuth service first"
116
+ }, indent=2)
72
117
  else:
73
- # Original Meta authentication flow
118
+ # Original Meta authentication flow (development/local)
74
119
  # Check if we have a cached token
75
120
  cached_token = auth_manager.get_access_token()
76
121
  token_status = "No token" if not cached_token else "Valid token"
@@ -0,0 +1,185 @@
1
+ """Targeting search functionality for Meta Ads API."""
2
+
3
+ import json
4
+ from typing import Optional, List, Dict, Any
5
+ from .api import meta_api_tool, make_api_request
6
+ from .server import mcp_server
7
+
8
+
9
+ @mcp_server.tool()
10
+ @meta_api_tool
11
+ async def search_interests(access_token: str = None, query: str = None, limit: int = 25) -> str:
12
+ """
13
+ Search for interest targeting options by keyword.
14
+
15
+ Args:
16
+ access_token: Meta API access token (optional - will use cached token if not provided)
17
+ query: Search term for interests (e.g., "baseball", "cooking", "travel")
18
+ limit: Maximum number of results to return (default: 25)
19
+
20
+ Returns:
21
+ JSON string containing interest data with id, name, audience_size, and path fields
22
+ """
23
+ if not query:
24
+ return json.dumps({"error": "No search query provided"}, indent=2)
25
+
26
+ endpoint = "search"
27
+ params = {
28
+ "type": "adinterest",
29
+ "q": query,
30
+ "limit": limit
31
+ }
32
+
33
+ data = await make_api_request(endpoint, access_token, params)
34
+
35
+ return json.dumps(data, indent=2)
36
+
37
+
38
+ @mcp_server.tool()
39
+ @meta_api_tool
40
+ async def get_interest_suggestions(access_token: str = None, interest_list: List[str] = None, limit: int = 25) -> str:
41
+ """
42
+ Get interest suggestions based on existing interests.
43
+
44
+ Args:
45
+ access_token: Meta API access token (optional - will use cached token if not provided)
46
+ interest_list: List of interest names to get suggestions for (e.g., ["Basketball", "Soccer"])
47
+ limit: Maximum number of suggestions to return (default: 25)
48
+
49
+ Returns:
50
+ JSON string containing suggested interests with id, name, audience_size, and description fields
51
+ """
52
+ if not interest_list:
53
+ return json.dumps({"error": "No interest list provided"}, indent=2)
54
+
55
+ endpoint = "search"
56
+ params = {
57
+ "type": "adinterestsuggestion",
58
+ "interest_list": json.dumps(interest_list),
59
+ "limit": limit
60
+ }
61
+
62
+ data = await make_api_request(endpoint, access_token, params)
63
+
64
+ return json.dumps(data, indent=2)
65
+
66
+
67
+ @mcp_server.tool()
68
+ @meta_api_tool
69
+ async def validate_interests(access_token: str = None, interest_list: List[str] = None,
70
+ interest_fbid_list: List[str] = None) -> str:
71
+ """
72
+ Validate interest names or IDs for targeting.
73
+
74
+ Args:
75
+ access_token: Meta API access token (optional - will use cached token if not provided)
76
+ interest_list: List of interest names to validate (e.g., ["Japan", "Basketball"])
77
+ interest_fbid_list: List of interest IDs to validate (e.g., ["6003700426513"])
78
+
79
+ Returns:
80
+ JSON string with validation results showing valid status and audience_size for each interest
81
+ """
82
+ if not interest_list and not interest_fbid_list:
83
+ return json.dumps({"error": "No interest list or FBID list provided"}, indent=2)
84
+
85
+ endpoint = "search"
86
+ params = {
87
+ "type": "adinterestvalid"
88
+ }
89
+
90
+ if interest_list:
91
+ params["interest_list"] = json.dumps(interest_list)
92
+
93
+ if interest_fbid_list:
94
+ params["interest_fbid_list"] = json.dumps(interest_fbid_list)
95
+
96
+ data = await make_api_request(endpoint, access_token, params)
97
+
98
+ return json.dumps(data, indent=2)
99
+
100
+
101
+ @mcp_server.tool()
102
+ @meta_api_tool
103
+ async def search_behaviors(access_token: str = None, limit: int = 50) -> str:
104
+ """
105
+ Get all available behavior targeting options.
106
+
107
+ Args:
108
+ access_token: Meta API access token (optional - will use cached token if not provided)
109
+ limit: Maximum number of results to return (default: 50)
110
+
111
+ Returns:
112
+ JSON string containing behavior targeting options with id, name, audience_size bounds, path, and description
113
+ """
114
+ endpoint = "search"
115
+ params = {
116
+ "type": "adTargetingCategory",
117
+ "class": "behaviors",
118
+ "limit": limit
119
+ }
120
+
121
+ data = await make_api_request(endpoint, access_token, params)
122
+
123
+ return json.dumps(data, indent=2)
124
+
125
+
126
+ @mcp_server.tool()
127
+ @meta_api_tool
128
+ async def search_demographics(access_token: str = None, demographic_class: str = "demographics", limit: int = 50) -> str:
129
+ """
130
+ Get demographic targeting options.
131
+
132
+ Args:
133
+ access_token: Meta API access token (optional - will use cached token if not provided)
134
+ demographic_class: Type of demographics to retrieve. Options: 'demographics', 'life_events',
135
+ 'industries', 'income', 'family_statuses', 'user_device', 'user_os' (default: 'demographics')
136
+ limit: Maximum number of results to return (default: 50)
137
+
138
+ Returns:
139
+ JSON string containing demographic targeting options with id, name, audience_size bounds, path, and description
140
+ """
141
+ endpoint = "search"
142
+ params = {
143
+ "type": "adTargetingCategory",
144
+ "class": demographic_class,
145
+ "limit": limit
146
+ }
147
+
148
+ data = await make_api_request(endpoint, access_token, params)
149
+
150
+ return json.dumps(data, indent=2)
151
+
152
+
153
+ @mcp_server.tool()
154
+ @meta_api_tool
155
+ async def search_geo_locations(access_token: str = None, query: str = None,
156
+ location_types: List[str] = None, limit: int = 25) -> str:
157
+ """
158
+ Search for geographic targeting locations.
159
+
160
+ Args:
161
+ access_token: Meta API access token (optional - will use cached token if not provided)
162
+ query: Search term for locations (e.g., "New York", "California", "Japan")
163
+ location_types: Types of locations to search. Options: ['country', 'region', 'city', 'zip',
164
+ 'geo_market', 'electoral_district']. If not specified, searches all types.
165
+ limit: Maximum number of results to return (default: 25)
166
+
167
+ Returns:
168
+ JSON string containing location data with key, name, type, and geographic hierarchy information
169
+ """
170
+ if not query:
171
+ return json.dumps({"error": "No search query provided"}, indent=2)
172
+
173
+ endpoint = "search"
174
+ params = {
175
+ "type": "adgeolocation",
176
+ "q": query,
177
+ "limit": limit
178
+ }
179
+
180
+ if location_types:
181
+ params["location_types"] = json.dumps(location_types)
182
+
183
+ data = await make_api_request(endpoint, access_token, params)
184
+
185
+ return json.dumps(data, indent=2)
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "meta-ads-mcp"
7
- version = "0.6.0"
7
+ version = "0.7.1"
8
8
  description = "Model Context Protocol (MCP) plugin for interacting with Meta Ads API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"