bing-ads-mcp
by xar
README.md
# bing-ads-mcp
An MCP (Model Context Protocol) server that lets AI assistants manage your Microsoft Advertising (Bing Ads) campaigns. Built with [Bun](https://bun.sh) and the [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk).
Works with Claude Code, Claude Desktop, Cursor, and any MCP-compatible client.
## Why
The Microsoft Advertising web interface is painful for bulk operations. This MCP server gives AI assistants direct access to your Bing Ads account through clean, typed tools -- so you can manage campaigns, ad groups, keywords, and extensions conversationally.
## Features
- **24 tools** covering campaigns, ad groups, ads, keywords, negative keywords, and ad extensions
- **OAuth2 with auto-refresh** -- built-in browser-based setup flow, tokens auto-refresh
- **Auto-discovers Customer ID** -- no need to hunt for it in the UI
- **Streamable HTTP transport** -- works as a remote MCP server
- **Zero config** -- just add credentials and go
## Quick Start
### Prerequisites
- [Bun](https://bun.sh) v1.0+
- A Microsoft Advertising account
- An Azure App Registration ([setup guide below](#azure-app-registration))
### Install & Run
```bash
git clone https://github.com/user/bing-ads-mcp.git
cd bing-ads-mcp
bun install
```
Create a `.env` file with your credentials:
```env
BING_CLIENT_ID=your-azure-app-client-id
BING_CLIENT_SECRET=your-azure-app-client-secret
BING_DEVELOPER_TOKEN=your-developer-token
BING_ACCOUNT_ID=your-account-id
```
Authenticate via OAuth (opens browser):
```bash
bun run setup
```
Start the server:
```bash
bun run start
```
The MCP server runs at `http://localhost:3100/mcp`.
### Connect to Your AI Client
**Claude Code** -- add to `~/.claude.json` or project `.mcp.json`:
```json
{
"mcpServers": {
"bing-ads": {
"url": "http://localhost:3100/mcp"
}
}
}
```
**Claude Desktop** -- add to your MCP settings:
```json
{
"mcpServers": {
"bing-ads": {
"url": "http://localhost:3100/mcp"
}
}
}
```
## Available Tools
### Campaigns
| Tool | Description |
|------|-------------|
| `list_campaigns` | List all campaigns, optionally filter by type |
| `get_campaign` | Get detailed campaign info by IDs |
| `create_campaign` | Create campaigns with name, budget, type, status |
| `update_campaign` | Update campaign name, status, budget |
| `delete_campaigns` | Delete campaigns by ID |
### Ad Groups
| Tool | Description |
|------|-------------|
| `list_ad_groups` | List ad groups in a campaign |
| `create_ad_group` | Create ad groups with CPC bid, network targeting |
| `update_ad_group` | Update ad group name, status, bid |
| `delete_ad_groups` | Delete ad groups by ID |
### Ads
| Tool | Description |
|------|-------------|
| `list_ads` | List ads in an ad group |
| `create_responsive_search_ad` | Create RSAs with headlines, descriptions, URLs |
| `update_ad` | Update ad content, status, URLs |
| `delete_ads` | Delete ads by ID |
### Keywords
| Tool | Description |
|------|-------------|
| `list_keywords` | List keywords in an ad group |
| `create_keywords` | Add keywords with match type and bid |
| `update_keywords` | Update keyword text, bid, match type, status |
| `delete_keywords` | Delete keywords by ID |
### Negative Keywords
| Tool | Description |
|------|-------------|
| `list_negative_keywords` | List negatives for a campaign or ad group |
| `add_negative_keywords` | Add negative keywords (Exact or Phrase match) |
| `delete_negative_keywords` | Delete negative keywords by ID |
### Ad Extensions
| Tool | Description |
|------|-------------|
| `list_ad_extensions` | List extensions by type |
| `get_ad_extensions` | Get extension details by IDs |
| `create_sitelink_extension` | Create sitelink extensions |
| `create_callout_extension` | Create callout extensions |
| `create_structured_snippet_extension` | Create structured snippets |
| `associate_ad_extensions` | Link extensions to campaigns or ad groups |
| `delete_ad_extensions` | Delete extensions by ID |
## Setup Guide
You need 4 values for your `.env` file. Here's how to get each one step by step.
### Step 1: Create an Azure App Registration (`BING_CLIENT_ID` + `BING_CLIENT_SECRET`)
This creates the OAuth app that lets the MCP server authenticate with Microsoft on your behalf.
1. Go to [Azure Portal > App registrations](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade)
- Sign in with the same Microsoft account you use for Microsoft Advertising
2. Click **+ New registration** at the top
3. Fill in the form:
- **Name**: `Bing Ads MCP` (or anything you like)
- **Supported account types**: Select **"Accounts in any organizational directory (Any Microsoft Entra ID tenant - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)"**
- **Redirect URI**: Select **Web** from the dropdown, then enter: `http://localhost:3456/callback`
4. Click **Register**
5. You'll land on the app's **Overview** page. Copy the **Application (client) ID** -- this is your `BING_CLIENT_ID`
6. In the left sidebar, click **Certificates & secrets**
7. Click **+ New client secret**
- **Description**: `MCP server` (or anything)
- **Expires**: Pick an expiry (e.g. 24 months)
8. Click **Add**
9. Copy the **Value** column immediately (it won't be shown again) -- this is your `BING_CLIENT_SECRET`
> **Important**: You do NOT need to add any API permissions in the Azure Portal. The `msads.manage` scope is requested automatically at runtime during the OAuth consent flow.
### Step 2: Get Your Developer Token (`BING_DEVELOPER_TOKEN`)
The developer token authorizes your app to use the Bing Ads API.
1. Go to [Microsoft Advertising](https://ads.microsoft.com) and sign in
2. Navigate to **Tools** > **Developer Portal** (or go directly to [developers.ads.microsoft.com](https://developers.ads.microsoft.com/Account))
3. If you don't have a developer token yet, request one -- for personal use it's typically approved instantly
4. Go to the **Account** tab
5. Copy your **Developer Token** -- this is your `BING_DEVELOPER_TOKEN`
> **Note**: For sandbox/testing, you can use the universal sandbox token `BBD37VB98`.
### Step 3: Get Your Account ID (`BING_ACCOUNT_ID`)
1. Go to [Microsoft Advertising](https://ads.microsoft.com) and sign in
2. Click **Settings** (gear icon) in the left sidebar
3. Click **Account settings**
4. Find **Account ID** (a numeric value like `138954571`) -- this is your `BING_ACCOUNT_ID`
- This is NOT the "Account number" (which looks like `F107MTKS`) -- you need the numeric ID
### Step 4: Create Your `.env` File
Create a `.env` file in the project root with your 4 values:
```env
BING_CLIENT_ID=your-client-id-from-step-1
BING_CLIENT_SECRET=your-client-secret-from-step-1
BING_DEVELOPER_TOKEN=your-developer-token-from-step-2
BING_ACCOUNT_ID=your-account-id-from-step-3
```
You can leave `BING_CUSTOMER_ID` and `BING_REFRESH_TOKEN` empty -- both are filled automatically:
- **Customer ID** is auto-discovered via the API on first request
- **Refresh Token** is saved when you run `bun run setup`
### Step 5: Authenticate
Run the OAuth setup to get your refresh token:
```bash
bun run setup
```
This opens your browser to Microsoft's login page. Sign in, grant consent, and the refresh token is automatically saved to your `.env` file. You'll see "Setup complete!" in the terminal when done.
### Step 6: Verify
Test that everything works:
```bash
bun run test-api
```
This will authenticate, auto-discover your Customer ID, and list your campaigns. If you see your campaigns printed, you're all set.
## Commands
| Command | Description |
|---------|-------------|
| `bun run start` | Start the MCP server on port 3100 |
| `bun run setup` | Run OAuth flow (opens browser for consent) |
| `bun run refresh` | Re-authenticate when token expires |
| `bun run test-api` | Test API connectivity by listing campaigns |
| `bun run inspect` | Open MCP Inspector for interactive testing |
## Architecture
```
index.ts Entry point -- MCP server + Streamable HTTP via Bun.serve()
setup.ts OAuth setup CLI -- opens browser, saves tokens to .env
test-api.ts API connection test script
src/
auth.ts OAuth2 token management (auto-refresh, caching, persistence)
client.ts Bing Ads REST API client (auto-discovers Customer ID)
types.ts TypeScript types for API objects
tools/
campaigns.ts Campaign CRUD
adgroups.ts Ad Group CRUD
ads.ts Ad CRUD (Responsive Search Ads)
keywords.ts Keyword CRUD
negative-keywords.ts Negative keyword management
extensions.ts Ad extensions (sitelinks, callouts, snippets, associations)
```
## How It Works
1. **Auth**: OAuth2 authorization code flow with Microsoft identity platform. Refresh tokens are stored in `.env` and auto-refreshed on expiry.
2. **API**: Thin wrapper over the [Bing Ads REST API v13](https://learn.microsoft.com/en-us/advertising/campaign-management-service/campaign-management-service-reference?view=bingads-13) using native `fetch()`.
3. **Transport**: Streamable HTTP transport via `Bun.serve()` -- each MCP client session gets its own server instance with full state isolation.
4. **Customer ID**: Automatically discovered via the `GetUser` API on first request and cached to `.env`.
## Troubleshooting
| Problem | Solution |
|---------|----------|
| "No refresh token available" | Run `bun run setup` to authenticate |
| Token expired / 401 errors | Run `bun run refresh` to re-authenticate |
| Port 3100 in use | `lsof -ti:3100 \| xargs kill -9` |
| Customer ID errors | Auto-discovered on first call. Check Microsoft Advertising Settings if issues persist |
| API errors with TrackingId | Use the TrackingId when contacting Microsoft support |
## Tech Stack
- **[Bun](https://bun.sh)** -- runtime, HTTP server, env loading, file I/O
- **[MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)** -- Model Context Protocol server
- **[Zod](https://zod.dev)** -- tool parameter validation
- **[Microsoft Advertising REST API v13](https://learn.microsoft.com/en-us/advertising/guides/?view=bingads-13)** -- campaign management
## License
MIT
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues