Skip to content

[Bug]: Tool-router session.search omits Google Ads tools that exist in catalog and are executable #3777

Description

@jaijuneja

SDK Language

Python SDK (composio package)

SDK Version

composio-client==1.41.0, composio==0.17.1 (Also reproduced on composio-client==1.39.0, composio==0.15.0.)

Runtime Environment

Python 3.12.8 on macOS (darwin 24.6.0)

Environment

Local Development

Describe the Bug

Several Google Ads tools exist in the toolkit catalog and are executable via session.execute, but are missing from the tool-router semantic search index (POST /api/v3.1/tool_router/session/{session_id}/search and the COMPOSIO_SEARCH_TOOLS meta tool).

Pattern:

  • tools.list(toolkit_slug="googleads") returns the full toolkit (22 tools in our project).
  • tools.retrieve(slug) / composio.tools.get(user_id, tools=[slug]) succeed for affected slugs.
  • tool_router.session.search never returns them — not in primary_tool_slugs, related_tool_slugs, or tool_schemas.
  • When other Google Ads tools are semantically relevant but outside the session allowlist, search lists them under "Restricted tools (not returned...)". Affected tools are often not even on that restricted list — they appear completely absent from the search corpus.
  • The same behavior occurs via COMPOSIO_SEARCH_TOOLS (session.execute) — not a separate search path.
  • With a wildcard session (toolkits.enable: ["googleads"], no per-tool allowlist), search returns some Google Ads tools but still omits others that are in the catalog.

Example (confirmed): GOOGLEADS_MUTATE_CAMPAIGN_CRITERIA — needed for negative keywords / campaign criteria mutation. Present in catalog, executable when connected, never surfaced by search (even when it is the only tool enabled in the session). We have observed the same class of omission for other Google Ads actions (e.g. ad creation, shared sets, offline conversion upload, keyword idea generation) that appear in catalog/tools.list but not in search results; Pipedream’s Google Ads integration exposes a fuller set for the same workflows.

Expected: Tools in the catalog that match a use case and are allowed by the session should be returned by search, or listed as restricted when semantically relevant but outside the allowlist.

Actual: A subset of Google Ads tools is invisible to search entirely, while sibling tools in the same toolkit are discoverable (or explicitly listed as restricted).

Impact: Agents relying on tool-router discovery cannot find tools users enable in the UI; execution-by-slug still works, so discovery and execution disagree.

Steps to Reproduce

  1. Initialize composio_client.Composio with a valid project API key.
  2. List Google Ads catalog: client.tools.list(toolkit_slug="googleads", limit=500) — note tools present (e.g. GOOGLEADS_MUTATE_CAMPAIGN_CRITERIA).
  3. Create a tool-router session scoped to Google Ads — either:
    • Single-tool: tools={"googleads": {"enable": ["GOOGLEADS_MUTATE_CAMPAIGN_CRITERIA"]}}, or
    • Wildcard: only toolkits={"enable": ["googleads"]}.
  4. Call session.search with a use case that should match (e.g. "add negative keyword to Google Ads campaign via criteria mutate").
  5. Observe empty or partial results: affected slugs missing from primary_tool_slugs / related_tool_slugs / tool_schemas, and often absent from the restricted-tools list too.
  6. (Optional) Repeat via session.execute(..., tool_slug="COMPOSIO_SEARCH_TOOLS", ...) — same outcome.
  7. (Optional) session.execute with the slug directly (with a connected account) — tool runs (we saw live Google API responses, e.g. 429 RESOURCE_EXHAUSTED, not “tool not found”).

Minimal Reproducible Example

import json
from composio_client import Composio

client = Composio(api_key="YOUR_API_KEY")

TOOLKIT = "googleads"
# Example of a catalog tool missing from search; others in the same toolkit show the same pattern.
EXAMPLE_SLUG = "GOOGLEADS_MUTATE_CAMPAIGN_CRITERIA"
QUERY = "add negative keyword to Google Ads campaign via criteria mutate"

# 1) Catalog includes the tool
catalog = client.tools.list(toolkit_slug=TOOLKIT, limit=500)
catalog_slugs = {t.slug for t in catalog.items}
assert EXAMPLE_SLUG in catalog_slugs
print(f"Catalog: {len(catalog_slugs)} tools, includes {EXAMPLE_SLUG}")

