LinkedIn Ads MCP Toolkit
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@LinkedIn Ads MCP Toolkitcompare last month's campaigns by spend and conversions"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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.
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.
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
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-Aftersupport.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.
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 |
| Protocol handling and stdio transport |
Input validation | Ajv | Compile and apply tool JSON Schemas |
Configuration | Environment variables and | CLI configuration and |
OAuth callback | Native | Temporary loopback authorization listener |
Browser launch |
| Optional platform-independent authorization/dashboard opening |
Dashboard charts | Chart.js | Browser-side charts loaded from a versioned CDN URL |
Tests |
| 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 copydist/ 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 --> ACollection 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
Node.js 22 or newer and npm. Verify with
node --versionandnpm --version.A LinkedIn account that has access to the advertising account you want to query.
A LinkedIn developer application associated with the appropriate organization/company page.
Approved API products and OAuth scopes for the operations you intend to use.
An MCP client with local stdio support to invoke the tools interactively.
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 buildIf 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 .envWindows PowerShell:
Copy-Item .env.example .envEdit .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.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
Open the LinkedIn Developer Portal.
Create or select your application and associate the required company page.
In Products, request the advertising/reporting API access required by your use case. Approval and available products depend on LinkedIn's review process.
In Auth, add the exact redirect URI:
http://localhost:3000/callback.Copy the application's Client ID and Client Secret into
.env.Check the scopes granted to the application. The default requested scopes are:
Scope | Used for |
| Advertising-account and campaign management |
| Advertising analytics and reporting |
| Reading organization post content for creative enrichment |
| 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.
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 authThe 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.jsonOn Windows, ~ denotes your user profile directory. Set TOKEN_STORAGE_PATH to choose a different absolute path.
Verify the saved state:
npm run auth -- --statusAccess-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..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.jsonWindows:
%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 |
| Install Node.js, reopen the terminal, and verify PATH. Desktop applications may need a restart or an absolute executable path. |
| Run |
Missing OAuth credentials | Replace placeholders in |
Callback | Choose a free loopback port in |
Redirect URI mismatch | Match scheme, hostname, port, and path exactly between the application and |
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 |
HTTP 401 | Inspect local expiration and reauthenticate with |
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 |
JSON-RPC parsing error | Launch |
Token save fails | Check directory permissions and available disk space; use a writable absolute token directory. |
API version is rejected | Choose a supported |
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 checkIndividual commands:
Command | Purpose |
| Strict TypeScript validation without emitting files |
| Build and execute all test files |
| Build and run core/API/OAuth regression tests |
| Build and test MCP discovery, dispatch, read-only behavior, and a real stdio subprocess |
| Query the npm advisory database for the installed dependency tree |
| 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.mjsCovered 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.jsNormally 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:localThe 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-tis 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.jsonfile can interfere with atomic rename. Mount the containing directory instead.Do not bake
.envor token files into an image. The supplied.dockerignoreexcludes 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_PATHvalues. 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
Ask the client to run
get_server_statusto inspect local settings and token expiration.Run
list_ad_accountsand select an account ID.Start with a short reporting range.
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 |
| Local | Configuration, local token expiration, tool count; no network request |
| Read | Accessible accounts; status/type/test filtering |
| Read | Account currency, settings, status, and notifications |
| Read | Campaign KPIs and optional time buckets |
| Read | Creative KPIs, engagement breakdown, and optional video metrics |
| Read | Campaign groups, campaign counts, optional performance when a start date is supplied |
| Read | Performance by supported demographic pivot |
| Read | Member reach, frequency, and supported penetration metrics |
| Read | Saved/matched audience configuration and status |
| Read | Conversion-action metrics and attribution breakdown |
| Read | Conversion tracking definitions |
| Read | Leads, form opens, qualified leads, and acquisition costs |
| Read | Lead form configuration |
| Read | Period, campaign-set, or campaign-group-set comparisons |
| Read | Daily records, totals, weekday summaries, peak and lowest days |
| Read | Raw custom metrics, pivots, time granularity, and entity filters |
| Read | All matching campaigns, including drafts and zero-impression entities |
| Read | Keyword, advertiser, country, dates, targeting facets, and impression filters |
| Write | Create a campaign group; defaults to draft |
| Write | Change group name, status, budget, or end date |
| Write | Delete a resolved draft or request deletion for a resolved non-draft group |
| Write | Create a campaign with budget, objective, bidding, and targeting |
| Write | Update campaign settings and supported targeting/budget fields |
| Write | Delete a resolved draft or request deletion for a resolved non-draft campaign |
| Write | Sponsor an existing content reference |
| Write | Create an ad with inline post content |
| Write | Change intended creative status |
| 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=falseThen run:
npm run dashboardThe 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 performanceCustom 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.
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 / | Cost denominated in USD |
| 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
nullwhere 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_analyticsfor raw custom results, or request all required base metrics.get_daily_trendsreturns the rawrecordswhen 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 |
| Unset | Developer application client ID; required for login and refresh |
| Unset | Developer application secret; required for login and refresh |
|
| HTTP loopback URL with a registered host, port, and path; no credentials, query, or fragment |
|
| Space-separated approved OAuth scope names |
|
| Authorization flow deadline; integer 1–1,800,000 ms |
| Unset | Exact value |
|
| Token-file path; an absolute path is recommended |
|
| Marketing API version in |
|
| Exact |
|
| Per-attempt request/body deadline; integer 1–300,000 ms |
|
| Extra GET attempts; integer 0–10. |
|
| Initial retry delay; integer 1–60,000 ms |
|
| Maximum retry delay; integer from the base delay to 60,000 ms |
|
| Simultaneous API attempts per client; integer 1–50 |
|
| Collection request page size; integer 1–100 |
|
| Maximum pages per collection traversal; integer 1–10,000 |
|
| Local upload byte limit; integer 1–104,857,600. LinkedIn restrictions also apply |
| Unset | Positive numeric account ID; required for dashboard generation |
|
| Dashboard display name |
| 90 days before today | Dashboard start date in |
| Today | Dashboard end date in |
|
| Generated HTML output path |
| Unset | Exact value |
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 |
| Reuse usable local authentication or start OAuth when necessary |
| Start a new authorization flow even when a token is present |
| Show local authentication/expiration metadata without network access |
| Remove local tokens |
| 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
If you find this tool useful, consider leaving a star on GitHub or supporting the author directly.
Available Tools
28 toolscompare_performanceBRead-onlyIdempotent
Compares performance between two time periods, campaigns, or campaign groups. Calculates percentage changes and highlights significant differences. Essential for reporting on performance trends.
| Name | Required | Description | Default |
|---|---|---|---|
| periodA | Yes | First period or entity set for comparison | |
| periodB | Yes | Second period or entity set for comparison | |
| accountId | Yes | The LinkedIn Ad Account ID | |
| comparisonType | Yes | Type of comparison to make |
TDQS
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.
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.
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.
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.
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.
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_campaignBDestructive
Creates a new LinkedIn ad campaign within a campaign group. Requires targeting criteria, budget, and objective type.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Campaign name | |
| type | No | Campaign type. Default: SPONSORED_UPDATES | |
| status | No | Initial status. Default: DRAFT | |
| endDate | No | End date in YYYY-MM-DD format | |
| costType | No | Cost/bid type. Default: CPM | |
| accountId | Yes | The LinkedIn Ad Account ID | |
| startDate | No | Start date in YYYY-MM-DD format | |
| localeCountry | No | Target country code. Default: US | |
| objectiveType | Yes | Campaign objective type | |
| localeLanguage | No | Target language code. Default: en | |
| unitCostAmount | Yes | Bid amount per unit (e.g., "5.00") | |
| campaignGroupId | Yes | Campaign group ID to place this campaign under | |
| politicalIntent | No | Political advertising intent. Default: NOT_POLITICAL | |
| unitCostCurrency | No | Currency code. Default: USD | |
| creativeSelection | No | Creative rotation strategy. Default: OPTIMIZED | |
| dailyBudgetAmount | Yes | Daily budget amount (e.g., "50.00") | |
| targetingCriteria | Yes | Targeting criteria object with include/exclude conditions. Example: {"include":{"and":[{"or":{"urn:li:adTargetingFacet:locations":["urn:li:geo:103644278"]}}]}} | |
| totalBudgetAmount | No | Total/lifetime budget amount | |
| dailyBudgetCurrency | No | Currency code. Default: USD | |
| totalBudgetCurrency | No | Currency code. Default: USD | |
| offsiteDeliveryEnabled | No | Enable LinkedIn Audience Network delivery. Default: false | |
| audienceExpansionEnabled | No | Enable audience expansion. Default: false |
TDQS
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.
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.
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.
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.
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.
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_groupBDestructive
Creates a new LinkedIn campaign group for organizing campaigns. Campaign groups manage status, budget, and performance across multiple related campaigns.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Name of the campaign group (max 100 characters) | |
| status | No | Initial status. Default: DRAFT | |
| endDate | No | End date in YYYY-MM-DD format. Required if totalBudget is set | |
| accountId | Yes | The LinkedIn Ad Account ID | |
| startDate | Yes | Start date in YYYY-MM-DD format | |
| objectiveType | No | Campaign group objective type. Immutable once set. All campaigns in the group inherit this objective. | |
| dailyBudgetAmount | No | Daily budget amount (only with DYNAMIC budget optimization) | |
| totalBudgetAmount | No | Total budget amount (e.g., "5000.00") | |
| dailyBudgetCurrency | No | Daily budget currency code | |
| totalBudgetCurrency | No | Budget currency code (e.g., "USD"). Must match account currency |
TDQS
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.
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.
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.
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.
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.
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_creativeADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | Optional name for the creative | |
| accountId | Yes | The LinkedIn Ad Account ID | |
| campaignId | Yes | Campaign ID to create the creative under (numeric ID or full URN) | |
| leadgenFormId | No | Lead gen form ID (required for LEAD_GENERATION objective campaigns) | |
| intendedStatus | No | Initial status. Default: DRAFT | |
| contentReference | Yes | URN of the content to sponsor (e.g., urn:li:share:123 or urn:li:ugcPost:123) | |
| leadgenCallToActionLabel | No | Call to action label for lead gen campaigns |
TDQS
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.
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.
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.
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.
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.
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_adBDestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | Name for the creative (for internal reference in Campaign Manager) | |
| mediaId | No | URN 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. | |
| accountId | Yes | The LinkedIn Ad Account ID | |
| campaignId | Yes | Campaign ID to create the ad under (numeric ID or full URN) | |
| commentary | Yes | The ad text/copy that appears as the post commentary | |
| mediaTitle | No | Title for the media (displayed as headline for videos) | |
| leadgenFormId | No | Lead gen form ID (required for LEAD_GENERATION objective campaigns) | |
| intendedStatus | No | Initial status. Default: DRAFT | |
| landingPageUrl | No | Landing page URL for the ad. Required for WEBSITE_VISIT and WEBSITE_CONVERSIONS campaign objectives. | |
| organizationId | Yes | Organization/company ID that will be the ad author (numeric ID or full URN like urn:li:organization:123) | |
| callToActionLabel | No | Call to action button label | |
| leadgenCallToActionLabel | No | Call to action label for lead gen form button |
TDQS
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.
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.
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.
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.
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.
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_campaignADestructive
Deletes a LinkedIn campaign. Draft campaigns are deleted immediately. Non-draft campaigns are set to PENDING_DELETION status.
| Name | Required | Description | Default |
|---|---|---|---|
| accountId | Yes | The LinkedIn Ad Account ID | |
| campaignId | Yes | The campaign ID to delete |
TDQS
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.
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.
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.
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.
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.
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_groupADestructive
Deletes a LinkedIn campaign group. Draft groups are deleted immediately. Non-draft groups are set to PENDING_DELETION status.
| Name | Required | Description | Default |
|---|---|---|---|
| accountId | Yes | The LinkedIn Ad Account ID | |
| campaignGroupId | Yes | The campaign group ID to delete |
TDQS
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.
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.
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.
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.
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.
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_detailsARead-onlyIdempotent
Retrieves detailed information about a specific LinkedIn Ad Account including status, currency, notification settings, and linked organization.
| Name | Required | Description | Default |
|---|---|---|---|
| accountId | Yes | The LinkedIn Ad Account ID (numeric ID, not the URN) |
TDQS
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.
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.
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.
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.
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.
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_analyticsARead-onlyIdempotent
Query raw LinkedIn analytics with custom metrics, entity or demographic pivots, filters, and time granularity. Large metric selections are split and merged automatically.
| Name | Required | Description | Default |
|---|---|---|---|
| pivot | Yes | ||
| endDate | No | ||
| metrics | No | ||
| accountId | Yes | ||
| campaigns | No | ||
| creatives | No | ||
| startDate | Yes | ||
| campaignGroups | No | ||
| timeGranularity | No |
TDQS
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.
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.
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.
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.
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.
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_demographicsARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Top N results to return (max 100). Default: 25 | |
| metric | No | Primary metric to sort by. Default: impressions | |
| endDate | No | End date in YYYY-MM-DD format. Default: today | |
| accountId | Yes | The LinkedIn Ad Account ID | |
| startDate | Yes | Start date in YYYY-MM-DD format | |
| campaignIds | No | Filter by specific campaigns | |
| demographicType | Yes | The demographic dimension to analyze (MEMBER_COUNTRY and MEMBER_REGION are the newer versions) |
TDQS
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.
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.
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.
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.
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.
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_reachARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| endDate | No | End date in YYYY-MM-DD format. Default: today | |
| accountId | Yes | The LinkedIn Ad Account ID | |
| startDate | Yes | Start date in YYYY-MM-DD format (max 92 days range) | |
| campaignIds | No | Filter by specific campaigns | |
| campaignGroupIds | No | Filter by campaign groups |
TDQS
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.
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.
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.
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.
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.
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_groupsBRead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| status | No | Filter by status | |
| endDate | No | End date for performance (if included) | |
| accountId | Yes | The LinkedIn Ad Account ID | |
| startDate | No | Start date for performance (if included) | |
| includePerformance | No | Include performance metrics. Default: true |
TDQS
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.
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.
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.
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.
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.
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_performanceARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| endDate | No | End date in YYYY-MM-DD format. Default: today | |
| metrics | No | Optional LinkedIn metric names. Missing standard metrics are not available in the response; use get_analytics for raw custom metrics. | |
| accountId | Yes | The LinkedIn Ad Account ID | |
| startDate | Yes | Start date in YYYY-MM-DD format | |
| campaignIds | No | Specific campaign IDs to filter. If omitted, returns all campaigns. | |
| timeGranularity | No | Time granularity for the data. Default: ALL | |
| campaignGroupIds | No | Filter by campaign group IDs |
TDQS
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.
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.
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.
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.
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.
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_performanceBRead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| endDate | No | End date in YYYY-MM-DD format. Default: today | |
| accountId | Yes | The LinkedIn Ad Account ID | |
| startDate | Yes | Start date in YYYY-MM-DD format | |
| campaignIds | No | Filter by specific campaigns | |
| includePostView | No | Include view-through conversions. Default: true | |
| timeGranularity | No | Time granularity. Default: ALL |
TDQS
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.
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.
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.
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.
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.
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_performanceBRead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| endDate | No | End date in YYYY-MM-DD format. Default: today | |
| accountId | Yes | The LinkedIn Ad Account ID | |
| startDate | Yes | Start date in YYYY-MM-DD format | |
| campaignIds | No | Filter creatives by campaign IDs | |
| creativeIds | No | Specific creative IDs to fetch | |
| timeGranularity | No | Time granularity. Default: ALL | |
| includeVideoMetrics | No | Include video-specific metrics. Default: true |
TDQS
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.
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.
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.
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.
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.
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_daily_trendsARead-onlyIdempotent
Retrieves daily performance trends over a specified period. Returns time-series data for visualizing performance patterns, identifying anomalies, and understanding day-of-week effects.
| Name | Required | Description | Default |
|---|---|---|---|
| endDate | No | End date in YYYY-MM-DD format. Default: today | |
| metrics | No | Metrics to include. Default: impressions, clicks, costInUsd, conversions | |
| accountId | Yes | The LinkedIn Ad Account ID | |
| startDate | Yes | Start date in YYYY-MM-DD format | |
| campaignIds | No | Filter by specific campaigns | |
| entityLevel | No | Level of aggregation. Default: ACCOUNT |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare the full safety profile (readOnly, idempotent, non-destructive, open-world), so the bar is lower. The description usefully adds the return shape (time-series data), but says nothing about pagination, rate limits, or auth beyond what the schema implies.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences that front-load the core purpose before the analytic use cases. No filler, though the trailing purpose list is slightly redundant.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only reporting tool with full schema coverage and annotations covering safety, the description is adequate; it hints at the returned time-series nature. With no output schema, it could still say more about the structure of the trend series, but nothing critical to calling it is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 every parameter including formats and defaults. The description adds no syntax, defaults, or units beyond what the schema provides, making the baseline 3 appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb+resource combination ('Retrieves daily performance trends') and pins the granularity to daily time-series data. It is clear what the tool does, though it never names the sibling report tools (get_campaign_performance, get_analytics) it should be chosen over.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied via the stated goals (visualizing patterns, spotting anomalies, day-of-week effects). There is no explicit when-to-use versus alternatives such as get_campaign_performance or compare_performance, nor any stated exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_lead_gen_performanceBRead-onlyIdempotent
Retrieves lead generation form performance including form submissions, qualified leads, and cost per lead. Essential for B2B marketers running lead gen campaigns.
| Name | Required | Description | Default |
|---|---|---|---|
| endDate | No | End date in YYYY-MM-DD format. Default: today | |
| accountId | Yes | The LinkedIn Ad Account ID | |
| startDate | Yes | Start date in YYYY-MM-DD format | |
| campaignIds | No | Filter by specific campaigns | |
| timeGranularity | No | Time granularity. Default: ALL |
TDQS
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.
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.
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.
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.
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.
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_statusARead-only
Returns local configuration and token expiration status without exposing secrets or making API calls.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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_accountsARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| type | No | Filter by account type. | |
| status | No | Filter by account status. If not specified, returns all statuses. | |
| includeTest | No | Include test accounts. Default: false |
TDQS
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.
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.
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.
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.
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.
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_campaignsARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| status | No | Filter by campaign status. If omitted, returns all statuses. | |
| accountId | Yes | The LinkedIn Ad Account ID | |
| campaignGroupIds | No | Filter by campaign group IDs (numeric IDs) |
TDQS
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.
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.
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.
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.
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.
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_conversionsBRead-onlyIdempotent
Lists all conversion tracking rules configured for an account. Shows conversion names, types, attribution windows, and enabled status.
| Name | Required | Description | Default |
|---|---|---|---|
| accountId | Yes | The LinkedIn Ad Account ID | |
| enabledOnly | No | Only show enabled conversions. Default: false |
TDQS
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.
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.
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.
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.
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.
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_formsBRead-onlyIdempotent
Lists all lead generation forms configured for an account with their questions and settings. Helps understand what forms are available and their configuration.
| Name | Required | Description | Default |
|---|---|---|---|
| status | No | Filter by status | |
| accountId | Yes | The LinkedIn Ad Account ID | |
| includeQuestions | No | Include form questions. Default: true |
TDQS
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.
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.
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.
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.
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.
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_audiencesARead-onlyIdempotent
Lists saved/matched audiences available in the account for targeting. Shows audience names, sizes, and statuses to help plan campaign targeting.
| Name | Required | Description | Default |
|---|---|---|---|
| status | No | Filter by status | |
| accountId | Yes | The LinkedIn Ad Account ID | |
| audienceType | No | Filter by audience type |
TDQS
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.
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.
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.
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.
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.
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_libraryARead-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).
| Name | Required | Description | Default |
|---|---|---|---|
| count | No | Number of results per page (max 25). Default: 25 | |
| start | No | Pagination offset. Default: 0 | |
| endDate | No | End date for when ads were served (YYYY-MM-DD format, exclusive). Default: 1 day ago. | |
| keyword | No | Keywords to search in ad content. Multiple keywords separated by space are treated as AND logic. | |
| countries | No | Two-letter country codes to filter ads by served country (e.g., "us", "gb", "de"). | |
| payerName | No | Entity who paid for sponsoring the ad. | |
| startDate | No | Start date for when ads were served (YYYY-MM-DD format). Default: 2 days ago. | |
| advertiser | No | Advertiser name to search against. | |
| sortByField | No | Field to sort by. Default: CREATED_TIME | |
| sortByOrder | No | Sort order. Default: DESCENDING | |
| totalImpressionsRangeTo | No | Upper bound of total impressions range (0 = 1M+). | |
| totalImpressionsRangeFrom | No | Lower bound of total impressions range. | |
| excludedTargetingFacetCategories | No | Filter ads that exclude these targeting facet categories. | |
| includedTargetingFacetCategories | No | Filter ads that include these targeting facet categories. |
TDQS
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.
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.
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.
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.
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.
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_campaignADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | New campaign name | |
| status | No | New status. Common transitions: DRAFT->ACTIVE, ACTIVE->PAUSED, PAUSED->ACTIVE, ACTIVE->ARCHIVED | |
| endDate | No | New end date as Unix timestamp in milliseconds | |
| accountId | Yes | The LinkedIn Ad Account ID | |
| campaignId | Yes | The campaign ID to update | |
| unitCostAmount | No | New bid amount per unit | |
| unitCostCurrency | No | Bid currency code | |
| dailyBudgetAmount | No | New daily budget amount | |
| targetingCriteria | No | New targeting criteria object. Replaces existing targeting entirely. | |
| totalBudgetAmount | No | New total/lifetime budget amount | |
| dailyBudgetCurrency | No | Budget currency code | |
| totalBudgetCurrency | No | Budget currency code | |
| offsiteDeliveryEnabled | No | Enable/disable LinkedIn Audience Network | |
| optimizationTargetType | No | Optimization target type | |
| audienceExpansionEnabled | No | Enable/disable audience expansion |
TDQS
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.
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.
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.
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.
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.
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_groupBDestructive
Updates an existing LinkedIn campaign group. Can change status (ACTIVE/PAUSED/ARCHIVED/DRAFT), budget, name, or end date.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | New name for the campaign group | |
| status | No | New status. ACTIVE->PAUSED, ACTIVE->ARCHIVED, DRAFT->ACTIVE are common transitions. | |
| endDate | No | New end date as Unix timestamp in milliseconds | |
| accountId | Yes | The LinkedIn Ad Account ID | |
| campaignGroupId | Yes | The campaign group ID to update | |
| totalBudgetAmount | No | New total budget amount | |
| totalBudgetCurrency | No | Budget currency code |
TDQS
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.
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.
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.
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.
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.
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_statusADestructive
Updates the intended status of a LinkedIn creative/ad. Can activate, pause, or archive creatives.
| Name | Required | Description | Default |
|---|---|---|---|
| accountId | Yes | The LinkedIn Ad Account ID | |
| creativeId | Yes | The creative ID (numeric ID or full URN) | |
| intendedStatus | Yes | New intended status for the creative |
TDQS
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.
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.
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.
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.
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.
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_imageADestructive
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.
| Name | Required | Description | Default |
|---|---|---|---|
| filePath | Yes | Absolute path to the image file on the local filesystem | |
| accountId | No | Optional: LinkedIn Ad Account ID to register the image in the account media library | |
| assetName | No | Optional: Name for the asset in the media library (required if accountId is provided) | |
| organizationId | Yes | Owner 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
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.
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.
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.
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.
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.
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.
28 tool updates
v1.1.0- First observed
compare_performance - First observed
create_campaign - First observed
create_campaign_group - First observed
create_creative - First observed
create_inline_ad - First observed
delete_campaign - First observed
delete_campaign_group - First observed
get_account_details - First observed
get_analytics - First observed
get_audience_demographics - First observed
get_audience_reach - First observed
get_campaign_groups - First observed
get_campaign_performance - First observed
get_conversion_performance - First observed
get_creative_performance - First observed
get_daily_trends - First observed
get_lead_gen_performance - First observed
get_server_status - First observed
list_ad_accounts - First observed
list_campaigns - First observed
list_conversions - First observed
list_lead_forms - First observed
list_saved_audiences - First observed
search_ad_library - First observed
update_campaign - First observed
update_campaign_group - First observed
update_creative_status - First observed
upload_image
TDQS
Scored across 28 tools
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.
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.
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.
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
Related MCP Connectors
Meta Ads MCP: Facebook and Instagram reports, campaigns, creatives, audiences. Approval on writes.
MCP server for querying and analyzing data from ad platforms, analytics tools, and spreadsheets
Google Ads MCP: reports, search terms, negatives, budgets, campaigns. Approval on every write.
Read-only MCP access to your DEXUN AdWhiz account: ad accounts, AI recommendations, savings.
Related MCP Servers
- FlicenseNot gradedqualityBmaintenanceA 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.-
- AlicenseAqualityBmaintenanceEnables 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.41235 npmApache 2.0
- AlicenseBqualityAmaintenanceEnables MCP clients to read and analyze TikTok advertising data, with optional preview-first write tools for managing campaigns, ad groups, and budgets.3568 npmApache 2.0
- AlicenseAqualityCmaintenanceEnables 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.16MIT