MCP connector
Drive Fenn from Claude or ChatGPT.
Fenn ships a hosted MCP server with 15 tools. Add it as a custom connector and the whole product is available in conversation — ask “how visible is mydomain.com?” and your agent calls the tools and answers. It is enabled on every plan, including the free one, and read tools have no side effects at all.
What the connector does.
It exposes your visibility data and your fix pipeline as MCP tools, so an agent can read, reason and act without you opening a dashboard. Three things follow from that.
Ask in plain language
You never name a tool. The agent picks between visibility_report, keyword_gaps and the rest based on what you asked.
Reason across your data
Because read tools are free of side effects, an agent can chain a dozen calls to answer one question — engine by engine, prompt by prompt — without asking permission each time.
Act, behind a gate
It can draft the fix as a real diff. It cannot open the PR on its own: gated tools return pending_approval and wait for a person.
Tool surface
15 tools, three bands.
The band tells you what a tool can do to you before you call it. This is the whole permission model — there is nothing else to configure, and no setting that promotes a tool to a lower band.
10 read tools
No side effects. Your agent can call these freely, in a loop, without asking you first.
| Tool | What it does | Arguments | Returns |
|---|---|---|---|
visibility_report | The whole picture for a domain: Visibility Score, Share of Voice, Average Position and Sentiment, per engine, over a date range. | domain, range?, engines? | Scores per engine + trend series |
prompt_detail | Everything about one tracked prompt: the raw answer each engine gave, who got cited, where you appeared, and how that moved. | prompt_id, engines?, range? | Per-engine answers, citations, position, sentiment |
list_prompts | Every prompt currently tracked for a domain, with its latest result. | domain, status? | Prompt list with last-run summary |
rank_check | Classic rank for a keyword alongside answer-engine presence, so you can see where the two disagree. | domain, keyword, locale? | Rank, AI Overview presence, citing engines |
keyword_gaps | Prompts and keywords where competitors are cited and you are not — ranked by how often the question gets asked. | domain, competitors?, limit? | Gap list with competitor citations |
audit_issues | Technical and content issues that suppress citation: thin answers, missing schema, throat-clearing intros, orphaned pages, slow LCP. | domain, severity?, category? | Issue list with severity, URL, evidence |
backlinks | Referring domains and the pages engines actually quote when they answer about you. | domain, limit? | Referring domains + cited-source overlap |
suggest_fixes | For a given issue or gap, the concrete changes that would most likely earn the citation. Read-only — proposes, does not write. | issue_id | gap_id, max_suggestions? | Ranked suggestions with rationale |
get_fix_prompt | The exact prompt to hand your own coding agent if you would rather implement the fix yourself, in your own repo, on your own terms. | fix_id | Ready-to-paste prompt + file targets |
list_fix_tasks | The state of every fix: drafted, pending approval, PR open, merged, or measured. | domain, status? | Fix task list with PR links and status |
3 draft tools
Changes what is tracked, or produces a draft artefact. Reversible from the dashboard, and every call is written to the audit log.
| Tool | What it does | Arguments | Returns |
|---|---|---|---|
track_prompt | Start tracking a new prompt. Counts against the prompt allowance on your plan. | domain, prompt, engines? | prompt_id + first scheduled run |
track_keyword | Add a keyword to rank tracking. | domain, keyword, locale? | keyword_id + first scheduled check |
draft_fix | Turn a suggestion into a real diff against your connected repo. Produces a branch and a patch — it does not push, open, or merge anything. | fix_id | issue_id, repo, base_branch? | fix_task_id + unified diff preview |
2 gated tools
Never executes on call. Returns status: "pending_approval" plus an approval URL. A human approves in the dashboard before anything leaves the sandbox.
| Tool | What it does | Arguments | Returns |
|---|---|---|---|
execute_fix | Open the pull request for a drafted fix. Returns pending_approval and stops; a human approves in the dashboard, and only then is the PR opened. | fix_task_id | status: "pending_approval" + approval_url |
request_publish | Publish a report or a content change to a connected CMS. Same rule: returns pending_approval and waits for a human. | target, payload_ref | status: "pending_approval" + approval_url |
Gated tools never execute on call.
Calling execute_fix or request_publish does not open a pull request or publish anything. It creates an approval request and returns:
{
"status": "pending_approval",
"approval_url": "https://app.fenn.sh/approvals/apr_7c31",
"expires_in": "72h"
}A human with a login approves it in the dashboard, and only then does the pull request open. There is no configuration flag that removes this step, for any plan, including agency.
Setup
Copy, paste, done.
Create an API key in your dashboard under Settings → API keys, then add the connector. Keys are scoped to a workspace; on the agency plan you can issue one key per client so each client’s agent sees only their data.
Settings → Connectors → Add custom connector
Name: Fenn
URL: https://api.fenn.sh/api/aeo-engine/mcp
Auth: Bearer <your API key>
Then, in any chat:
"Using Fenn, how visible is mydomain.com this week?"Settings → Connectors → Create → MCP server
Name: Fenn
Server URL: https://api.fenn.sh/api/aeo-engine/mcp
Auth: API key (paste the key from your dashboard)
Enable the connector in the composer, then ask your question
in plain language. No tool names required.{
"mcpServers": {
"fenn": {
"type": "http",
"url": "https://api.fenn.sh/api/aeo-engine/mcp",
"headers": { "Authorization": "Bearer $FENN_API_KEY" }
}
}
}Full API-key documentation, scopes and rotation policy live in the dashboard at https://app.fenn.sh/docs/api-keys. Never paste a key into a shared chat; issue a second key instead and revoke it when you are done.
Worked example
“How visible is mydomain.com?”
One question, in a normal chat. The agent chooses the tools; you never type a tool name. Here is the full round trip, from the question through to a drafted fix waiting on a human.
you ▸ How visible is mydomain.com?
agent ▸ visibility_report({ domain: "mydomain.com", range: "7d" })
Visibility Score 42.4% (+3.1 pts vs previous 7d)
Share of Voice 18.1% (+2.4 pts)
Average Position 2.8 (was 3.4)
Sentiment 71% positive / 24% neutral / 5% negative
Per engine
ChatGPT 62% Perplexity 54%
Claude 47% AI Overviews 38%
Gemini 31% Grok 22%
agent ▸ keyword_gaps({ domain: "mydomain.com", limit: 3 })
3 prompts where competitors are cited and you are not.
Top one: "best <category> for small teams" — cited on 5 of 6
engines, you appear on 0.
agent ▸ Grok is your weakest engine at 22%, and the biggest single
gap is one prompt cited on five engines where you are absent.
Want me to run suggest_fixes on it?
you ▸ yes, and draft the fix
agent ▸ suggest_fixes({ gap_id: "gap_8f21" })
draft_fix({ fix_id: "fix_2c90", repo: "acme/www" })
Drafted. Branch aeo/answer-first-pricing-page, 2 files changed.
Diff preview above. Nothing has been pushed.
To open the pull request, call execute_fix — it will return
pending_approval and wait for a human to approve at
https://app.fenn.sh/approvals.Note where it stops. The agent drafted a real diff and then handed control back. That is the boundary between the fix loop and anything actually changing in your repository.
The MCP server is on the free plan too.
One domain, five prompts, ten keywords, all ten read tools. Enough to point your agent at your own domain and find out what the engines say about you.