Skip to main content
Glama
ZLeventer

linkedin-campaign-manager-mcp

li_list_campaigns

List LinkedIn ad campaigns with details on status, objective, budget, and targeting. Filter by status or campaign group to streamline campaign management.

Instructions

List campaigns in a LinkedIn ad account. Returns campaign name, status, objectiveType (WEBSITE_VISITS/LEAD_GENERATION/BRAND_AWARENESS/etc.), optimizationTargetType, bid amount, daily/total budget, run schedule, and targeting criteria summary. Filter by status or campaign_group_id. Use li_get_campaign for full targeting detail on a specific campaign.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
ad_account_idNoAd account numeric ID or URN. Defaults to LINKEDIN_DEFAULT_AD_ACCOUNT.
statusNoFilter by campaign status. Omit to return campaigns in all statuses.
campaign_group_idNoFilter to campaigns belonging to a specific campaign group (numeric ID or URN).
page_sizeNo

Implementation Reference

  • src/index.ts:74-79 (registration)
    Registration of the li_list_campaigns MCP tool. Maps the tool name to listCampaignsSchema and the listCampaigns handler function.
    server.tool(
      "li_list_campaigns",
      "List campaigns in a LinkedIn ad account. Returns campaign name, status, objectiveType (WEBSITE_VISITS/LEAD_GENERATION/BRAND_AWARENESS/etc.), optimizationTargetType, bid amount, daily/total budget, run schedule, and targeting criteria summary. Filter by status or campaign_group_id. Use li_get_campaign for full targeting detail on a specific campaign.",
      listCampaignsSchema,
      async (args) => { try { return ok(await listCampaigns(args)); } catch (e) { return err(e); } }
    );
  • The listCampaigns handler function. Resolves the ad account, builds query params with optional status and campaign_group_id filters, and calls the LinkedIn API GET /adAccounts/{accountId}/adCampaigns endpoint.
    export async function listCampaigns(args: {
      ad_account_id?: string;
      status?: string;
      campaign_group_id?: string;
      page_size?: number;
    }) {
      const account = resolveAdAccount(args.ad_account_id);
      const accountId = unwrapURN(account);
      const params: Record<string, string | number> = {
        q: "search",
        pageSize: args.page_size ?? 50,
      };
      const criteria: string[] = [];
      if (args.status) {
        criteria.push(`status:(values:List(${args.status}))`);
      }
      if (args.campaign_group_id) {
        const groupUrn = urn("sponsoredCampaignGroup", args.campaign_group_id);
        criteria.push(`campaignGroup:(values:List(${groupUrn}))`);
      }
      if (criteria.length > 0) {
        params["search"] = `(${criteria.join(",")})`;
      }
      return liGet(`/adAccounts/${accountId}/adCampaigns`, params);
    }
  • Zod schema for li_list_campaigns input validation. Defines optional ad_account_id, status (enum), campaign_group_id, and page_size (default 50) parameters.
    export const listCampaignsSchema = {
      ad_account_id: z
        .string()
        .optional()
        .describe("Ad account numeric ID or URN. Defaults to LINKEDIN_DEFAULT_AD_ACCOUNT."),
      status: z
        .enum(CAMPAIGN_STATUSES)
        .optional()
        .describe("Filter by campaign status. Omit to return campaigns in all statuses."),
      campaign_group_id: z
        .string()
        .optional()
        .describe("Filter to campaigns belonging to a specific campaign group (numeric ID or URN)."),
      page_size: z.number().int().min(1).max(100).default(50),
    };
  • The liGet helper function used by listCampaigns to perform the HTTP GET request to LinkedIn's REST API.
    export async function liGet<T = unknown>(
      path: string,
      query?: Record<string, string | number | boolean | undefined>
    ): Promise<T> {
      const url = new URL(BASE_URL + path);
      if (query) {
        for (const [k, v] of Object.entries(query)) {
          if (v !== undefined && v !== null) url.searchParams.set(k, String(v));
        }
      }
      return liFetch<T>("GET", url.toString());
    }
  • The resolveAdAccount helper used by listCampaigns to resolve the ad account ID from argument or environment variable.
    /** Resolve an ad account from arg override or LINKEDIN_DEFAULT_AD_ACCOUNT env. Returns full URN. */
    export function resolveAdAccount(override?: string): string {
      const v = (override ?? process.env.LINKEDIN_DEFAULT_AD_ACCOUNT ?? "").trim();
      if (!v) {
        throw new LinkedInError(
          "No ad account provided. Pass ad_account_id or set LINKEDIN_DEFAULT_AD_ACCOUNT in .env."
        );
      }
      return urn("sponsoredAccount", v);
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided. The description implies a read operation by listing campaigns, but does not explicitly state read-only behavior, rate limits, or pagination details. It returns specific fields but not the output format (e.g., array).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with the main action, followed by returned data and filtering/alternative. Every sentence adds value without redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers the tool's purpose, inputs, and outputs sufficiently for a listing tool. However, it does not explicitly state the output is an array or mention pagination or sorting behavior. The return fields are listed but not structured.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 75% (3 of 4 parameters described). The description mentions filtering by status and campaign_group_id, adding context to those parameters. However, it does not describe page_size or ad_account_id further, and the schema already covers parameter details.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses the verb 'List' with the resource 'campaigns in a LinkedIn ad account', making the action and scope clear. It also distinguishes itself from sibling li_get_campaign by noting that the latter provides full targeting detail.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description states filtering options (status, campaign_group_id) and explicitly recommends li_get_campaign for full targeting detail. It lacks an explicit 'when not to use' statement, but the alternative is clearly given.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ZLeventer/linkedin-campaign-manager-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server