Mcp Agencyanalytics Python — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Mcp Agencyanalytics Python (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
A Python Model Context Protocol (MCP) server for AgencyAnalytics that enables AI assistants to manage campaigns, users, backlinks, keywords, and SEO rankings through natural language.
Works with: Claude Desktop, Claude Code, Cursor, Windsurf, Cline, Continue, Zed, and any MCP-compatible client.
Brought to you by [Osher Digital](https://osher.com.au) - Specialist AI consultants helping businesses harness the power of artificial intelligence.
Note: Only account Admin users can access the API key. API access is only available on Premier plans. The API key does not expire.
git clone https://github.com/osherai/mcp-agencyanalytics-python.git
cd mcp-agencyanalytics-pythonUsing uv (recommended):
uv venv && uv pip install -e .Or using pip:
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install -e .Copy the example environment file and add your API key:
cp .env.example .envEdit .env:
AGENCYANALYTICS_API_KEY=your_api_key_hereuv run agencyanalytics-mcpThe server should start without errors (it will wait for input on stdin).
This MCP server works with any MCP-compatible client. Below are setup instructions for popular clients.
Note: Replace /path/to/mcp-agencyanalytics-python with your actual installation path in all examples below.Add to your Claude Desktop configuration file:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"agencyanalytics": {
"command": "/path/to/mcp-agencyanalytics-python/.venv/bin/python",
"args": ["-m", "agencyanalytics_mcp.server"],
"cwd": "/path/to/mcp-agencyanalytics-python"
}
}
}Restart Claude Desktop (fully quit and reopen) for changes to take effect.
Add the server using the Claude Code CLI:
claude mcp add agencyanalytics \
-e AGENCYANALYTICS_API_KEY=your_api_key_here \
-- /path/to/mcp-agencyanalytics-python/.venv/bin/python -m agencyanalytics_mcp.serverOr add to your ~/.claude/settings.json:
{
"mcpServers": {
"agencyanalytics": {
"command": "/path/to/mcp-agencyanalytics-python/.venv/bin/python",
"args": ["-m", "agencyanalytics_mcp.server"],
"cwd": "/path/to/mcp-agencyanalytics-python"
}
}
}Add to your Cursor MCP configuration:
macOS: ~/.cursor/mcp.json Windows: %USERPROFILE%\.cursor\mcp.json
{
"mcpServers": {
"agencyanalytics": {
"command": "/path/to/mcp-agencyanalytics-python/.venv/bin/python",
"args": ["-m", "agencyanalytics_mcp.server"],
"cwd": "/path/to/mcp-agencyanalytics-python"
}
}
}Restart Cursor for changes to take effect.
Add to your Windsurf MCP configuration:
macOS: ~/.codeium/windsurf/mcp_config.json Windows: %USERPROFILE%\.codeium\windsurf\mcp_config.json
{
"mcpServers": {
"agencyanalytics": {
"command": "/path/to/mcp-agencyanalytics-python/.venv/bin/python",
"args": ["-m", "agencyanalytics_mcp.server"],
"cwd": "/path/to/mcp-agencyanalytics-python"
}
}
}Restart Windsurf for changes to take effect.
Add to your Cline MCP settings:
macOS: ~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json Windows: %APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json
{
"mcpServers": {
"agencyanalytics": {
"command": "/path/to/mcp-agencyanalytics-python/.venv/bin/python",
"args": ["-m", "agencyanalytics_mcp.server"],
"cwd": "/path/to/mcp-agencyanalytics-python"
}
}
}Add to your Continue configuration at ~/.continue/config.json:
{
"experimental": {
"modelContextProtocolServers": [
{
"transport": {
"type": "stdio",
"command": "/path/to/mcp-agencyanalytics-python/.venv/bin/python",
"args": ["-m", "agencyanalytics_mcp.server"],
"cwd": "/path/to/mcp-agencyanalytics-python"
}
}
]
}
}Add to your Zed settings at ~/.config/zed/settings.json:
{
"context_servers": {
"agencyanalytics": {
"command": {
"path": "/path/to/mcp-agencyanalytics-python/.venv/bin/python",
"args": ["-m", "agencyanalytics_mcp.server"]
},
"settings": {}
}
}
}Once configured, you can ask natural language questions about your AgencyAnalytics data:
Campaigns are called "Clients" in the AgencyAnalytics UI (renamed February 2025), but the API still uses the term "campaign".
#### list_campaigns
List campaigns with pagination.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
offset | integer | No | 0 | Pagination offset |
limit | integer | No | 50 | Max results per page |
#### get_campaign
Get a single campaign by ID.
| Parameter | Type | Required | Description |
|---|---|---|---|
campaign_id | integer | Yes | The campaign ID |
#### create_campaign
Create a new campaign.
| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | Yes | Website URL for the campaign |
company | string | Yes | Company/client name |
#### update_campaign
Update an existing campaign.
| Parameter | Type | Required | Description |
|---|---|---|---|
campaign_id | integer | Yes | The campaign ID to update |
url | string | No | New website URL |
company | string | No | New company name |
#### delete_campaign
Delete a campaign by ID.
| Parameter | Type | Required | Description |
|---|---|---|---|
campaign_id | integer | Yes | The campaign ID to delete |
#### list_users
List all platform users with pagination.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
offset | integer | No | 0 | Pagination offset |
limit | integer | No | 50 | Max results per page |
Returned fields: id, date_created, date_modified, email, username, first_name, last_name, role, campaign_access, status, account_id, campaign_id
#### get_user
Get a single user by ID.
| Parameter | Type | Required | Description |
|---|---|---|---|
user_id | integer | Yes | The user ID |
#### create_user
Create a new user.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
email | string | Yes | - | User email address |
first_name | string | Yes | - | First name |
last_name | string | Yes | - | Last name |
role | string | No | client | User role |
campaign_access | string | No | restricted | restricted or all |
campaign_id | integer | No | - | Campaign to grant access to |
#### update_user
Update an existing user.
| Parameter | Type | Required | Description |
|---|---|---|---|
user_id | integer | Yes | The user ID to update |
email | string | No | New email |
first_name | string | No | New first name |
last_name | string | No | New last name |
role | string | No | New role |
campaign_access | string | No | New access level |
#### delete_user
Delete a user by ID.
| Parameter | Type | Required | Description |
|---|---|---|---|
user_id | integer | Yes | The user ID to delete |
#### list_backlinks
List backlinks with optional campaign filtering.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
campaign_id | integer | No | - | Filter by campaign |
offset | integer | No | 0 | Pagination offset |
limit | integer | No | 50 | Max results per page |
Returned fields: id, title, status, date_created, date_published, url, link_type, target_anchor, target_url, notes, campaign_id, domain_authority, page_authority
#### create_backlink
Create a new backlink.
| Parameter | Type | Required | Description |
|---|---|---|---|
campaign_id | integer | Yes | Campaign to associate with |
title | string | Yes | Backlink title |
url | string | Yes | Page URL where link appears |
target_url | string | Yes | URL the link points to |
target_anchor | string | No | Anchor text |
link_type | string | No | Type of link |
status | string | No | Backlink status |
notes | string | No | Additional notes |
#### update_backlink
Update an existing backlink.
| Parameter | Type | Required | Description |
|---|---|---|---|
backlink_id | integer | Yes | The backlink ID to update |
title | string | No | New title |
url | string | No | New page URL |
target_url | string | No | New target URL |
target_anchor | string | No | New anchor text |
status | string | No | New status |
notes | string | No | New notes |
#### delete_backlink
Delete a backlink by ID.
| Parameter | Type | Required | Description |
|---|---|---|---|
backlink_id | integer | Yes | The backlink ID to delete |
#### list_keywords
List tracked keywords for a campaign. Keywords can only be queried one campaign at a time.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
campaign_id | integer | Yes | - | The campaign ID |
offset | integer | No | 0 | Pagination offset |
limit | integer | No | 50 | Max results per page |
#### get_keyword_rankings
Get keyword-level ranking data for a campaign. Returns positions across Google, Google Mobile, Google Places, and Bing with change tracking, SERP URLs, and monthly search volume.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
campaign_id | integer | Yes | - | The campaign ID |
offset | integer | No | 0 | Pagination offset |
limit | integer | No | 50 | Max results per page |
Returned fields include: keywordId, keywordPhrase, googleRanking, googleRankingChange, googleRankingUrl, googleMobileRanking, googleMobileRankingChange, googlePlacesRanking, bingRanking, bingRankingChange, localMonthlySearches, and more.
#### get_campaign_rankings
Get aggregate campaign ranking data over time. Returns historical trends with ranking distribution (positions 1-3, 4-10, 11-20, 21-50, 51+) and competitor data.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
campaign_id | integer | Yes | - | The campaign ID |
offset | integer | No | 0 | Pagination offset |
limit | integer | No | 50 | Max results per page |
#### create_competitor
Add a competitor URL to a campaign for rank comparison.
| Parameter | Type | Required | Description |
|---|---|---|---|
campaign_id | integer | Yes | Campaign to add competitor to |
url | string | Yes | Competitor website URL |
#### create_tag
Create an organizational tag.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Tag name |
#### create_login_grant
Generate a one-time SSO login URL for a user. The token allows logging into AgencyAnalytics without a password. Useful for embedding reports or building custom login pages.
| Parameter | Type | Required | Description |
|---|---|---|---|
user_id | integer | Yes | User to generate login token for |
Returns: token, login_url, time, user_id, origin_user_id
Unlike traditional REST APIs, the AgencyAnalytics API uses a single POST endpoint (https://apirequest.app/query) for all operations. Every request includes a JSON body specifying:
provider - Always "agency-analytics-v2"asset - The resource type (campaign, user, backlink, keyword, etc.)operation - The action (create, read, update, delete)fields - Which fields to return (for read operations)rows - Data for create/update/delete operationsoffset / limit - PaginationAuthentication uses HTTP Basic Auth with an empty username and your API key as the password.
| Asset | Create | Read | Update | Delete | Notes |
|---|---|---|---|---|---|
campaign | Yes | Yes | Yes | Yes | Called "Client" in UI since Feb 2025 |
user | Yes | Yes | Yes | Yes | Platform users with roles |
backlink | Yes | Yes | Yes | Yes | Custom backlink tracking |
keyword | - | Yes | - | - | Read only; requires campaign_id |
keyword-rankings | - | Yes | - | - | Read only; ranking feed data |
campaign-rankings | - | Yes | - | - | Read only; aggregate ranking feed |
competitor | Yes | - | - | - | Create only |
tag | Yes | - | - | - | Create only |
login-grant | Yes | - | - | - | Create only; SSO tokens |
The AgencyAnalytics API is designed for platform management and SEO data retrieval. It does not provide access to:
While AgencyAnalytics connects to 90+ integrations in its UI, none of that data is exposed through the API.
MCP Tool Call
-> AgencyAnalyticsClient._build_payload() (construct JSON body)
-> AgencyAnalyticsClient._request() (POST to https://apirequest.app/query)
-> Parse response { metadata, data }
-> format_response() (JSON with summary)
-> Return to MCP clientmcp-agencyanalytics-python/
├── pyproject.toml # Project config, dependencies, entry point
├── .env.example # Environment variables template
├── .gitignore # Python/IDE/OS ignores
├── README.md # This file
├── src/
│ └── agencyanalytics_mcp/
│ ├── __init__.py # Package version
│ ├── server.py # MCP server with 21 tool definitions
│ ├── config.py # Dataclass config loaded from environment
│ └── client.py # AgencyAnalytics API client wrapper
└── tests/
├── __init__.py
├── conftest.py # Shared pytest fixtures
├── test_config.py # Configuration tests
├── test_client.py # API client tests
└── test_server.py # MCP tool testshttpx.Client (sync) since the AgencyAnalytics API is a single endpoint with no parallelism benefit"ERROR: ..." strings rather than raising exceptions, following MCP conventions for graceful error handlingjson.dumps(data, indent=2, default=str) for consistent, readable output| Variable | Required | Default | Description |
|---|---|---|---|
AGENCYANALYTICS_API_KEY | Yes | - | Your AgencyAnalytics API key (Admin Settings > Company Details) |
AGENCYANALYTICS_BASE_URL | No | https://apirequest.app/query | API endpoint URL (override for testing) |
# Install dev dependencies
uv pip install -e ".[dev]"
# Run all tests
uv run pytest
# Run with verbose output
uv run pytest -v
# Run a specific test file
uv run pytest tests/test_client.py
# Run a specific test class
uv run pytest tests/test_server.py::TestListCampaignstests/
├── conftest.py # Fixtures: sample_config, sample_campaign, sample_user,
│ # sample_backlink, sample_keyword_ranking, sample_api_response
├── test_config.py # Tests for environment loading and validation (5 tests)
├── test_client.py # Tests for auth headers, payload building, HTTP requests,
│ # and client methods (15 tests)
└── test_server.py # Tests for each MCP tool with mocked client (14 tests)All tests use unittest.mock to mock the API client. No real API calls are made during testing.
Ensure your .env file exists in the project root and contains your API key:
AGENCYANALYTICS_API_KEY=your_api_key_hereThe AgencyAnalytics API may return errors for:
.venv directory cd /path/to/mcp-agencyanalytics-python
.venv/bin/python -m agencyanalytics_mcp.serverThe server should start without errors (it will wait for input on stdin)
Keywords can only be queried one campaign at a time. You must provide a campaign_id:
list_keywords(campaign_id=123)Contributions are welcome! Please feel free to submit a Pull Request.
git checkout -b feature/amazing-feature)git commit -m 'Add some amazing feature')git push origin feature/amazing-feature)This project is licensed under the MIT License - see the LICENSE file for details.
This project is maintained by Osher Digital, specialist AI consultants based in Australia. We help businesses integrate AI solutions to streamline operations and drive growth.
Need help with AI integration? Get in touch
This is an unofficial, community-maintained project. It is not affiliated with, officially maintained, or endorsed by AgencyAnalytics.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.