Skip to main content
Glama
adops-tool

LinkedIn Ads MCP Toolkit

by adops-tool

LinkedIn Ads MCP Toolkit

A configurable Model Context Protocol server for querying LinkedIn advertising data, generating performance reports, and managing campaigns from an MCP-compatible client.

Version CI Node.js TypeScript MCP

Repository · Issue Tracker · CI Runs

Use natural-language requests such as “Compare last month's campaigns” or “Show lead acquisition costs,” while the client translates those requests into typed MCP tool calls. The server also provides standalone HTML dashboard and console report generators.

IMPORTANT

This project requires your own LinkedIn developer application, approved API products, and access to the advertising accounts you query. It cannot grant permissions that your LinkedIn account or developer application does not have.

Table of Contents

  1. Features

  2. Tech Stack & Architecture

  3. Getting Started

  4. Testing

  5. Deployment

  6. Usage

  7. Configuration

  8. License

  9. Contacts & Community Support

Related MCP server: meta-ads-mcp-server

Features

  • 28 MCP tools: account discovery, performance analysis, demographic reporting, conversion tracking, lead generation, ad library searches, campaign administration, image uploads, custom analytics, and local diagnostics.

  • Client-independent integration: standard MCP over stdin/stdout; compatible with clients that support locally launched stdio servers.

  • Account discovery: enumerate accessible accounts, inspect account settings, and optionally include test accounts.

  • Campaign and creative reports: impressions, clicks, landing-page clicks, spend, CTR, CPC, CPM, engagements, conversion metrics, video metrics, reach estimates, and supported dwell-time and penetration metrics.

  • Audience reporting: job function, seniority, industry, company size, job title, company, country, and region pivots, subject to API permissions and reporting availability.

  • Conversion and lead analysis: tracking definitions, attribution breakdowns, form submissions, qualified leads, and acquisition costs.

  • Comparative reporting: time-period, campaign-set, and campaign-group comparisons with absolute and percentage changes.

  • Daily trends: date-level records, aggregate statistics, peak/lowest days, and UTC weekday summaries.

  • Custom analytics: select raw metrics, reporting dimensions, entity filters, and daily/monthly/yearly/all-time granularity.

  • Metric batching: large selections are split into bounded requests and merged by reporting dimensions; mismatched dimensions produce an explicit error.

  • Complete collection traversal: cursor pagination for account-structure searches and offset/link pagination for supported collection endpoints.

  • Pagination safeguards: repeated cursors, untrusted pagination URLs, and configured page limits fail explicitly instead of silently returning incomplete data.

  • Campaign administration: create groups and campaigns, update budgets and targeting, manage creative statuses, create inline ads, and delete supported entities.

  • Account-aware budgets: omitted budget currencies use the advertising account's currency; conflicting currencies are rejected before writes.

  • Optional read-only operation: hide write tools from discovery and reject mutation requests at both MCP and API boundaries.

  • Validated inputs: JSON Schema validation plus calendar-date, identifier, monetary-value, URL, range, and upload-path checks.

  • Resilient reads: per-attempt timeouts, bounded concurrency, selected transient-error retries, exponential backoff, jitter for HTTP retries, and Retry-After support.

  • Safe mutation behavior: write requests are issued once; ambiguous network failures explain that remote state should be checked before another attempt.

  • Concurrent GET deduplication: identical reads in flight share one request without retaining completed account data in a response cache.

  • OAuth lifecycle management: loopback callback server, random state validation, configurable scopes, local token storage, refresh handling, forced login, status, and logout.

  • Reliable token persistence: validated token payloads, atomic file replacement, restricted creation modes where supported, and serialized writes within a process.

  • Standalone reporting: configurable HTML dashboard and a console performance summary.

  • Dashboard content handling: escaped text/attributes, protected embedded JSON, and HTTP(S)-only links and image URLs.

  • Operational tooling: strict TypeScript compilation, automated regression tests, MCP protocol integration tests, cross-platform CI configuration, and a non-root Docker image definition.

NOTE

Metric availability depends on the selected API version, pivot, date range, app products, and account permissions. An empty successful result can mean there was no eligible activity; a failed API request is reported separately.

Tech Stack & Architecture

Component

Technology

Responsibility

Runtime

Node.js 22+

ESM execution, native HTTP/fetch, cancellation, filesystem, test runner

Language

TypeScript with strict checking

Source types and compile-time validation

MCP

@modelcontextprotocol/sdk

Protocol handling and stdio transport

Input validation

Ajv

Compile and apply tool JSON Schemas

Configuration

Environment variables and dotenv

CLI configuration and .env loading

OAuth callback

Native node:http

Temporary loopback authorization listener

Browser launch

open

Optional platform-independent authorization/dashboard opening

Dashboard charts

Chart.js

Browser-side charts loaded from a versioned CDN URL

Tests

node:test, node:assert

Regression, HTTP, OAuth, and MCP integration coverage

Project Structure

.
├── .github/workflows/ci.yml       # Linux/Windows, Node.js 22/24 checks
├── .dockerignore
├── .env.example                  # Documented configuration template
├── .gitignore
├── CHANGELOG.md
├── Dockerfile                    # Multi-stage build; non-root runtime
├── LICENSE
├── README.md
├── package.json
├── package-lock.json
├── tsconfig.json
├── src/
│   ├── index.ts                  # Process entry point and stdio lifecycle
│   ├── server.ts                 # Tool registry, validation, dispatch
│   ├── auth-cli.ts               # Login/status/logout CLI
│   ├── auth/
│   │   ├── oauth.ts              # Authorization and callback exchange
│   │   └── token-store.ts        # Atomic persistence and token refresh
│   ├── lib/
│   │   ├── config.ts             # Validated transport configuration
│   │   ├── dates.ts              # Calendar and range validation
│   │   ├── html.ts               # HTML, script JSON, and URL helpers
│   │   ├── http.ts               # Timeouts, retries, concurrency, errors
│   │   ├── linkedin-api.ts       # LinkedIn endpoints and pagination
│   │   ├── types.ts              # API and tool interfaces
│   │   └── validation.ts         # Schema and semantic input checks
│   ├── tools/
│   │   ├── accounts.ts
│   │   ├── ad-library.ts
│   │   ├── analytics.ts
│   │   ├── campaign-management.ts
│   │   ├── conversions.ts
│   │   ├── demographics.ts
│   │   └── performance.ts
│   └── scripts/
│       ├── generate-dashboard.ts
│       └── list_performance.ts
├── test/
│   ├── auth.test.mjs
│   ├── core.test.mjs
│   └── mcp.test.mjs
└── linkedin-ads-mcp-main/         # Compatibility launcher for the former nested copy

dist/ is generated by the build and is not source-controlled. The root project is the canonical source tree; the legacy nested launcher forwards commands to it.

Key Design Decisions

  • Stdio as the transport: the MCP client owns the server process. Protocol messages use stdout; operational server messages use stderr.

  • Separate startup and server construction: createMcpServer() can be exercised using in-memory transports without starting a subprocess or touching LinkedIn.

  • Validation before dispatch: malformed tool arguments fail before API handlers execute. Unknown top-level arguments are rejected to catch misspelled configuration.

  • One shared API client per server: tool calls use the same concurrency limiter and in-flight read map.

  • No automatic write retries: an upstream operation may have committed even if its response was lost. The caller must inspect the account before repeating that action.

  • No completed-response cache: subsequent reads obtain fresh data; only overlapping identical reads are coalesced.

  • Bounded collection traversal: retrieving a large account cannot loop indefinitely on a repeated cursor.

  • Local OAuth persistence: tokens stay on the machine running the server. The application does not provide a hosted token service.

flowchart TD
    A[MCP client] -->|JSON-RPC over stdio| B[Server registry]
    B --> C[JSON Schema and semantic validation]
    C --> D[Read-only policy]
    D --> E[Tool handler]
    E --> F[LinkedIn API client]
    F --> G[Token store and refresh coordination]
    F --> H[Pagination and metric batching]
    H --> I[HTTP concurrency limiter]
    I --> J[Timeout and GET retry policy]
    J --> K[LinkedIn Marketing API]
    K --> L[Result or structured error]
    L --> A

Collection and entity authorization failures propagate to the caller. A missing entity is represented as absent only where the relevant lookup receives HTTP 404. Tool errors use an MCP result with isError: true; API errors may include HTTP status and a LinkedIn requestId when available.

get_server_status reads local metadata only. It does not refresh tokens or verify network access, granted scopes, app approval, or advertising-account membership.

Getting Started

Prerequisites

  1. Node.js 22 or newer and npm. Verify with node --version and npm --version.

  2. A LinkedIn account that has access to the advertising account you want to query.

  3. A LinkedIn developer application associated with the appropriate organization/company page.

  4. Approved API products and OAuth scopes for the operations you intend to use.

  5. An MCP client with local stdio support to invoke the tools interactively.

  6. Git, if cloning rather than using an extracted source archive.

Docker is optional. Python, a database, and a separate web server are not required.

1. Download and Build

Clone the repository and install the locked dependencies:

git clone https://github.com/adops-tool/linkedin-ads-mcp-toolkit.git
cd linkedin-ads-mcp-toolkit
npm ci --ignore-scripts
npm run build

If you already extracted the repository, open a terminal in the folder containing package.json and run the final two commands there.

2. Create Your Environment File

macOS / Linux:

cp .env.example .env

Windows PowerShell:

Copy-Item .env.example .env

Edit .env locally:

LINKEDIN_CLIENT_ID=replace_with_your_client_id
LINKEDIN_CLIENT_SECRET=replace_with_your_client_secret
LINKEDIN_REDIRECT_URI=http://localhost:3000/callback

# Optional: expose reporting tools only.
LINKEDIN_READ_ONLY=true
CAUTION

.env, token files, generated dashboards, and exported reports may contain credentials or confidential advertising data. Keep them out of public repositories and shared attachments. The supplied ignore rules cover the default paths; custom output paths require your own ignore rules.

3. Configure the LinkedIn Developer Application

  1. Open the LinkedIn Developer Portal.

  2. Create or select your application and associate the required company page.

  3. In Products, request the advertising/reporting API access required by your use case. Approval and available products depend on LinkedIn's review process.

  4. In Auth, add the exact redirect URI: http://localhost:3000/callback.

  5. Copy the application's Client ID and Client Secret into .env.

  6. Check the scopes granted to the application. The default requested scopes are:

Scope

Used for

rw_ads

Advertising-account and campaign management

r_ads_reporting

Advertising analytics and reporting

r_organization_social

Reading organization post content for creative enrichment

w_organization_social

Organization-owned content/image workflows where required

If your application has a different approved scope set, configure LINKEDIN_SCOPES as a space-separated list. Reporting-only installations may request approved read scopes such as r_ads r_ads_reporting; content enrichment may require additional permissions.

IMPORTANT

LINKEDIN_READ_ONLY=true restricts this server's operations. It does not change OAuth grants. Set LINKEDIN_SCOPES separately when you want a different authorization request.

4. Authenticate

npm run auth

The CLI starts a temporary loopback listener, prints the LinkedIn authorization URL, and attempts to open your browser. Sign in and authorize the application. A successful callback stores tokens in:

~/.linkedin-ads-mcp/tokens.json

On Windows, ~ denotes your user profile directory. Set TOKEN_STORAGE_PATH to choose a different absolute path.

Verify the saved state:

npm run auth -- --status

Access-token validity comes from LinkedIn's token response. Automatic refresh is available only when a usable refresh token was issued and client credentials are available to the server process. When no refresh token is available, authenticate again after expiration.