# 2) Search index does not — even when it's the only enabled tool
session = client.tool_router.session.create(
    user_id="test_user",
    toolkits={"enable": [TOOLKIT]},
    tools={TOOLKIT: {"enable": [EXAMPLE_SLUG]}},
    manage_connections={"enable": False},
)
resp = client.tool_router.session.search(
    session.session_id,
    queries=[{"use_case": QUERY}],
)
r = resp.results[0]
print("primary:", r.primary_tool_slugs)
print("related:", r.related_tool_slugs)
print("schemas:", list((resp.tool_schemas or {}).keys()) if isinstance(resp.tool_schemas, dict) else [])
print("guidance:", r.execution_guidance)

# 3) Wildcard session: some googleads tools appear, example slug still missing
wildcard = client.tool_router.session.create(
    user_id="test_user",
    toolkits={"enable": [TOOLKIT]},
    manage_connections={"enable": False},
)
wresp = client.tool_router.session.search(
    wildcard.session_id,
    queries=[{"use_case": QUERY}],
)
wr = wresp.results[0]
all_returned = set(wr.primary_tool_slugs) | set(wr.related_tool_slugs)
print("wildcard search returned:", sorted(all_returned))
print(f"{EXAMPLE_SLUG} in wildcard results:", EXAMPLE_SLUG in all_returned)

Error Output / Stack Trace

No HTTP error — search returns 200 with incomplete results. Example (single-tool session, criteria-mutate query):


{
  "results": [{
    "use_case": "add negative keyword to Google Ads campaign via criteria mutate",
    "primary_tool_slugs": [],
    "related_tool_slugs": [],
    "toolkits": [],
    "execution_guidance": "IMPORTANT: Some tools are restricted in this environment and have not been returned.\n\nRestricted tools (not returned; you cannot execute these in this environment): GOOGLEADS_MUTATE_CAMPAIGNS, GOOGLEADS_MUTATE_AD_GROUPS, GOOGLEADS_ADD_OR_REMOVE_TO_CUSTOMER_LIST, GOOGLEADS_LIST_ACCESSIBLE_CUSTOMERS, GOOGLEADS_SEARCH_STREAM_GAQL, GOOGLEADS_GET_CAMPAIGN_BY_ID, GOOGLEADS_CREATE_CUSTOMER_LIST\n\nNo matching tools found for \"add negative keyword to Google Ads campaign via criteria mutate\"."
  }],
  "tool_schemas": {},
  "toolkit_connection_statuses": []
}


`GOOGLEADS_MUTATE_CAMPAIGN_CRITERIA` is neither returned nor listed as restricted. Wildcard search for the same query returns other slugs (e.g. `GOOGLEADS_SEARCH_STREAM_GAQL`, `GOOGLEADS_MUTATE_CAMPAIGNS`) but still not the example slug.

Reproducibility

  • Always reproducible
  • Intermittent / Sometimes
  • Happened once, can’t reproduce

Additional Context or Screenshots

Toolkit

googleads — search index appears to be a strict subset of the catalog.

Tools discoverable via search (examples)

GOOGLEADS_SEARCH_STREAM_GAQL, GOOGLEADS_MUTATE_CAMPAIGNS, GOOGLEADS_MUTATE_AD_GROUPS, GOOGLEADS_GET_CAMPAIGN_BY_ID, GOOGLEADS_LIST_ACCESSIBLE_CUSTOMERS, etc.

Tools in catalog but missing from search (examples)

Includes at least GOOGLEADS_MUTATE_CAMPAIGN_CRITERIA; we’ve seen the same pattern for other Google Ads actions (negative keywords / criteria, ad creation, shared sets, offline conversions, keyword ideas) that show in tools.list but never in session.search.

COMPOSIO_SEARCH_TOOLS

Same empty/partial responses as session.search — not a separate index.

Direct semantic search (tools.get(search=...))

Also weak on natural-language queries for some of these tools; slug-based / tools.list inventory is reliable. Suggests catalog and tool-router search corpora are out of sync, not just ranking.

Related issues

  • #2798 — discovery truncation despite active connections
  • #3290 — list vs execute mismatch (inverse pattern)

Suggested fix direction

Align tool-router search index with the catalog for googleads (and audit other toolkits for the same gap), or document which tools are intentionally excluded from search and why.

Metadata

Metadata

Assignees

No one assigned

    Labels

    supportNeeds support team follow-up

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions