Docs / How the agent works

How the Synter agent actually works

Short version: the agent picks from a fixed catalog of typed, vetted tools. It is not writing a fresh query or shell command on every call. This page explains the runtime: how tools are chosen, how credentials are injected, what stops a write from going live, and where the audit trail lives.

Typed tool catalog (with narrow read-only escape hatches)

When the agent acts on an ad account, it selects a tool from a fixed registry. Each tool has a strict input schema, a named handler, and a single responsibility — pull performance, list campaigns, pause a campaign, generate a creative, etc.

The catalog is backed by ~100+ vetted Python scripts in apps/ppc-backend/client-tools/ plus a smaller set of TypeScript handlers in the web app. For write actions (create, update, pause, budget changes, creative uploads), the agent never writes SQL, GraphQL, or shell — it picks a named handler like pause_campaign or update_campaign_budget and passes structured arguments.

For reads, a few platforms expose constrained, read-only query languages — Google Ads (GAQL), LinkedIn (GraphQL), Meta (Insights API). The agent can pass those queries through tools like run_gaql_query, but the queries are scoped to the caller's connected account and cannot mutate data. See the GAQL escape hatch below.

You can browse the catalog at /docs/mcp/tools and the source ships in the jshorwitz/synter-media repo.

Per-tool credential injection

Tokens are never in the prompt and never in the tool arguments. The MCP runtime resolves the correct OAuth credential for the caller and target platform server-side, immediately before invoking the script:

  1. The agent calls run_ppc_script (or a typed tool like pull_meta_ads_performance) with platform: "GOOGLE" and structured args.
  2. The backend looks up the active platform_connections row for that user / org / platform, refreshes the token via Nango if needed, and injects it into the subprocess environment.
  3. The script reads GOOGLE_ADS_* / META_ACCESS_TOKEN / etc. from the env. The token is scoped to that single call.

If the agent tries to use a tool that maps to a platform the caller hasn't connected, the call fails closed with OAUTH_REAUTH_REQUIRED and the user is prompted to reconnect from the Credentials page.

Safety gates on every write

Reads are free to run; writes are constrained. The same rules apply whether the agent runs in the in-app Campaign IDE or via an external MCP client like Claude Desktop or Cursor:

  • Writes default to paused. New campaigns, ad groups, and creatives are created in a paused / draft state. Enabling spending is a separate, explicit step.
  • Unknown actions fail closed. A tool whose name isn't in the registry returns an error instead of falling through to execute_python or a shell.
  • Autonomy levels. When operating in assist mode, writes that affect budget, status, or creative require explicit user approval in the Pending Actions panel.
  • Managed-account guards. Multi-tenant routes (Synter-managed Google MCC, Meta BM, etc.) enforce per-user account ownership and auto-prefix campaign names with SYN-{user_id}_ on shared accounts so spend can be attributed.
  • Credit guard. Tools that cost credits check the caller's balance before executing and deduct only on a successful run.

Audit trail

Every tool call is persisted. Each row in agent_tool_executions records:

  • The exact tool name and the structured input args.
  • The output (truncated for large blobs, with the full payload available for support).
  • Status, duration, and error message if it failed.
  • The Synter user_id, org_id, and the chat thread the call belongs to.

You see the same information in the chat UI: expand any tool step to see the inputs the agent sent. Sensitive fields matching /token|secret|key|password/i are redacted before render.

The GAQL escape hatch

Google Ads has the richest query surface of any platform. For arbitrary reporting questions that don't fit a typed pull_* tool, the agent can call run_gaql_query with a Google Ads Query Language string.

GAQL is a read-only, typed query language defined by Google—not arbitrary SQL. The query is sent through the official Google Ads API client with the caller's OAuth token; it cannot mutate data and cannot reach any system other than the user's own Google Ads account.

Equivalent escape hatches exist for a few other platforms (e.g. GraphQL on LinkedIn, Insights API on Meta). All of them are read-only and scoped to the caller's connected account.

Browse the tool catalogGet an API key
How the Synter agent works | syntermedia.ai