5. Connect an MCP Client

Add a server entry using your client's local MCP configuration. Merge it with existing servers:

{
  "mcpServers": {
    "linkedin-ads": {
      "command": "node",
      "args": ["/absolute/path/to/linkedin-ads-mcp-toolkit/dist/index.js"],
      "env": {
        "LINKEDIN_CLIENT_ID": "your_client_id",
        "LINKEDIN_CLIENT_SECRET": "your_client_secret",
        "TOKEN_STORAGE_PATH": "/absolute/path/to/tokens.json",
        "LINKEDIN_READ_ONLY": "true"
      }
    }
  }
}

Use an actual absolute path. On Windows, JSON paths can use forward slashes, for example C:/Projects/linkedin-ads-mcp-toolkit/dist/index.js, or escaped backslashes. If the desktop client cannot resolve node, provide the full path to node.exe.

Restart or reconnect the client, then request:

Show the LinkedIn Ads server status.
List my LinkedIn advertising accounts.
NOTE

.env is loaded relative to the process working directory. Desktop MCP clients may launch the process from another directory. Supplying environment variables directly in the client configuration and an absolute TOKEN_STORAGE_PATH makes the runtime configuration explicit.

For clients using a mcpServers JSON configuration, the preceding example supplies the process definition. Some clients use a different wrapper or configuration format; retain the command, absolute argument path, and environment values.

Common desktop configuration locations include:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Windows: %APPDATA%/Claude/claude_desktop_config.json

The project does not edit these files automatically.

For manual browser opening, set LINKEDIN_AUTH_NO_BROWSER=true before npm run auth, then open the printed URL yourself. Authorization must return to the machine running the callback listener; a remote host's localhost is a different machine.

Symptom

Resolution

npm or node is not found

Install Node.js, reopen the terminal, and verify PATH. Desktop applications may need a restart or an absolute executable path.

dist/auth-cli.js or dist/index.js is missing

Run npm run build in the project root.

Missing OAuth credentials

Replace placeholders in .env, or pass the environment variables through the MCP client configuration.

Callback EADDRINUSE

Choose a free loopback port in LINKEDIN_REDIRECT_URI, and register that exact URI in the developer portal.

Redirect URI mismatch

Match scheme, hostname, port, and path exactly between the application and .env.

Invalid OAuth scope

Request only scopes approved for your developer application.

Authorization times out

Run the command again, open the new URL, and complete consent before the configured timeout.

No advertising accounts

Check account membership, selected LinkedIn identity, app access, and whether the account is a test account. Try includeTest: true where appropriate.

HTTP 401

Inspect local expiration and reauthenticate with npm run auth -- --force.

HTTP 403

Check API product approval, granted scopes, and advertising-account roles. Repeating the same request does not add permissions.

HTTP 429

Reduce concurrent requests or report scope. Reads honor a valid bounded Retry-After; long server delays return an error for the caller to retry later.

JSON-RPC parsing error

Launch dist/index.js directly. Avoid shell wrappers that print banners to stdout.

Token save fails

Check directory permissions and available disk space; use a writable absolute token directory.

API version is rejected

Choose a supported LINKEDIN_API_VERSION, validate endpoint compatibility, and rebuild/restart as needed.

Dashboard charts do not render

Check browser developer tools and network access to the versioned Chart.js CDN.

Testing

Run the complete static-check and test gate:

npm run check

Individual commands:

Command

Purpose

npm run typecheck

Strict TypeScript validation without emitting files

npm test

Build and execute all test files

npm run test:unit

Build and run core/API/OAuth regression tests

npm run test:integration

Build and test MCP discovery, dispatch, read-only behavior, and a real stdio subprocess

npm audit

Query the npm advisory database for the installed dependency tree

npm run build

Compile runtime JavaScript, declarations, and source maps

The test suite uses mocked LinkedIn responses and temporary local HTTP servers. It does not require LinkedIn credentials, spend advertising budget, or call the real LinkedIn API. OAuth tests exercise state validation, callback paths, port conflicts, token exchange, and timeout cleanup locally.

The repository uses TypeScript checking as its static-analysis gate; no separate ESLint configuration is included.

Report reproducible problems through the GitHub issue tracker. Include your Node.js version, operating system, failing command or tool name, and a sanitized error message.

To request native Node.js coverage output after building:

npm run build
node --test --experimental-test-coverage test/*.test.mjs

Covered scenarios include permanent/transient HTTP errors, failed writes, timeouts, concurrency limits, request coalescing, pagination, metric merging, token corruption and refresh races, HTML escaping, input validation, account currencies, and reporting edge cases.

Local and mocked tests verify application behavior. They do not establish that your developer application has the necessary LinkedIn access or that every endpoint is available in your account. After deployment, first use get_server_status, list_ad_accounts, and a small reporting request against an account you control. Test write workflows against a suitable test account before enabling them for production accounts.

Deployment

Local Production Process

Build and verify before removing development dependencies:

npm ci --ignore-scripts
npm run check
npm prune --omit=dev --ignore-scripts
node dist/index.js

Normally your MCP client launches the final command. An idle terminal process is expected: the stdio server waits for MCP messages and does not expose a browser interface or network health endpoint.

Use a dedicated writable token directory, explicit environment variables, and the operating-system identity intended to own the credentials. Start with LINKEDIN_READ_ONLY=true when deploying reporting workflows. The process handles SIGINT and SIGTERM by closing its MCP transport.

Continuous Integration

The included CI workflow installs the committed lockfile, runs type checking and tests, and checks high-or-critical npm advisories on Linux and Windows with Node.js 22 and 24. Configure it in your repository's Actions environment; adding the workflow does not mean a hosted run has already passed.

Container Deployment

docker build -t linkedin-ads-mcp-toolkit:local .

Authenticate on the host first, then mount the token directory into the non-root container:

docker run --rm -i \
  --env-file .env \
  -e LINKEDIN_READ_ONLY=true \
  -e TOKEN_STORAGE_PATH=/home/node/.linkedin-ads-mcp/tokens.json \
  --mount type=bind,source=/absolute/path/to/.linkedin-ads-mcp,target=/home/node/.linkedin-ads-mcp \
  linkedin-ads-mcp-toolkit:local

The directory must be writable by the container's node user when token refresh is required. The runtime image defaults to read-only tool operation; explicitly set LINKEDIN_READ_ONLY=false only when writes are part of your intended workflow.

  • Keep stdin attached with -i; allocating a terminal with -t is unnecessary for MCP stdio.

  • No port mapping is required for the MCP server. Authenticate on the host; the temporary OAuth callback is a separate local CLI workflow.

  • Mounting a single tokens.json file can interfere with atomic rename. Mount the containing directory instead.

  • Do not bake .env or token files into an image. The supplied .dockerignore excludes the default sensitive paths.

  • Relative file uploads resolve inside the container; MCP uploads require absolute paths that exist in that runtime.

  • After pruning development dependencies, run the compiled server directly. Development/build scripts require the TypeScript compiler.

  • Prefer one active process per token file, or use separate TOKEN_STORAGE_PATH values. Refresh coordination and write serialization are process-local.

  • The Dockerfile is an optional deployment definition. Verify the build and mount permissions in your Docker environment before use.

Usage

Basic Workflow

  1. Ask the client to run get_server_status to inspect local settings and token expiration.

  2. Run list_ad_accounts and select an account ID.

  3. Start with a short reporting range.

  4. Request comparisons or more detailed pivots as needed.

Example MCP tool-call parameters:

{
  "name": "get_campaign_performance",
  "arguments": {
    "accountId": "123456789",
    "startDate": "2026-09-01",
    "endDate": "2026-09-30",
    "timeGranularity": "ALL"
  }
}

These are the params supplied to an MCP tools/call request; your client provides the JSON-RPC envelope and request identifier.

Natural-language examples:

Show campaign performance for the last 30 days.
Which job functions have the highest click-through rate?
Compare September with August for account 123456789.
Show daily conversion trends for the previous two weeks.
Which creatives have the best video completion rate?
List draft and paused campaigns, including those without impressions.
Search the public ad library for a competitor's advertisements.

Available Tools

Tool

Operation

Purpose

get_server_status

Local

Configuration, local token expiration, tool count; no network request

list_ad_accounts

Read

Accessible accounts; status/type/test filtering

get_account_details

Read

Account currency, settings, status, and notifications

get_campaign_performance

Read

Campaign KPIs and optional time buckets

get_creative_performance

Read

Creative KPIs, engagement breakdown, and optional video metrics

get_campaign_groups

Read

Campaign groups, campaign counts, optional performance when a start date is supplied

get_audience_demographics

Read

Performance by supported demographic pivot

get_audience_reach

Read

Member reach, frequency, and supported penetration metrics

list_saved_audiences

Read

Saved/matched audience configuration and status

get_conversion_performance

Read

Conversion-action metrics and attribution breakdown

list_conversions

Read

Conversion tracking definitions

get_lead_gen_performance

Read

Leads, form opens, qualified leads, and acquisition costs

list_lead_forms

Read

Lead form configuration

compare_performance

Read

Period, campaign-set, or campaign-group-set comparisons

get_daily_trends

Read

Daily records, totals, weekday summaries, peak and lowest days

get_analytics

Read

Raw custom metrics, pivots, time granularity, and entity filters

list_campaigns

Read

All matching campaigns, including drafts and zero-impression entities

search_ad_library

Read

Keyword, advertiser, country, dates, targeting facets, and impression filters

create_campaign_group

Write

Create a campaign group; defaults to draft

update_campaign_group

Write

Change group name, status, budget, or end date

delete_campaign_group

Write

Delete a resolved draft or request deletion for a resolved non-draft group

create_campaign

Write

Create a campaign with budget, objective, bidding, and targeting

update_campaign

Write

Update campaign settings and supported targeting/budget fields

delete_campaign

Write

Delete a resolved draft or request deletion for a resolved non-draft campaign

create_creative

Write

Sponsor an existing content reference

create_inline_ad

Write

Create an ad with inline post content

update_creative_status

Write

Change intended creative status

upload_image

Write

Validate and upload a local PNG, JPEG, or GIF

Read-only mode exposes 18 tools, including the local status tool. All tools provide operation hints in MCP discovery. Discover schemas through tools/list for exact fields, required arguments, and enums.

Generate an HTML Dashboard

Set the account ID and optional presentation settings in .env:

LINKEDIN_ADS_ACCOUNT_ID=123456789
LINKEDIN_ADS_ACCOUNT_NAME=My advertising account
DASHBOARD_START_DATE=2026-09-01
DASHBOARD_END_DATE=2026-09-30
DASHBOARD_OUTPUT_PATH=dashboard.html
DASHBOARD_OPEN=false

Then run:

npm run dashboard

The dashboard includes campaign and creative tables, trends, demographic breakdowns, conversions, and interactive filters. Open the generated HTML file locally. Chart rendering requires network access to the pinned Chart.js CDN asset. The default reporting range is the preceding 90 days through today; the explicit dashboard range must not exceed 92 inclusive calendar days because reach metrics are included.

If an optional demographic or conversion-breakdown request fails, the generated dashboard displays a partial-report notice with the affected sections. Required campaign/report requests still fail the command.

For a console summary across accessible accounts:

npm run performance

Custom analytics preserves the raw API record structure, with metric values directly on each record:

{
  "name": "get_analytics",
  "arguments": {
    "accountId": "123456789",
    "pivot": "CAMPAIGN",
    "startDate": "2026-09-01",
    "endDate": "2026-09-30",
    "timeGranularity": "DAILY",
    "campaigns": ["987654321"],
    "metrics": ["impressions", "clicks", "costInUsd", "videoViews"]
  }
}

The raw analytics tool accepts up to 100 distinct metric names. LinkedIn must support each requested metric for the chosen pivot/version. Selections exceeding the per-request field budget are split, then merged by pivotValues and dateRange. Upstream dimension changes between batches result in an error to avoid joining unrelated rows.

Time-period comparison:

{
  "name": "compare_performance",
  "arguments": {
    "accountId": "123456789",
    "comparisonType": "TIME_PERIOD",
    "periodA": { "startDate": "2026-08-01", "endDate": "2026-08-31" },
    "periodB": { "startDate": "2026-09-01", "endDate": "2026-09-30" }
  }
}

For CAMPAIGNS or CAMPAIGN_GROUPS, supply nonempty numeric entityIds for both sets. The comparison uses the common date range defined by periodA (or the handler's default start range).

Direct API use after compilation:

import 'dotenv/config';
import { TokenStore } from './dist/auth/token-store.js';
import { LinkedInApiClient } from './dist/lib/linkedin-api.js';

const store = new TokenStore();
const api = new LinkedInApiClient(store);

// Retrieve all pages before selecting the account.
const accounts = await api.listAdAccounts();
if (!accounts.length) throw new Error('No accessible advertising accounts');

const rows = await api.getAnalytics({
  accountId: String(accounts[0].id),
  pivot: 'ACCOUNT',
  startDate: '2026-09-01',
  endDate: '2026-09-30',
  metrics: ['impressions', 'clicks', 'costInUsd'],
});
console.log(rows);

MCP validation normalizes matching account/campaign/group/creative URNs for the corresponding ...Id and ...Ids arguments. Direct low-level API consumers should pass the numeric IDs expected by each method. The raw get_analytics filter arrays use numeric IDs.

Write tools require LINKEDIN_READ_ONLY=false, the correct LinkedIn scopes, and sufficient account roles. New campaign groups, campaigns, and creatives default to DRAFT where the corresponding status is omitted.

Example campaign group:

{
  "name": "create_campaign_group",
  "arguments": {
    "accountId": "123456789",
    "name": "Product launch",
    "startDate": "2026-11-01",
    "endDate": "2026-11-30",
    "totalBudgetAmount": "5000.00",
    "status": "DRAFT"
  }
}

Budget amounts are positive decimal strings. The account currency is used when a budget/bid currency is omitted; explicit currencies must match the account. Group creation with a total budget requires an end date. Targeting criteria are LinkedIn API objects whose business rules are validated by LinkedIn.

For existing content, use create_creative with a contentReference such as urn:li:share:123456789. For inline content, use create_inline_ad with an organization, commentary, and optional media, destination, and call-to-action fields.

Image upload requires an absolute local path:

{
  "name": "upload_image",
  "arguments": {
    "organizationId": "urn:li:organization:123456",
    "filePath": "/absolute/path/to/creative.png"
  }
}

organizationId can also be an explicit urn:li:sponsoredAccount:... owner when supported by your permissions. Provide both accountId and assetName for media-library registration. Use the returned imageUrn as the media identifier in a subsequent ad request.

The server checks the extension, file signature, regular-file status, nonzero size, and configured byte limit before initializing an upload. LinkedIn may apply stricter dimensions, format, and size restrictions.

WARNING

Activating campaigns or creatives can affect advertising delivery and spend. Write requests are not automatically retried. If a write times out, inspect the account's current state before submitting it again. LinkedIn's create/delete workflows can have partial or asynchronous effects.

Metric

Interpretation

Spend / costInUsd

Cost denominated in USD

costInLocalCurrency

Cost denominated in the advertising account's currency

Impressions / clicks

API-reported delivery and clicks

CTR

Clicks divided by impressions, multiplied by 100

CPC / CPM

USD spend per click / per 1,000 impressions in standard USD reports

Engagement rate

Total engagements divided by impressions, multiplied by 100

Conversion rate

Conversions divided by clicks, multiplied by 100

Cost per conversion / lead

Spend divided by the corresponding eligible result count

Reach

Approximate member reach; availability and privacy limits apply

Frequency

Impressions divided by the relevant reach estimate

Audience penetration

API-supported reached share of target audience

Average dwell time

API-reported dwell-time metric; availability depends on reporting context

Conversion value

Local-currency conversion value, separate from USD cost

Important interpretation details:

  • Percentage change from a zero baseline is null, because the relative growth rate is undefined.

  • Undefined cost-per-result metrics are returned as null where their handlers support nullable values; other standard zero-denominator rates may be reported as zero.

  • Campaign/creative reports preserve API time dimensions when a time-based granularity is requested.

  • Missing reach in MCP performance/demographic reports is null; it is never fabricated from impression counts. Dashboard numeric aggregates include reported reach only, and mark unavailable campaign/creative reach.

  • Summed reach across campaigns or time buckets is not deduplicated account reach. Use an account-level reach query for that purpose.

  • Demographic metrics may be delayed, suppressed, or incomplete due to LinkedIn privacy thresholds. Static demographic-name mappings may fall back to IDs.

  • Reach reports enforce a maximum of 92 inclusive calendar days. Other metrics can have their own LinkedIn restrictions.

  • Analytics responses at the 15,000-row safety threshold fail explicitly; narrow the date range or filter scope. Analytics traversal does not assume ordinary collection pagination.

  • Custom campaign-performance selections can omit fields used by standard derived metrics. Use get_analytics for raw custom results, or request all required base metrics.

  • get_daily_trends returns the raw records when custom metrics are supplied, alongside its standard daily summary.

  • Account/group/creative names may be unavailable when the linked content was deleted or inaccessible.

  • Local status can show an unexpired token even if LinkedIn has revoked it. A real reporting request is required to verify authorization.

Reference the official reporting documentation, cursor pagination documentation, and API versioning documentation when selecting metrics or updating API versions.

Configuration

CLI entry points load .env from the working directory. Existing process environment variables take precedence. loadConfig() validates API transport settings when the API client is constructed; invalid values fail startup instead of being silently coerced.

The compatibility default API version is 202601; explicitly select another supported version with LINKEDIN_API_VERSION after validating your endpoints and app access. A correctly formatted version value does not establish that LinkedIn still supports it.

Variable

Default

Accepted values / purpose

LINKEDIN_CLIENT_ID

Unset

Developer application client ID; required for login and refresh

LINKEDIN_CLIENT_SECRET

Unset

Developer application secret; required for login and refresh

LINKEDIN_REDIRECT_URI

http://localhost:3000/callback

HTTP loopback URL with a registered host, port, and path; no credentials, query, or fragment

LINKEDIN_SCOPES

rw_ads r_ads_reporting r_organization_social w_organization_social

Space-separated approved OAuth scope names

LINKEDIN_OAUTH_TIMEOUT_MS

300000

Authorization flow deadline; integer 1–1,800,000 ms

LINKEDIN_AUTH_NO_BROWSER

Unset

Exact value true disables automatic browser opening

TOKEN_STORAGE_PATH

~/.linkedin-ads-mcp/tokens.json

Token-file path; an absolute path is recommended

LINKEDIN_API_VERSION

202601

Marketing API version in YYYYMM format

LINKEDIN_READ_ONLY

false

Exact true or false; hides and blocks mutation tools

LINKEDIN_REQUEST_TIMEOUT_MS

30000

Per-attempt request/body deadline; integer 1–300,000 ms

LINKEDIN_MAX_RETRIES

3

Extra GET attempts; integer 0–10. 0 means one attempt

LINKEDIN_RETRY_BASE_DELAY_MS

1000

Initial retry delay; integer 1–60,000 ms

LINKEDIN_RETRY_MAX_DELAY_MS

30000

Maximum retry delay; integer from the base delay to 60,000 ms

LINKEDIN_MAX_CONCURRENCY

5

Simultaneous API attempts per client; integer 1–50

LINKEDIN_PAGE_SIZE

100

Collection request page size; integer 1–100

LINKEDIN_MAX_PAGES

1000

Maximum pages per collection traversal; integer 1–10,000

LINKEDIN_MAX_UPLOAD_BYTES

20971520

Local upload byte limit; integer 1–104,857,600. LinkedIn restrictions also apply

LINKEDIN_ADS_ACCOUNT_ID

Unset

Positive numeric account ID; required for dashboard generation

LINKEDIN_ADS_ACCOUNT_NAME

LinkedIn Ads

Dashboard display name

DASHBOARD_START_DATE

90 days before today

Dashboard start date in YYYY-MM-DD format

DASHBOARD_END_DATE

Today

Dashboard end date in YYYY-MM-DD format

DASHBOARD_OUTPUT_PATH

dashboard.html in working directory

Generated HTML output path

DASHBOARD_OPEN

Unset

Exact value true opens the generated dashboard in the default browser

The retry policy covers GET network/timeout failures and HTTP 408, 429, 500, 502, 503, and 504. Other HTTP failures are returned immediately. A Retry-After beyond the configured wait budget produces an error instead of retrying early.

Concurrency queue time and separate pagination/batching requests are additional to the per-attempt timeout; the timeout is not a deadline for an entire multi-request report.

Command

Behavior

npm run auth

Reuse usable local authentication or start OAuth when necessary

npm run auth -- --force

Start a new authorization flow even when a token is present

npm run auth -- --status

Show local authentication/expiration metadata without network access

npm run auth -- --logout

Remove local tokens

npm run auth -- --help

Display available flags

Choose one authentication flag at a time. Logout removes this machine's saved credentials; it does not revoke grants at LinkedIn.

The token store checks access-token expiration with a five-minute refresh window. If refresh is unavailable or temporarily fails, an otherwise unexpired access token remains usable until its actual expiry. Failed refresh attempts are throttled for 30 seconds within a process.

When refresh succeeds, an omitted replacement refresh token is preserved. A supplied refresh-token lifetime is tracked separately. Existing token files without a valid absolute access-token expiration are not treated as authenticated.

Token writes use a temporary file, flush, and atomic replacement. Creation modes are 0700 for directories and 0600 for token files where the operating system supports those modes. Windows access control follows the user's filesystem ACLs. The files are local plaintext credentials and should be stored in a protected user directory.

License

Distributed under the Apache-2.0 license. See LICENSE for the complete terms.

Redistribution must retain the existing copyright and permission notices required by that license.

Support the Project

DevsInExile-Studio Patreon Ko-fi Boosty YouTube Telegram

If you find this tool useful, consider leaving a star on GitHub or supporting the author directly.

Available Tools

28 tools
compare_performanceB
Read-onlyIdempotent

Compares performance between two time periods, campaigns, or campaign groups. Calculates percentage changes and highlights significant differences. Essential for reporting on performance trends.

ParametersJSON Schema
NameRequiredDescriptionDefault
periodAYesFirst period or entity set for comparison
periodBYesSecond period or entity set for comparison
accountIdYesThe LinkedIn Ad Account ID
comparisonTypeYesType of comparison to make

TDQS

B3.4/5.0
Behavior3/5

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

Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the safety profile is covered. The description adds useful behavioral context ('Calculates percentage changes and highlights significant differences'), but says nothing about return format, significance thresholds, or how the two periods map to output.

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

Conciseness4/5

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

Three short sentences, front-loaded with the core action and scope. No filler, though the third sentence is somewhat promotional rather than informational.

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?

There is no output schema, and the description only vaguely gestures at results ('percentage changes', 'significant differences') without explaining the structure of the comparison output. For a nested-object, 4-required-param comparison tool, the safety annotations carry the rest, making this adequate but not complete.

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 description coverage is 100%, so accountId, comparisonType, and both period objects are already fully documented in the schema. The description adds no syntax or format detail beyond what the schema provides, so the baseline of 3 applies.

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

Purpose4/5

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

The description gives a specific verb ('Compares') and concrete resources ('two time periods, campaigns, or campaign groups'), which distinguishes it from the single-scope siblings like get_campaign_performance. It stops short of explicitly naming a sibling to route against, so it is clear but not maximally differentiating.

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

Usage Guidelines3/5

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

'Essential for reporting on performance trends' implies when the tool is useful, but gives no explicit when-to-use/when-not guidance and names no alternative (e.g., get_campaign_performance for single-period views). Usage context is only implied.

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

create_campaignB
Destructive

Creates a new LinkedIn ad campaign within a campaign group. Requires targeting criteria, budget, and objective type.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesCampaign name
typeNoCampaign type. Default: SPONSORED_UPDATES
statusNoInitial status. Default: DRAFT
endDateNoEnd date in YYYY-MM-DD format
costTypeNoCost/bid type. Default: CPM
accountIdYesThe LinkedIn Ad Account ID
startDateNoStart date in YYYY-MM-DD format
localeCountryNoTarget country code. Default: US
objectiveTypeYesCampaign objective type
localeLanguageNoTarget language code. Default: en
unitCostAmountYesBid amount per unit (e.g., "5.00")
campaignGroupIdYesCampaign group ID to place this campaign under
politicalIntentNoPolitical advertising intent. Default: NOT_POLITICAL
unitCostCurrencyNoCurrency code. Default: USD
creativeSelectionNoCreative rotation strategy. Default: OPTIMIZED
dailyBudgetAmountYesDaily budget amount (e.g., "50.00")
targetingCriteriaYesTargeting criteria object with include/exclude conditions. Example: {"include":{"and":[{"or":{"urn:li:adTargetingFacet:locations":["urn:li:geo:103644278"]}}]}}
totalBudgetAmountNoTotal/lifetime budget amount
dailyBudgetCurrencyNoCurrency code. Default: USD
totalBudgetCurrencyNoCurrency code. Default: USD
offsiteDeliveryEnabledNoEnable LinkedIn Audience Network delivery. Default: false
audienceExpansionEnabledNoEnable audience expansion. Default: false

TDQS

B3.2/5.0
Behavior2/5

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

Annotations already declare openWorld, non-idempotent, destructive, and non-read-only behavior, so the safety profile is covered. The description adds nothing beyond that: no mention of required account permissions, whether campaigns are created in DRAFT by default, or what happens on repeat calls. Minimal added value.

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

Conciseness4/5

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

Two short sentences with the action and scope front-loaded and no padding. The second sentence partially restates required-field information already visible in the schema, so it is efficient rather than maximally informative.

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?

For a 22-parameter destructive mutation with no output schema, the description is thin, though the high schema coverage compensates. It says nothing about the creation result (e.g., campaign ID) or default DRAFT status, leaving gaps an agent would need filled elsewhere.

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 description coverage is 100% with enums and defaults fully documented, so the schema carries the parameter burden. The description only names three required inputs generically ('targeting criteria, budget, and objective type') and adds no format or constraint details. Baseline 3 applies.

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

Purpose4/5

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

Clear verb+resource ('Creates a new LinkedIn ad campaign') with placement scope ('within a campaign group') and a summary of the required inputs. It does not explicitly distinguish itself from siblings like create_campaign_group or create_creative, but the resource is unambiguous.

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

Usage Guidelines3/5

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

The second sentence names the prerequisite inputs (targeting criteria, budget, objective type), which is useful context, but there is no explicit when-to-use vs. when-to-use-an-alternative guidance (e.g., update_campaign, create_campaign_group). Usage is only implied.

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

create_campaign_groupB
Destructive

Creates a new LinkedIn campaign group for organizing campaigns. Campaign groups manage status, budget, and performance across multiple related campaigns.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesName of the campaign group (max 100 characters)
statusNoInitial status. Default: DRAFT
endDateNoEnd date in YYYY-MM-DD format. Required if totalBudget is set
accountIdYesThe LinkedIn Ad Account ID
startDateYesStart date in YYYY-MM-DD format
objectiveTypeNoCampaign group objective type. Immutable once set. All campaigns in the group inherit this objective.
dailyBudgetAmountNoDaily budget amount (only with DYNAMIC budget optimization)
totalBudgetAmountNoTotal budget amount (e.g., "5000.00")
dailyBudgetCurrencyNoDaily budget currency code
totalBudgetCurrencyNoBudget currency code (e.g., "USD"). Must match account currency

TDQS

B3.1/5.0
Behavior2/5

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

Annotations declare destructiveHint=true, openWorldHint=true, and idempotentHint=false, which already flag this as a non-idempotent, destructive mutation. The description adds no behavioral context: it doesn't mention required permissions, what happens on failure, or that creation is irreversible. With annotations covering the safety profile at a high level, the description adds little beyond the basic action.

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?

Two concise sentences that front-load the core action and then explain the resource's role. No redundant or filler content.

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?

For a creation tool with 10 parameters, high schema coverage, and annotations covering safety, the description is adequate but minimal. It lacks details on return values (no output schema) and behavioral nuances like permissions or side effects, leaving some gaps.

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 description coverage is 100%, so the schema already documents all 10 parameters, including enums, formats, and immutability. The description provides no parameter-specific details, so baseline 3 is appropriate when the schema does the heavy lifting.

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

Purpose4/5

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

States a specific verb and resource: 'Creates a new LinkedIn campaign group for organizing campaigns.' This clearly identifies the operation. It does not explicitly differentiate from siblings like create_campaign or update_campaign_group, but the 'campaign group' resource is distinct enough.

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

Usage Guidelines2/5

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

No explicit guidance on when to use this tool versus alternatives (e.g., create_campaign for individual campaigns). The description only states the purpose; it does not cover prerequisites, when-not-to-use, or the relationship with sibling tools such as create_campaign.

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

create_creativeA
Destructive

Creates a new LinkedIn creative/ad under a campaign. Requires a content reference (post/share URN) or will be created as a reference to existing content.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNoOptional name for the creative
accountIdYesThe LinkedIn Ad Account ID
campaignIdYesCampaign ID to create the creative under (numeric ID or full URN)
leadgenFormIdNoLead gen form ID (required for LEAD_GENERATION objective campaigns)
intendedStatusNoInitial status. Default: DRAFT
contentReferenceYesURN of the content to sponsor (e.g., urn:li:share:123 or urn:li:ugcPost:123)
leadgenCallToActionLabelNoCall to action label for lead gen campaigns

TDQS

A3.5/5.0
Behavior3/5

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

Annotations already declare a non-read-only, non-idempotent, destructive, open-world write, so safety context is covered. The description adds the behavioral nuance that without a content reference the creative is created as a reference to existing content, but says nothing about side effects, external API requirements, or resulting state beyond the default DRAFT in the schema.

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?

Two short sentences, front-loaded with the action and scope, then the one non-obvious requirement. No filler or repetition.

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?

For a 7-parameter write tool with no output schema, the description omits what the call returns (e.g., the new creative URN) — information an agent likely needs for follow-up calls like update_creative_status. The critical inputs are covered by the schema, so this is adequate but with a real gap.

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 description coverage is 100%, so all seven parameters (including the enum constraints and the leadgenFormId conditional) are already documented inline. The description reiterates the contentReference requirement, which is slightly useful framing, but adds no format or inter-parameter semantics beyond the schema.

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

Purpose4/5

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

States a specific verb and resource ("Creates a new LinkedIn creative/ad") plus its placement scope ("under a campaign"), which separates it from generic create tools. It does not explicitly distinguish itself from the close sibling create_inline_ad, leaving that inference to the agent.

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

Usage Guidelines3/5

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

The description supplies one clear precondition (a content reference is required), but gives no guidance on when to choose this over create_inline_ad or how it relates to update_creative_status/create_campaign. Usage is implied from the create semantics rather than stated.

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

create_inline_adB
Destructive

Creates a new LinkedIn ad with inline content directly (without needing a pre-existing post). This creates the ad content (text, image/video, landing page, CTA) and the creative in a single call. Use this to create sponsored content ads from scratch.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNoName for the creative (for internal reference in Campaign Manager)
mediaIdNoURN of the image or video to use (e.g., urn:li:image:C5510AQE... or urn:li:video:C5510AQE...). Upload media first via LinkedIn's media upload APIs.
accountIdYesThe LinkedIn Ad Account ID
campaignIdYesCampaign ID to create the ad under (numeric ID or full URN)
commentaryYesThe ad text/copy that appears as the post commentary
mediaTitleNoTitle for the media (displayed as headline for videos)
leadgenFormIdNoLead gen form ID (required for LEAD_GENERATION objective campaigns)
intendedStatusNoInitial status. Default: DRAFT
landingPageUrlNoLanding page URL for the ad. Required for WEBSITE_VISIT and WEBSITE_CONVERSIONS campaign objectives.
organizationIdYesOrganization/company ID that will be the ad author (numeric ID or full URN like urn:li:organization:123)
callToActionLabelNoCall to action button label
leadgenCallToActionLabelNoCall to action label for lead gen form button

TDQS

B3.4/5.0
Behavior3/5

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

Annotations already declare the write/destructive, non-idempotent, open-world profile, so the description needn't restate safety. It does add useful behavior — that ad content and creative are created atomically in a single call — but says nothing about what is returned (no output schema) or required permissions/ordering beyond media upload.

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

Conciseness4/5

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

Three short sentences, front-loaded with the definition and mechanism, then the usage cue. Minor redundancy between "in a single call" and "from scratch," but nothing wasted.

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?

For a 12-parameter mutation with no output schema, the description covers what it does but omits the return value (e.g. created ad/creative ID) and any prerequisite ordering or account-scoping constraints. Adequate given the fully documented schema, but not complete for the tool's complexity.

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 description coverage is 100%, so every parameter (including the objective-conditional landingPageUrl and leadgenFormId requirements) is already documented in the schema. The description adds no format, default, or dependency detail beyond that, so the baseline of 3 applies.

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

Purpose4/5

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

States a specific verb and resource ("Creates a new LinkedIn ad with inline content") and clarifies the key mechanism: content and creative are built in one call without a pre-existing post. That implicitly differentiates it from the sibling create_creative, though it never names the alternative directly.

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

Usage Guidelines3/5

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

"Use this to create sponsored content ads from scratch" gives an intended scenario but no explicit when-not-to-use rule or named alternative (e.g. "use create_creative instead if you already have a post"). Usage is implied rather than routed.

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

delete_campaignA
Destructive

Deletes a LinkedIn campaign. Draft campaigns are deleted immediately. Non-draft campaigns are set to PENDING_DELETION status.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdYesThe LinkedIn Ad Account ID
campaignIdYesThe campaign ID to delete

TDQS

A4.3/5.0
Behavior5/5

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

Annotations already declare destructiveHint=true, but the description adds critical subtype behavior: draft campaigns are deleted immediately while non-draft campaigns become PENDING_DELETION. This is exactly the 'what gets destroyed' context the bar rewards.

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?

Two sentences, zero waste, and the subtype-specific behavior is front-loaded after the core action. Efficiently structured.

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

Completeness5/5

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

Complete for a destructive delete tool: annotations carry the safety profile, schema fully documents params, and the description fills the one remaining gap—how deletion behaves for drafts vs non-drafts. No output schema is needed since the operation returns no meaningful object.

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 100%, so the schema already documents accountId and campaignId. The description adds no syntax or format details beyond what's in the schema. Baseline 3 is appropriate.

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?

Specific verb (deletes) + resource (LinkedIn campaign). Clearly distinguishable from sibling delete_campaign_group and update_campaign by naming the exact resource.

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

Usage Guidelines3/5

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

No explicit when-to-use or when-not-to-use guidance, and no alternatives mentioned. The draft vs non-draft behavior is implied context, but doesn't route the agent to another tool.

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

delete_campaign_groupA
Destructive

Deletes a LinkedIn campaign group. Draft groups are deleted immediately. Non-draft groups are set to PENDING_DELETION status.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdYesThe LinkedIn Ad Account ID
campaignGroupIdYesThe campaign group ID to delete

TDQS

A3.5/5.0
Behavior4/5

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

Annotations already declare destructiveHint=true and idempotentHint=false, so the safety profile is covered. The description nonetheless adds genuinely new context: draft groups vanish immediately while non-draft groups only move to PENDING_DELETION status, which is the key operational distinction for a caller. It stops short of explaining what PENDING_DELETION ultimately means or whether child campaigns are affected.

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?

Two sentences, both earning their place: the first establishes the action, the second front-loads the behavioral nuance that matters most. No filler, no repetition of the name or annotations.

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?

For a destructive tool with full schema coverage and annotations, the definition covers the core nuance (immediate vs deferred deletion). However, it omits relevant consequences such as whether contained campaigns/creatives are also removed, whether the async deletion can be reverted, and what the caller must supply in terms of permissions.

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 description coverage is 100% (both accountId and campaignGroupId are documented in the schema), so the baseline of 3 applies. The description adds no syntax, format, or identifier guidance beyond the schema.

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

Purpose4/5

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

States a specific verb (Deletes) and resource (LinkedIn campaign group) that is inherently distinct from siblings such as delete_campaign and update_campaign_group. It never explicitly routes the agent between those siblings, but the resource noun does the disambiguation work.

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

Usage Guidelines2/5

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

No when-to-use guidance, no prerequisites, and no mention of alternatives like update_campaign_group or delete_campaign. The draft vs non-draft sentence describes behavior, not the conditions under which an agent should pick this tool.

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

get_account_detailsA
Read-onlyIdempotent

Retrieves detailed information about a specific LinkedIn Ad Account including status, currency, notification settings, and linked organization.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdYesThe LinkedIn Ad Account ID (numeric ID, not the URN)

TDQS

A3.6/5.0
Behavior3/5

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

Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false, and openWorldHint=true, so the safety and idempotency profile is covered by structured data. The description adds only the set of returned field categories; it says nothing about authorization requirements, error behavior on an invalid/non-numeric ID, or rate limits.

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?

One sentence, front-loaded with the verb and resource, with zero filler or repetition. Every clause (the enumerated field categories) carries information.

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

Completeness4/5

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

For a one-parameter read-only tool with full schema coverage and annotations covering the safety profile, the description is nearly sufficient, and its enumeration of returned fields partially compensates for the absence of an output schema. It stops short of covering failure modes or ID format validation beyond the schema note.

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?

There is a single parameter with 100% schema description coverage, and the schema already supplies the key nuance ('numeric ID, not the URN'). The description adds no parameter-level detail beyond the schema, which is the expected baseline when coverage is high.

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

Purpose4/5

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

States a specific verb (Retrieves) and resource (LinkedIn Ad Account details) and enumerates the fields returned (status, currency, notification settings, linked organization). The phrase 'a specific ... Account' implicitly distinguishes it from list_ad_accounts, though the sibling is not named explicitly.

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

Usage Guidelines3/5

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

Usage is implied: it takes a single accountId and returns one account's details, so the agent can infer it is the point-lookup counterpart to list_ad_accounts. However, there is no explicit when-to-use/when-not guidance, no named alternative, and no prerequisites such as required permissions or valid-ID expectations.

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

get_analyticsA
Read-onlyIdempotent

Query raw LinkedIn analytics with custom metrics, entity or demographic pivots, filters, and time granularity. Large metric selections are split and merged automatically.

ParametersJSON Schema
NameRequiredDescriptionDefault
pivotYes
endDateNo
metricsNo
accountIdYes
campaignsNo
creativesNo
startDateYes
campaignGroupsNo
timeGranularityNo

TDQS

A3.7/5.0
Behavior4/5

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

Annotations already cover readOnly/openWorld/idempotent/destructive, so the safety profile is settled. The description adds a genuinely non-obvious operational behavior — that large metric selections are 'split and merged automatically', meaning results may be fetched via internal fan-out — which matters for latency and limit expectations and is not derivable from the schema or annotations.

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?

Two sentences with zero filler. The core capability is front-loaded in sentence one, and the auto-split/merge caveat follows immediately, earning its place by changing expectations about result assembly.

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?

There is no output schema, so return values need not be described, and annotations cover the safety profile. What remains missing for a 9-parameter, 0%-coverage query tool with demographic pivots and date parameters is format/permission guidance — accepted date strings, account scoping, and the practical meaning of the demographic pivot enum values.

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 description coverage is 0% across 9 parameters, so the description carries the burden and only partially does so: it names the metric, pivot, filter and granularity concepts but never explains date formats, ID formats, the 100-metric/500-entity caps, or what the 13 pivot enum values mean. It gives category-level mapping to the schema, not the format-level detail a 0%-coverage schema lacks.

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

Purpose4/5

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

States a specific verb (Query) and resource (raw LinkedIn analytics) and enumerates the dimensions of control: custom metrics, entity or demographic pivots, filters, time granularity. This implicitly distinguishes it from the fixed-shape siblings (get_campaign_performance, get_daily_trends), but it never names an alternative, so sibling differentiation is left to inference.

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

Usage Guidelines3/5

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

The phrase 'custom metrics, entity or demographic pivots' implies this is the flexible/generic query tool to reach for when the prebuilt reports won't do, which is real implied guidance. However there is no explicit when/when-not statement and no mention of which sibling report tools it supersedes, so an agent must infer the routing.

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

get_audience_demographicsA
Read-onlyIdempotent

Retrieves demographic breakdown of who saw or interacted with your ads. Shows performance segmented by job function, seniority, industry, company size, or geographic location. Essential for understanding if you're reaching your target audience. Note: Demographic data has a 12-24 hour delay.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoTop N results to return (max 100). Default: 25
metricNoPrimary metric to sort by. Default: impressions
endDateNoEnd date in YYYY-MM-DD format. Default: today
accountIdYesThe LinkedIn Ad Account ID
startDateYesStart date in YYYY-MM-DD format
campaignIdsNoFilter by specific campaigns
demographicTypeYesThe demographic dimension to analyze (MEMBER_COUNTRY and MEMBER_REGION are the newer versions)

TDQS

A3.7/5.0
Behavior4/5

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

Annotations already declare the safe read profile (readOnly, idempotent, non-destructive), so the description's main contribution is the "12-24 hour delay" freshness caveat on demographic data, which the agent cannot learn from structured fields. It stops short of describing result volume, pagination, or whether the delay differs by dimension.

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

Conciseness4/5

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

Four short sentences, front-loaded with what the tool returns and ending on the caveat. The "Essential for understanding..." sentence is mild sales framing rather than operational information, costing it a point.

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

Completeness4/5

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

For a read-only analytics tool with no output schema and full parameter documentation, the description covers purpose, the segmentation dimensions, and the data-latency caveat. Return shape is not explained, but no output schema exists to lean on, leaving a modest gap for a tool that can return ranked result sets.

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 100%, so all seven parameters including both enums are already documented in the schema. The description lists the demographic dimensions, but this only paraphrases the demographicType enum values and adds nothing on limit, metric, date handling, or campaignIds. Baseline 3 applies.

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

Purpose4/5

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

States a specific verb and resource (retrieves demographic breakdown / performance segmented by dimension) and enumerates the segmentation dimensions, which implicitly separates it from get_campaign_performance and get_creative_performance. It never names a sibling tool outright, so an agent must infer the boundary rather than being told it.

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

Usage Guidelines3/5

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

"Essential for understanding if you're reaching your target audience" gives a use case, but there is no guidance on when to prefer this over get_audience_reach, get_analytics, or compare_performance, and no exclusions or prerequisites. Usage is implied rather than specified.

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

get_audience_reachA
Read-onlyIdempotent

Shows unique member reach and native audience penetration for campaigns. Returns LinkedIn's native audiencePenetration metric (approximate unique members reached / total target audience size). Helps understand what percentage of your target audience you've reached. Note: Date range must be 92 days or less.

ParametersJSON Schema
NameRequiredDescriptionDefault
endDateNoEnd date in YYYY-MM-DD format. Default: today
accountIdYesThe LinkedIn Ad Account ID
startDateYesStart date in YYYY-MM-DD format (max 92 days range)
campaignIdsNoFilter by specific campaigns
campaignGroupIdsNoFilter by campaign groups

TDQS

A3.7/5.0
Behavior4/5

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

Annotations already establish the read-only, idempotent, non-destructive, open-world profile, lowering the bar. The description adds domain context by defining the audiencePenetration metric and its formula, plus the 92-day date constraint, though the latter partly duplicates the schema.

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

Conciseness4/5

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

The definition leads with the purpose and keeps to four short sentences. The 'Helps understand what percentage...' sentence is somewhat redundant with the preceding metric definition, but overall it is well front-loaded and low-waste.

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

Completeness4/5

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

With no output schema, the description carries the burden of explaining return values, and it does define the key audiencePenetration metric. It stops short of describing other fields or aggregation/segmentation behavior, leaving a small gap for a reporting tool.

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 description coverage is 100%, so all five parameters are already documented in the schema. The description adds no parameter-level syntax or format beyond the date-range note, so the baseline of 3 applies.

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

Purpose4/5

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

The description states a specific verb and resource (shows unique member reach / audience penetration for campaigns) and even names the exact metric returned (audiencePenetration). It does not, however, differentiate itself from siblings like get_audience_demographics or get_analytics, so an agent must infer when reach is the right report.

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

Usage Guidelines3/5

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

Usage is only implied: 'Helps understand what percentage of your target audience you've reached' suggests a reach-measurement context but gives no explicit when-to-use or when-to-prefer-a-sibling guidance. The 92-day constraint is included, which is useful, but no alternatives are named.

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

get_campaign_groupsB
Read-onlyIdempotent

Lists all campaign groups for an account with their configuration and optionally aggregated performance. Campaign groups help organize campaigns and control budget at a higher level.

ParametersJSON Schema
NameRequiredDescriptionDefault
statusNoFilter by status
endDateNoEnd date for performance (if included)
accountIdYesThe LinkedIn Ad Account ID
startDateNoStart date for performance (if included)
includePerformanceNoInclude performance metrics. Default: true

TDQS

B3.2/5.0
Behavior3/5

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

Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false and openWorldHint, so safety is fully covered structurally. The description adds useful domain context that campaign groups organize campaigns and control budget at a higher level, plus that performance data is optional and aggregated, but says nothing about pagination, result limits, or freshness of the performance metrics.

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

Conciseness4/5

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

Two tight sentences with the core action front-loaded and no filler. The second sentence is explanatory rather than operational, but it is short and earns its place by disambiguating campaign groups.

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?

There is no output schema, so the description bears more burden for return values; it does say the response includes configuration and optionally aggregated performance, which is a reasonable sketch. However, it omits pagination, result limits, and how the date parameters interact with includePerformance, leaving gaps for a five-parameter list tool.

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 description coverage is 100%, so all five parameters are already documented in the schema and the baseline is 3. The description's mention of optional aggregated performance loosely maps to includePerformance, but adds no syntax, date-format, or default details beyond what the schema provides.

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

Purpose4/5

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

States a specific verb and resource ('Lists all campaign groups for an account') and clarifies the returned scope (configuration plus optionally aggregated performance). The second sentence explains what a campaign group is, which helps distinguish it from list_campaigns, though it never names that sibling explicitly.

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

Usage Guidelines2/5

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

There is no when-to-use or when-not-to-use guidance, no mention of alternatives such as list_campaigns or get_campaign_performance, and no indication of when to enable performance or apply the status filter. The phrase 'optionally aggregated performance' hints at a use case but leaves the choice entirely to inference.

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

get_campaign_performanceA
Read-onlyIdempotent

Retrieves performance metrics for campaigns within a specified date range. Returns key metrics like impressions, clicks, spend, CTR, conversions, audience penetration, and average dwell time. The primary tool for daily campaign monitoring and optimization decisions.

ParametersJSON Schema
NameRequiredDescriptionDefault
endDateNoEnd date in YYYY-MM-DD format. Default: today
metricsNoOptional LinkedIn metric names. Missing standard metrics are not available in the response; use get_analytics for raw custom metrics.
accountIdYesThe LinkedIn Ad Account ID
startDateYesStart date in YYYY-MM-DD format
campaignIdsNoSpecific campaign IDs to filter. If omitted, returns all campaigns.
timeGranularityNoTime granularity for the data. Default: ALL
campaignGroupIdsNoFilter by campaign group IDs

TDQS

A3.6/5.0
Behavior3/5

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

Annotations already declare readOnlyHint, idempotentHint, openWorldHint, and non-destructive, so the safety profile is fully covered. The description adds the metric list returned, which is useful behavioral context given the absence of an output schema, but says nothing about rate limits, auth requirements, or response shape.

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 tight sentences: verb+resource first, return contents second, use case third. No filler, every sentence carries distinct information, and the actionable purpose is front-loaded.

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

Completeness4/5

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

With no output schema, the description compensates by listing the key metrics returned, and the schema fully documents all 7 parameters. It is nearly complete; a brief note on granularity/pagination behavior or how it relates to sibling performance tools would close the remaining gap.

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 description coverage is 100%, so each of the 7 parameters is documented in the schema itself, establishing a baseline of 3. The description adds no parameter-level detail (formats, filter behavior) beyond what the schema already states.

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

Purpose4/5

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

States a specific verb and resource ('Retrieves performance metrics for campaigns') and enumerates the metrics returned, so the agent knows exactly what it gets. However, it does not differentiate itself from the many performance siblings (get_creative_performance, get_conversion_performance, get_daily_trends, compare_performance) beyond claiming to be the 'primary' tool.

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

Usage Guidelines3/5

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

'The primary tool for daily campaign monitoring and optimization decisions' implies a use case, which is better than nothing. But it names no alternatives and gives no when-not guidance, even though the sibling set contains several overlapping performance tools. The only alternative hint ('use get_analytics for raw custom metrics') lives in the schema, not the description.

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

get_conversion_performanceB
Read-onlyIdempotent

Retrieves conversion metrics broken down by conversion type/action. Shows which conversions are being driven by which campaigns, with cost per conversion and conversion value.

ParametersJSON Schema
NameRequiredDescriptionDefault
endDateNoEnd date in YYYY-MM-DD format. Default: today
accountIdYesThe LinkedIn Ad Account ID
startDateYesStart date in YYYY-MM-DD format
campaignIdsNoFilter by specific campaigns
includePostViewNoInclude view-through conversions. Default: true
timeGranularityNoTime granularity. Default: ALL

TDQS

B3.4/5.0
Behavior3/5

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

Annotations already declare this is a read-only, idempotent, non-destructive, open-world operation, so safety is covered. The description adds genuinely useful output context (conversion attribution to campaigns, cost per conversion, conversion value), but says nothing about pagination, result limits, or latency on large date ranges.

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?

Two compact sentences that front-load the primary purpose and then the return contents. No filler, no repetition of the tool name.

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

Completeness4/5

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

With no output schema, the description usefully sketches what comes back (per-conversion-type metrics with campaign attribution and cost/value). Given 100% schema coverage and full annotations, the only real gap is nothing about result size or granularity implications.

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 description coverage is 100%, so all six parameters (dates, accountId, campaignIds, includePostView, timeGranularity) are already documented in the schema. The description adds no additional meaning about parameter behavior or formats, so the baseline of 3 applies.

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

Purpose4/5

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

States a specific verb ('Retrieves') and resource ('conversion metrics broken down by conversion type/action') and lists the payload dimensions (campaign attribution, cost per conversion, conversion value). It does not explicitly distinguish itself from siblings like get_campaign_performance or list_conversions, which an agent must infer from the 'by conversion type' framing.

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

Usage Guidelines2/5

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

There is no guidance on when to choose this over get_campaign_performance, get_creative_performance, or list_conversions, nor any stated prerequisites or exclusions. Usage is only implied by the breakdown-by-conversion-type phrasing.

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

get_creative_performanceB
Read-onlyIdempotent

Retrieves performance metrics for individual ad creatives. Shows which specific ads are performing best, including engagement metrics (likes, comments, shares), video metrics, and average dwell time. Essential for creative optimization.

ParametersJSON Schema
NameRequiredDescriptionDefault
endDateNoEnd date in YYYY-MM-DD format. Default: today
accountIdYesThe LinkedIn Ad Account ID
startDateYesStart date in YYYY-MM-DD format
campaignIdsNoFilter creatives by campaign IDs
creativeIdsNoSpecific creative IDs to fetch
timeGranularityNoTime granularity. Default: ALL
includeVideoMetricsNoInclude video-specific metrics. Default: true

TDQS

B3.2/5.0
Behavior3/5

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

Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the safety profile is fully covered. The description adds useful context about what is returned (engagement metrics, video metrics, dwell time), but discloses no rate limits, aggregation behavior, or granularity semantics beyond the schema's default.

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

Conciseness4/5

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

Three sentences, front-loaded with the core purpose and the metric categories, with no wasted filler. The closing marketing line ('Essential for creative optimization') is slightly promotional but harmless.

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

Completeness4/5

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

With no output schema, the description usefully enumerates the returned metric families, which is the key missing structured info. Safety and parameters are covered by annotations and schema, leaving little an agent needs that is absent.

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 100%, so all 7 parameters including date formats, granularity enum, and campaign/creative filters are already documented in the schema. The description adds no syntax or format detail beyond that, so the baseline of 3 applies.

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

Purpose4/5

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

States a specific verb ('Retrieves') plus resource ('performance metrics for individual ad creatives') and specifies the creative-level granularity, which implicitly separates it from campaign- or conversion-level siblings. However, it never names an alternative like get_campaign_performance or get_analytics, so the agent must infer the scope distinction.

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

Usage Guidelines2/5

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

There is no explicit when-to-use or when-not-to-use guidance. 'Essential for creative optimization' is a benefit statement, not a routing rule, and no sibling or alternative is referenced.

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

get_lead_gen_performanceB
Read-onlyIdempotent

Retrieves lead generation form performance including form submissions, qualified leads, and cost per lead. Essential for B2B marketers running lead gen campaigns.

ParametersJSON Schema
NameRequiredDescriptionDefault
endDateNoEnd date in YYYY-MM-DD format. Default: today
accountIdYesThe LinkedIn Ad Account ID
startDateYesStart date in YYYY-MM-DD format
campaignIdsNoFilter by specific campaigns
timeGranularityNoTime granularity. Default: ALL

TDQS

B3.2/5.0
Behavior3/5

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

Annotations already declare readOnlyHint, idempotentHint, non-destructive, and openWorld, so the safety profile is covered. The description adds the returned metric set (submissions, qualified leads, cost per lead), which is useful. It says nothing about date-range behavior, granularity effects, row limits, or aggregation semantics.

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

Conciseness4/5

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

Two sentences with the core capability front-loaded in the first. The second sentence is soft marketing copy that gives an agent little actionable value, but the whole definition is short enough that nothing is buried.

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

Completeness4/5

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

A read-only analytics tool with complete schema coverage and no output schema; the description compensates by naming the metrics returned. Safety is covered by annotations. Missing only guidance on the analytics siblings, which is a usage rather than completeness gap.

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 description coverage is 100%, so all five parameters (accountId, startDate, endDate, campaignIds, timeGranularity) are documented at the schema level, including the enum and date format. The description adds no parameter-level meaning beyond that, so the baseline 3 applies.

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

Purpose4/5

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

States a specific verb (retrieves) and resource (lead generation form performance) plus the three metric families returned: submissions, qualified leads, cost per lead. This distinguishes it from get_campaign_performance and get_conversion_performance by resource scope, though it never names those siblings explicitly.

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

Usage Guidelines2/5

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

The only usage signal is the audience tagline 'Essential for B2B marketers running lead gen campaigns' – a persona statement, not a condition. It gives no guidance on when to pick this over get_conversion_performance, list_lead_forms, or get_analytics, and states no prerequisites or exclusions.

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

get_server_statusA
Read-only

Returns local configuration and token expiration status without exposing secrets or making API calls.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true, destructiveHint=false, and openWorldHint=false, so the safety profile is covered. The description adds genuinely new context beyond them: it will not expose secrets and performs no network/API calls, which is exactly the kind of assurance an agent needs before surfacing config data.

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?

A single front-loaded sentence with no filler; the return content comes first and the two safety constraints ('without exposing secrets', 'without making API calls') are packed into the same clause without bloat.

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

Completeness4/5

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

For a zero-parameter, read-only status tool with full annotation coverage, the description is nearly sufficient — it names the data returned and the no-secrets/no-API guarantees. No output schema exists, and the description gives only a summary of the return shape rather than enumerating config fields, which is the one modest gap.

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

Parameters4/5

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

The tool takes zero parameters, so there is nothing for the description to disambiguate and the baseline of 4 applies. No syntactic or format guidance is needed for a no-arg call.

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?

States a specific verb (Returns) and precisely names the two resources delivered: local configuration and token expiration status. The scope marker 'local ... without making API calls' cleanly separates it from the entire sibling set of ad-account/API tools.

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

Usage Guidelines3/5

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

Usage is only implied — the 'local configuration and token expiration status' framing suggests a diagnostics/health-check use case, and 'without making API calls' hints at when it is cheap to call. No explicit when-to-use trigger or named alternative is given, so the agent must infer the context.

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

list_ad_accountsA
Read-onlyIdempotent

Lists all LinkedIn Ad Accounts accessible to the authenticated user. Returns account names, IDs, status, currency, and serving statuses. Use this to get an overview of all accounts or find a specific account ID.

ParametersJSON Schema
NameRequiredDescriptionDefault
typeNoFilter by account type.
statusNoFilter by account status. If not specified, returns all statuses.
includeTestNoInclude test accounts. Default: false

TDQS

A3.9/5.0
Behavior4/5

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

Annotations already declare readOnlyHint, idempotentHint, non-destructive and openWorld, so the safety profile is covered. The description adds genuinely useful context beyond that: it enumerates the returned fields (names, IDs, status, currency, serving statuses) and the authenticated-user access scope. It omits pagination/result-limit behavior, which keeps it off a 5.

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

Conciseness4/5

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

Three short sentences, front-loaded with what is listed before what is returned and when to use it. There is minor overlap between 'all accounts accessible to the authenticated user' and 'get an overview of all accounts', but no wasted sentences.

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

Completeness4/5

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

With no output schema, describing the returned fields is necessary and it is done. All three parameters are covered by the schema and safety is covered by annotations, so the only remaining gap is pagination/ordering behavior and any result cap on an openWorld list call.

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 description coverage is 100%: each of the three filters (type, status, includeTest) is documented in the schema with enums and a default. The description adds no filter syntax, combination rules, or defaulting behavior, so the schema carries the full burden and the baseline of 3 applies.

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

Purpose4/5

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

The description states a specific verb and resource ('Lists all LinkedIn Ad Accounts') and scopes it to accounts 'accessible to the authenticated user', so the agent knows exactly what set is returned. It does not explicitly distinguish itself from the sibling get_account_details, which is the one overlap an agent might hesitate on.

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?

It gives clear positive usage context: 'Use this to get an overview of all accounts or find a specific account ID.' That covers the two realistic intents (browse vs. resolve an ID). It stops short of naming when NOT to use it (e.g., use get_account_details when you already have the ID and want detail), so it is clear but not fully routing.

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

list_campaignsA
Read-onlyIdempotent

Lists campaigns for a LinkedIn Ad Account. Unlike get_campaign_performance (which only returns campaigns with analytics data), this tool returns ALL campaigns including DRAFT and PAUSED campaigns with zero impressions. Supports filtering by campaign group and status.

ParametersJSON Schema
NameRequiredDescriptionDefault
statusNoFilter by campaign status. If omitted, returns all statuses.
accountIdYesThe LinkedIn Ad Account ID
campaignGroupIdsNoFilter by campaign group IDs (numeric IDs)

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already declare readOnly, idempotent, non-destructive, open-world reads, so the safety profile is covered. The description adds genuinely useful behavior beyond that: it discloses result completeness (includes zero-impression DRAFT/PAUSED campaigns), which is exactly the trait an agent needs to reason about output. It doesn't mention pagination or result limits.

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?

Two sentences, no filler, with the defining contrast front-loaded after the core purpose. Every sentence earns its place.

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

Completeness4/5

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

For a read-only list tool with full schema coverage and annotations, the description is nearly complete. The only unaddressed item is return volume/pagination behavior, which matters somewhat for a list endpoint with no output schema, but it is a minor gap.

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 100% and each parameter has its own description, so the schema carries the semantics. The description only echoes that campaign group and status filtering are supported, adding no syntax or format detail beyond structured data — baseline 3.

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?

States a specific verb and resource ('Lists campaigns') scoped to a LinkedIn Ad Account, and explicitly contrasts with get_campaign_performance by noting it returns ALL campaigns including DRAFT/PAUSED with zero impressions. An agent can distinguish it from the sibling without opening either schema.

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?

Names the alternative (get_campaign_performance) and the condition that selects this tool instead (you need campaigns without analytics data). It stops short of stating an explicit when-not-to-use or a full routing rule, but the selection context is clear.

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

list_conversionsB
Read-onlyIdempotent

Lists all conversion tracking rules configured for an account. Shows conversion names, types, attribution windows, and enabled status.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdYesThe LinkedIn Ad Account ID
enabledOnlyNoOnly show enabled conversions. Default: false

TDQS

B3.4/5.0
Behavior3/5

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

Annotations already declare readOnlyHint=true, idempotentHint=true and destructiveHint=false, so the safety profile is covered. The description adds useful context by listing the shape of returned fields and the account scoping, but says nothing about pagination, result limits, or permissions.

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?

Two short sentences, front-loaded with the verb and resource, with the second sentence earning its place by previewing the returned fields. No filler.

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

Completeness4/5

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

For a read-only listing tool with full annotation coverage and complete parameter documentation, the description is nearly sufficient; listing the returned fields compensates for the absent output schema. Only the lack of any routing hint toward get_conversion_performance keeps it from being fully complete.

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 100%, so both accountId and the enabledOnly filter are fully documented in the schema; the description adds no syntax or format detail beyond it. Baseline 3 is appropriate when the schema does the heavy lifting.

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

Purpose4/5

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

States a specific verb (Lists) and resource (conversion tracking rules) scoped to an account, and enumerates the returned fields (names, types, attribution windows, enabled status), which implicitly separates it from the performance-oriented sibling get_conversion_performance. It stops short of naming that sibling explicitly, so it is clear but not fully differentiated.

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

Usage Guidelines2/5

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

There is no when-to-use or when-not-to-use guidance, and no mention of the closely named alternative get_conversion_performance. An agent must infer from 'configured for an account' that this returns configuration rather than metrics.

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

list_lead_formsB
Read-onlyIdempotent

Lists all lead generation forms configured for an account with their questions and settings. Helps understand what forms are available and their configuration.

ParametersJSON Schema
NameRequiredDescriptionDefault
statusNoFilter by status
accountIdYesThe LinkedIn Ad Account ID
includeQuestionsNoInclude form questions. Default: true

TDQS

B3.2/5.0
Behavior3/5

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

Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false, and openWorldHint=true, so the safety profile is fully covered without the description. The description adds that results include questions and settings, which is mildly useful, but says nothing about pagination, result limits, or ordering.

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

Conciseness4/5

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

Two short sentences, front-loaded with the core action. The second sentence is largely redundant with the first ('their questions and settings' vs 'their configuration'), which is the only waste.

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

Completeness4/5

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

For a simple read-only list tool with no output schema, the description covers what is returned (forms plus questions and settings) and the account scope. Only the absence of any note on result volume or pagination keeps it from being fully complete.

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 description coverage is 100%, so accountId, status (DRAFT/PUBLISHED/ARCHIVED), and includeQuestions are already documented in the schema. The description adds no parameter-level detail such as default filtering behavior or the cost of including questions, so the baseline 3 applies.

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

Purpose4/5

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

The description names a specific verb and resource (lists lead generation forms), scopes it to an account, and even states what each item carries (questions and settings). It is unambiguous, though it does not explicitly differentiate itself from sibling listing tools like list_campaigns or list_saved_audiences.

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

Usage Guidelines2/5

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

There is no explicit guidance on when to call this versus other list tools, and no prerequisites (e.g., that a valid accountId from list_ad_accounts is required). The second sentence, 'Helps understand what forms are available,' is a restatement of purpose rather than usable when-to-use guidance.

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

list_saved_audiencesA
Read-onlyIdempotent

Lists saved/matched audiences available in the account for targeting. Shows audience names, sizes, and statuses to help plan campaign targeting.

ParametersJSON Schema
NameRequiredDescriptionDefault
statusNoFilter by status
accountIdYesThe LinkedIn Ad Account ID
audienceTypeNoFilter by audience type

TDQS

A3.6/5.0
Behavior3/5

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

Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false, and openWorldHint=true, so safety is well covered. The description adds that returned data covers names, sizes, and statuses, but does not mention pagination, rate limits, or the implications of the open-world/network dependency. Modest added value beyond annotations.

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?

Two tight sentences, front-loaded with the verb and resource. Every clause earns its place, and no redundancy with the title or schema.

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

Completeness4/5

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

For a low-risk read-only list tool with full schema coverage and no output schema, the description covers purpose, scope, and returned fields sufficiently. The only minor gap is lack of routing guidance to sibling audience tools, which is not critical for invocation correctness.

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 description coverage is 100%, so all three parameters (accountId, status, audienceType) are already documented in the schema. The description does not add filtering syntax, defaults, or enum semantics beyond what the schema provides, so baseline 3 applies.

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

Purpose4/5

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

States a specific verb (Lists) and resource (saved/matched audiences) with clear scope ('available in the account for targeting'). Distinguishes itself from audience analytics siblings like get_audience_demographics or get_audience_reach by framing it as a discovery/list tool, though it doesn't explicitly name an alternative.

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

Usage Guidelines3/5

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

The phrase 'to help plan campaign targeting' implies the use case (pre-campaign audience discovery), but it does not state when to use this versus get_audience_reach or get_audience_demographics, nor does it give any exclusions. Usage context is implied only.

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

search_ad_libraryA
Read-onlyIdempotent

Searches the LinkedIn Ad Library for ads matching criteria. Find and analyze real ads from any advertiser across different countries and time periods. Supports filtering by keyword, advertiser name, country, date range, targeting facets, and impression range. Returns ad preview URLs, advertiser details, targeting information, and impression statistics. Maximum 25 results per request (use pagination).

ParametersJSON Schema
NameRequiredDescriptionDefault
countNoNumber of results per page (max 25). Default: 25
startNoPagination offset. Default: 0
endDateNoEnd date for when ads were served (YYYY-MM-DD format, exclusive). Default: 1 day ago.
keywordNoKeywords to search in ad content. Multiple keywords separated by space are treated as AND logic.
countriesNoTwo-letter country codes to filter ads by served country (e.g., "us", "gb", "de").
payerNameNoEntity who paid for sponsoring the ad.
startDateNoStart date for when ads were served (YYYY-MM-DD format). Default: 2 days ago.
advertiserNoAdvertiser name to search against.
sortByFieldNoField to sort by. Default: CREATED_TIME
sortByOrderNoSort order. Default: DESCENDING
totalImpressionsRangeToNoUpper bound of total impressions range (0 = 1M+).
totalImpressionsRangeFromNoLower bound of total impressions range.
excludedTargetingFacetCategoriesNoFilter ads that exclude these targeting facet categories.
includedTargetingFacetCategoriesNoFilter ads that include these targeting facet categories.

TDQS

A3.9/5.0
Behavior4/5

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

Annotations already declare readOnly, idempotent, openWorld, and non-destructive, so the safety profile is fully covered. The description adds behavior the annotations cannot: a hard cap of 25 results per request with an instruction to paginate, plus a summary of what the response contains (preview URLs, advertiser details, targeting info, impression stats). It does not mention auth requirements or rate limits, but the cap-and-paginate note is genuinely useful.

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

Conciseness4/5

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

Purpose is front-loaded in the first sentence and the practical constraint (25-result cap) is stated plainly. The second sentence ('Find and analyze real ads from any advertiser...') largely restates the first with marketing flavor, and the return-value inventory is a touch listy, but nothing is confusing or bloated.

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

Completeness4/5

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

For a 14-parameter, zero-required, open-world search with no output schema, the description does solid work: it compensates for the missing output schema by naming the returned fields and warns about the pagination limit. It stops short of explaining default behaviors (e.g., the implicit recent date window) or how multiple filters combine, which would fully close the gap.

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 description coverage is 100% across 14 parameters, so the schema already documents syntax, formats, and defaults in detail. The description only enumerates filter categories (keyword, advertiser, country, date range, targeting facets, impression range), which maps to the schema but adds no meaning beyond it. Baseline 3 applies.

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?

States a specific verb and resource ('Searches the LinkedIn Ad Library for ads matching criteria') and immediately scopes it to 'real ads from any advertiser across different countries and time periods'. This is unmistakably distinct from every sibling, all of which operate on the caller's own accounts, campaigns, or creatives.

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

Usage Guidelines3/5

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

The description conveys the research context (analyze any advertiser's ads across countries/time periods), which implies when it is useful, but it never states an explicit when-to-use condition or names an alternative among the siblings. No exclusions or prerequisites are given, so the agent must infer the intended use case.

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

update_campaignA
Destructive

Updates an existing LinkedIn campaign. Can change status (ACTIVE/PAUSED/ARCHIVED), budget, name, targeting, bid amount, and other settings. Uses partial update - only specified fields are changed.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNoNew campaign name
statusNoNew status. Common transitions: DRAFT->ACTIVE, ACTIVE->PAUSED, PAUSED->ACTIVE, ACTIVE->ARCHIVED
endDateNoNew end date as Unix timestamp in milliseconds
accountIdYesThe LinkedIn Ad Account ID
campaignIdYesThe campaign ID to update
unitCostAmountNoNew bid amount per unit
unitCostCurrencyNoBid currency code
dailyBudgetAmountNoNew daily budget amount
targetingCriteriaNoNew targeting criteria object. Replaces existing targeting entirely.
totalBudgetAmountNoNew total/lifetime budget amount
dailyBudgetCurrencyNoBudget currency code
totalBudgetCurrencyNoBudget currency code
offsiteDeliveryEnabledNoEnable/disable LinkedIn Audience Network
optimizationTargetTypeNoOptimization target type
audienceExpansionEnabledNoEnable/disable audience expansion

TDQS

A3.8/5.0
Behavior4/5

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

Annotations already declare destructiveHint=true and idempotentHint=false, so the safety profile is covered. The description adds the valuable partial-update semantic ('only specified fields are changed'), which tells the agent it won't clobber unrelated settings — a real behavioral detail beyond the annotations. It stops short of noting permissions or irreversibility of destructive changes.

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

Conciseness4/5

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

Three tight sentences; the core action is front-loaded and the partial-update caveat is a useful closer. Every sentence earns its place, though it could be marginally denser.

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?

For a 15-parameter mutation tool with no output schema, the description covers the mutation and partial-update behavior adequately, but omits auth/permission requirements and what a successful response returns. Annotations carry the safety profile, but the mutation risk and side effects (e.g., targeting replacement) are only visible in the schema.

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 description coverage is 100%, so the baseline is 3. The description names several updatable fields but adds no syntax or format detail beyond what the schema already documents (e.g., timestamp formats, currency pairing, enumeration values).

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?

States a specific verb and resource ('Updates an existing LinkedIn campaign') and enumerates the mutable fields (status, budget, name, targeting, bid). This clearly distinguishes it from sibling tools like create_campaign and delete_campaign without needing to open either schema.

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

Usage Guidelines3/5

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

'Updates an existing' implies the campaign must already exist, giving implied usage context. However, it never states when to prefer this over create_campaign, delete_campaign, or update_campaign_group, and no exclusions or prerequisites are offered.

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

update_campaign_groupB
Destructive

Updates an existing LinkedIn campaign group. Can change status (ACTIVE/PAUSED/ARCHIVED/DRAFT), budget, name, or end date.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNoNew name for the campaign group
statusNoNew status. ACTIVE->PAUSED, ACTIVE->ARCHIVED, DRAFT->ACTIVE are common transitions.
endDateNoNew end date as Unix timestamp in milliseconds
accountIdYesThe LinkedIn Ad Account ID
campaignGroupIdYesThe campaign group ID to update
totalBudgetAmountNoNew total budget amount
totalBudgetCurrencyNoBudget currency code

TDQS

B3.2/5.0
Behavior3/5

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

Annotations already declare readOnlyHint=false, destructiveHint=true, and idempotentHint=false, so the agent knows this is a destructive, non-idempotent write. The description adds the concrete set of changeable fields, which is useful, but says nothing about permissions, reversibility, or partial-update semantics beyond what structured data covers.

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

Conciseness4/5

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

Two tight sentences, front-loaded with the action and then the mutable fields, with no filler. Slightly padded by the parenthetical enum, which duplicates the schema.

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?

For a destructive mutation tool with no output schema, the definition is adequate but thin: it doesn't clarify whether fields update independently, what a successful response looks like, or any rate/permission constraints. Annotations carry the safety profile, so the gap is moderate rather than severe.

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 description coverage is 100%, so all seven parameters are already documented, including the ACTIVE/PAUSED/ARCHIVED/DRAFT enum and the Unix-millisecond end date. The description only restates the field list at a high level, adding no syntax or format detail beyond the schema.

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

Purpose4/5

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

"Updates an existing LinkedIn campaign group" is a specific verb+resource, and it enumerates the mutable fields (status, budget, name, end date). The word "existing" implicitly separates it from create_campaign_group and delete_campaign_group, though no sibling is named explicitly.

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

Usage Guidelines2/5

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

The description says what can be changed but not when to prefer this tool over siblings like update_campaign or delete_campaign_group. No prerequisites, preconditions, or exclusions are stated, leaving usage entirely inferred.

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

update_creative_statusA
Destructive

Updates the intended status of a LinkedIn creative/ad. Can activate, pause, or archive creatives.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountIdYesThe LinkedIn Ad Account ID
creativeIdYesThe creative ID (numeric ID or full URN)
intendedStatusYesNew intended status for the creative

TDQS

A3.5/5.0
Behavior3/5

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

Annotations already declare readOnlyHint=false, destructiveHint=true, idempotentHint=false and openWorldHint=true, so the safety profile is carried by structured fields. The description adds only the set of target statuses, and never mentions that archiving may be irreversible or what constraints apply to status transitions.

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?

Two short sentences, front-loaded with the verb and resource, with zero filler. The allowed operations are stated immediately after the purpose.

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?

For a three-parameter mutation tool with full schema coverage and complete annotations, the description is serviceable but omits two things an agent would want: whether ARCHIVED is recoverable and whether all status transitions are legal. No output schema exists, so return behavior is also unaddressed.

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 description coverage is 100%, so accountId, creativeId and the intendedStatus enum are all documented in the schema. The description echoes the status concept but adds no format or validation detail (e.g., URN vs numeric ID handling) beyond the schema.

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

Purpose4/5

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

Names a specific verb (Updates) and resource (the intended status of a LinkedIn creative/ad), and enumerates the three effects: activate, pause, archive. No sibling performs creative-status changes, so differentiation is inherent, though the distinction between 'intended' and effective status is left unexplained.

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

Usage Guidelines3/5

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

The three listed actions imply the use cases (activate/pause/archive a creative), but there is no explicit when-to-use guidance, no prerequisites (permissions, whether status changes can be reverted), and no pointer to the create_creative sibling for related work.

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

upload_imageA
Destructive

Uploads an image file to LinkedIn for use in ads. Supports PNG, JPG, and GIF formats. Returns the image URN that can be used as the mediaId when creating inline ads. The image is uploaded in two steps: initialize upload (get URL + URN), then upload binary.

ParametersJSON Schema
NameRequiredDescriptionDefault
filePathYesAbsolute path to the image file on the local filesystem
accountIdNoOptional: LinkedIn Ad Account ID to register the image in the account media library
assetNameNoOptional: Name for the asset in the media library (required if accountId is provided)
organizationIdYesOwner of the image. Can be an organization URN (urn:li:organization:123) or a sponsored account URN (urn:li:sponsoredAccount:123). Use sponsoredAccount if you only have rw_ads permissions.

TDQS

A3.8/5.0
Behavior4/5

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

Annotations already declare destructiveHint=true, idempotentHint=false and openWorldHint=true, so the safety profile is partly covered. The description adds real context beyond that: supported formats (PNG/JPG/GIF) and the two-step initialize-then-upload-binary process. It omits auth/permission requirements and any warning that re-uploads are not idempotent.

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?

Four short sentences, front-loaded with purpose, then format support, then the return value, then the mechanics. Each sentence carries distinct information with no repetition or filler.

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

Completeness4/5

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

With no output schema, the description usefully states what is returned (the image URN and its role as mediaId). Annotations cover the mutation/idempotency profile. The only gap is permission/auth context, which is only hinted at indirectly in the schema.

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 description coverage is 100%, so all four parameters are already documented, including the organizationId URN options and the accountId/assetName pairing. The description adds no syntax or format detail beyond the schema, so the baseline of 3 applies.

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

Purpose4/5

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

States a specific verb and resource ('Uploads an image file to LinkedIn') and narrows the scope to ads, which separates it from read/analytics siblings like get_creative_performance. It also links the output to the downstream create-inline-ad workflow. It does not explicitly name a sibling tool, so it stops short of full differentiation.

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

Usage Guidelines3/5

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

The description implies usage ('for use in ads', 'used as the mediaId when creating inline ads') which gives useful context for when this tool fits. However, it states no explicit when-to-use/when-not conditions and never names an alternative or prerequisite, leaving the agent to infer the placement in the workflow.

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

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 28 tool updatesv1.1.0
    • First observedcompare_performance
    • First observedcreate_campaign
    • First observedcreate_campaign_group
    • First observedcreate_creative
    • First observedcreate_inline_ad
    • First observeddelete_campaign
    • First observeddelete_campaign_group
    • First observedget_account_details
    • First observedget_analytics
    • First observedget_audience_demographics
    • First observedget_audience_reach
    • First observedget_campaign_groups
    • First observedget_campaign_performance
    • First observedget_conversion_performance
    • First observedget_creative_performance
    • First observedget_daily_trends
    • First observedget_lead_gen_performance
    • First observedget_server_status
    • First observedlist_ad_accounts
    • First observedlist_campaigns
    • First observedlist_conversions
    • First observedlist_lead_forms
    • First observedlist_saved_audiences
    • First observedsearch_ad_library
    • First observedupdate_campaign
    • First observedupdate_campaign_group
    • First observedupdate_creative_status
    • First observedupload_image

TDQS

A3.6/5.0

Scored across 28 tools

Disambiguation4/5

Most tools have clearly distinct purposes (CRUD on campaigns/groups vs. specialized performance retrievals), and descriptions proactively distinguish look-alikes like get_campaign_performance vs. list_campaigns. However, the cluster of performance tools (get_campaign_performance, get_creative_performance, get_audience_demographics, get_conversion_performance, get_lead_gen_performance, get_daily_trends, get_analytics) has overlapping boundaries where an agent could easily pick the wrong one.

Naming Consistency5/5

Nearly every tool follows a clean snake_case verb_noun pattern (get_, list_, create_, update_, delete_, upload_, compare_, search_). The convention is applied consistently across all 28 tools with no camelCase or naming-style deviations.

Tool Count3/5

28 tools is a large surface that sits just above the comfortable 15-25 range, and several performance tools could plausibly be consolidated under get_analytics. The broad LinkedIn Ads domain (campaigns, creatives, audiences, conversions, lead gen, ad library) does justify most of them, so it's borderline rather than bloated.

Completeness4/5

Core lifecycle coverage is strong: full CRUD for campaigns and campaign groups, creative creation and status changes, plus extensive read/analytics for audiences, conversions, and lead gen. Minor gaps exist (no create/update for lead forms, audiences, or conversions; no creative delete beyond status), but agents can work around these.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    B
    maintenance
    A read-only MCP server for querying Meta Ads accounts through Meta's Marketing API. It provides tools to retrieve ad accounts, campaigns, ad sets, ads, and performance insights.
    -
  • A
    license
    A
    quality
    B
    maintenance
    Enables MCP clients to read and analyze Facebook (Meta) Ads marketing data, including campaigns, ad sets, ads, insights, and 190 metrics, with optional write tools that preview changes before applying.
    41
    235 npm
    Apache 2.0
  • A
    license
    B
    quality
    A
    maintenance
    Enables MCP clients to read and analyze TikTok advertising data, with optional preview-first write tools for managing campaigns, ad groups, and budgets.
    35
    68 npm
    Apache 2.0
  • A
    license
    A
    quality
    C
    maintenance
    Enables MCP clients to retrieve Google Ads performance data, run GAQL queries and keyword planner research, and create or manage search and P-MAX campaigns, budgets, keywords, and ad status. Includes safety defaults such as paused creation and read-only mode.
    16
    MIT