Contentstack Canoe
Bring your Contentstack Canoe AI visibility data into any agent. See how ChatGPT, Gemini, Perplexity and Claude name and cite your brand, who they recommend instead and what to fix first, then start a fresh run. Nine tools. Free plan to start, MCP on Growth. OAuth sign-in is coming soon.
Servidor MCP alojado
npx add-mcp 'https://canoe.contentstack.com/api/mcp'Se instala en Claude Code, Codex, Cursor y más
Documentación
Server URL
https://canoe.contentstack.com/api/mcp
Transport
Streamable HTTP, stateless. Tools only.
Auth
A Read & write API key, sent as Authorization: Bearer <key>
Config name
contentstack-canoe. Use it as the server key in your client config so skills and examples match.
One loop, nine tools
Most jobs need one call: get_geo_latest_report returns scores, competitors, recommendations and run deltas, already interpreted.
- Discover Find the brand and monitor.
list_projectslist_geo_tests - Assess Read the report, then history, answers and sources.
get_geo_latest_reportget_geo_test_historyget_geo_latest_citations - Fix Your agent drafts the change in your CMS or repo. Contentstack MCP or your own tools
- Re-measure Start a run and compare it with the last one.
trigger_geo_test_runget_geo_latest_report
Each run feeds the next assessment. run_deltas shows what moved.
The fix happens in your systems. Contentstack Canoe never writes to your site.
Names in the tools
A project is a brand. A geo test (test_id) is a monitor.
Quickstart
Five minutes with Claude Code. Other clients are under Connect your client.
- Check your plan MCP comes with Growth and Enterprise. Compare plans
- Create a Read & write key Open Settings, then API keys. Name it after the job, pick Read & write and copy it. It's shown once.
- Put the key in an environment variable
export CONTENTSTACK_CANOE_API_KEY="sk_your_key_here" - Add the server
claude mcp add --transport http \ contentstack-canoe \ https://canoe.contentstack.com/api/mcp \ --scope user \ --header \ "Authorization: Bearer $CONTENTSTACK_CANOE_API_KEY"claude mcp listshould showcontentstack-canoeas connected. - Ask your first question
Reading data never spends prompts.List my Contentstack Canoe brands and their monitors. Which monitor lost the most visibility since its last run?
API keys
Every request sends a key in the header. Keys work on Growth and Enterprise.
Format
sk_..., sent as Authorization: Bearer sk_... or x-api-key: sk_...
Identity
Acts as its creator, in one workspace. If they lose access, so does the key.
Limit
Up to 3 active keys per person in each workspace.
Lifetime
No expiry. Revoke one and it stops at once.
Requests
10,000 a month on Growth, shared with the API. Over MCP, only tool calls count, including failed ones.
Prompts
Only trigger_geo_test_run spends prompts.
Why Read & write, even to read
MCP requests are POSTs, and Read only keys can only make GETs, so they're refused. Read only MCP access is coming soon. Until then, block the write tool.
Connect your client
Every config uses contentstack-canoe as the server key and keeps the key out of files you commit.
Available claude mcp add or .mcp.json with a header
The Quickstart command adds it for all your projects. To share it through git, commit .mcp.json at the repo root instead. Claude Code fills in ${CONTENTSTACK_CANOE_API_KEY} from each person's environment.
{
"mcpServers": {
"contentstack-canoe": {
"type": "http",
"url": "https://canoe.contentstack.com/api/mcp",
"headers": {
"Authorization": "Bearer ${CONTENTSTACK_CANOE_API_KEY}"
}
}
}
}
Check with claude mcp list or /mcp. Tools show as mcp__contentstack-canoe__list_projects and so on.
Available mcp.json with url and headers
Use ~/.cursor/mcp.json for every project or .cursor/mcp.json for one repo.
{
"mcpServers": {
"contentstack-canoe": {
"url": "https://canoe.contentstack.com/api/mcp",
"headers": {
"Authorization": "Bearer ${env:CONTENTSTACK_CANOE_API_KEY}"
}
}
}
}
Cursor reads ${env:...} from the environment it was launched from.
AvailableCopilot agent mode, .vscode/mcp.json with servers and a password input
For Copilot agent mode, save this as .vscode/mcp.json. VS Code asks for the key once and stores it. For every workspace, add the block with MCP: Open User Configuration.
{
"inputs": [
{
"type": "promptString",
"id": "contentstack-canoe-key",
"description": "Contentstack Canoe API key",
"password": true
}
],
"servers": {
"contentstack-canoe": {
"type": "http",
"url": "https://canoe.contentstack.com/api/mcp",
"headers": {
"Authorization": "Bearer ${input:contentstack-canoe-key}"
}
}
}
}
It's servers, not mcpServers, so a config pasted from Cursor won't work.
AvailableWindsurf and Devin Desktop, mcp_config.json with serverUrl and headers
Windsurf now lives under Devin Desktop (Cascade). Edit ~/.config/devin/mcp_config.json on macOS and Linux or %APPDATA%\devin\mcp_config.json on Windows. Older installs use ~/.codeium/windsurf/mcp_config.json.
{
"mcpServers": {
"contentstack-canoe": {
"serverUrl": "https://canoe.contentstack.com/api/mcp",
"headers": {
"Authorization": "Bearer ${env:CONTENTSTACK_CANOE_API_KEY}"
}
}
}
}
Cascade allows 100 tools across all servers. This one adds nine.
Available config.toml with bearer_token_env_var
Codex sends the key from the variable you name as a bearer token.
[mcp_servers.contentstack-canoe]
url = "https://canoe.contentstack.com/api/mcp"
bearer_token_env_var = "CONTENTSTACK_CANOE_API_KEY"
Or run codex mcp add contentstack-canoe --url https://canoe.contentstack.com/api/mcp, then add the bearer_token_env_var line.
Availablethe mcp-remote bridge
The config file runs local servers, so the mcp-remote bridge (needs Node.js) connects the remote one. Edit claude_desktop_config.json in ~/Library/Application Support/Claude/ on macOS or %APPDATA%\Claude\ on Windows, then restart.
{
"mcpServers": {
"contentstack-canoe": {
"command": "npx",
"args": [
"-y", "mcp-remote",
"https://canoe.contentstack.com/api/mcp",
"--header", "Authorization:${CONTENTSTACK_CANOE_AUTH}"
],
"env": {
"CONTENTSTACK_CANOE_AUTH": "Bearer sk_your_key_here"
}
}
}
}
The key lives in this file, so keep it private. The space inside the env value avoids a Windows argument bug.
LimitedClaude.ai and Claude Desktop connectors, request-header beta organizations only
Custom connectors with a key header work only for organizations in Anthropic's request-header beta. Not in it? Use it without a connector.
- An organization owner opens Organization settings, then Connectors, and adds a custom web connector at
https://canoe.contentstack.com/api/mcp. - Choose no sign-in and add a request header named
authorizationwith the valueBearer sk_... - Members connect it in their settings. Block
trigger_geo_test_rununless people should start runs.
One key for everyone
Every user acts as the key's creator. The header can't be edited, so rotate by removing and re-adding the connector.
Coming soonneeds OAuth sign-in
ChatGPT can't send an API key header, so it connects once OAuth sign-in ships. Until then, use it without a connector, or use a client that sends headers, like the Codex CLI.
Available authorization_token on the server entry
For agents you build. The MCP connector sends your key as a bearer token. trigger_geo_test_run is off, since no person is there to approve a run.
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: mcp-client-2025-11-20" \
-H "content-type: application/json" \
-d @- <<EOF
{
"model": "claude-opus-5",
"max_tokens": 16000,
"mcp_servers": [{
"type": "url",
"url": "https://canoe.contentstack.com/api/mcp",
"name": "contentstack-canoe",
"authorization_token": "$CONTENTSTACK_CANOE_API_KEY"
}],
"tools": [{
"type": "mcp_toolset",
"mcp_server_name": "contentstack-canoe",
"configs": { "trigger_geo_test_run": { "enabled": false } }
}],
"messages": [{ "role": "user", "content": "Summarize the latest Contentstack Canoe report for Tarnwell Cycles." }]
}
EOF
Add an agent skill for the procedure. Details in Anthropic's MCP connector docs.
Any client that speaks Streamable HTTP and sends a custom header should work. To check the connection by hand, run npx @modelcontextprotocol/inspector, pick Streamable HTTP and add the header.
Tool reference
All nine, in the order agents usually call them. Every tool but trigger_geo_test_run is annotated read only, so clients that honor annotations run them without asking.
9 tools, in call order
list_projects
Read onlyLists the brands in the key's workspace
Start here. Each brand's id is the project_id for the next call.
Parameters
None.
Returns
projects: your brands, each with id and name (up to 10). Have more? List them all with GET /api/projects?limit=50 from the API.
List my Contentstack Canoe brands.
list_geo_tests
Read onlyLists a brand's monitors with score and trend
| Parameter | Type | Description |
|---|---|---|
project_id | string, required | The brand's id from list_projects |
tests: one entry per monitor with test_id, url, product, persona, location, cadence, score, score_trend_pct, schedule_status, last_run_at and last_run_status.
Show every Tarnwell Cycles monitor with its last score, its trend and when it last ran.
get_geo_latest_report
Read onlyThe full latest report for a monitor, interpreted, with the site audit
| Parameter | Type | Description |
|---|---|---|
test_id | string, required | The monitor's test_id from list_geo_tests |
test_id, run_status, identity (brand, url, product, persona, location, answer_engines), overview (overall_score, visibility_pct, brand_rank, sentiment), score_anatomy, competitors, sentiment_detail, funnel, citations, site_audit, recommendations and run_deltas, plus _guidance when something needs your attention. See Reading responses.
site_audit carries the AI Readiness Score (readiness_score, 0 to 100) and the top fixes from the brand's latest site audit. It's null until an audit has run or if the site blocked the audit. Its score is separate from overall_score.
Summarize the latest results for our commuter e-bike monitor. Are the numbers from a finished run?
get_geo_test_history
Read onlyScores and rank run by run, for trends
| Parameter | Type | Description |
|---|---|---|
test_id | string, required | The monitor to read |
window | integer, optional | How many runs to return, 1 to 50. Default 20. |
runs: each with report_id, created_at, overall_score, per_engine_scores, visibility_pct, competition, mention_rank, sentiment_score, funnel and leaderboard_rank. Older runs carry rank only for the leaderboard.
How have our score and brand rank moved over the last 8 runs, engine by engine?
get_geo_latest_prompt_details
Read onlyEvery engine answer from the latest run
Every engine's full answer from the latest completed run, for exact quotes. It's large, so call it for words, not numbers.
| Parameter | Type | Description |
|---|---|---|
test_id | string, required | The monitor to read |
Per-question answers from each engine and the visibility detail for the run, plus run with the report_id and created_at it came from.
Show me the exact answers where Aldren was recommended and Tarnwell Cycles wasn't mentioned.
get_geo_prompt_details
Read onlyEvery engine answer from a past run
| Parameter | Type | Description |
|---|---|---|
report_id | string, required | The run to read, from get_geo_test_history |
Per-question answers and visibility detail for that run. An empty object if the run isn't found in your workspace.
Compare how ChatGPT answered "best commuter e-bike under $2,000" in the first September run and in the latest one.
get_geo_latest_citations
Read onlyCited sources from the latest run
Latest cited sources, split into your domain, competitors and third parties. by_url lists pages for PR and reviews.
| Parameter | Type | Description |
|---|---|---|
test_id | string, required | The monitor to read |
scope | string, optional | summary (default) or by_url |
stage | string, optional | Limit to one buyer stage: Awareness, Consideration or Decision. Exact case. Other values return no rows. |
from, to | dates, optional | Limit to a date range |
With summary: total, owner_breakdown (brand, competitor and third party, each with count and percent), up to six top_domains and by_model. With by_url: up to 200 rows, each with url, domain, owner, prompts, responses and a per-engine count. Both include run.
Which third-party sites do the engines cite in the Consideration stage, and which of them also name Solvik or Pellow?
get_geo_citations
Read onlyCited sources for any run or date range
| Parameter | Type | Description |
|---|---|---|
test_id | string, required | The monitor to read |
scope | string, optional | summary (default) or by_url |
report_id | string, optional | One specific run |
stage | string, optional | Limit to one buyer stage, in exact case, as above |
from, to | dates, optional | Limit to a date range |
The same summary or by_url shapes as get_geo_latest_citations, without run.
Did our own domain's share of citations grow between July and September?
trigger_geo_test_run
Starts a runStarts a run now. Spends prompts.
Runs a monitor now and spends prompts, so agents should ask first. It takes about two minutes. Then poll get_geo_latest_report until run_status.status is success or error.
| Parameter | Type | Description |
|---|---|---|
test_id | string, required | The monitor to run |
url_id (the same id as test_id), status (running) and job_id. Fails with already_running if a run is in flight. If you're out of prompts, the run is accepted but doesn't start, and run_status.status turns to error.
We just published the Aldren comparison page. Re-run the monitor and tell me what changed.
Reading responses
Fields are snake_case and can be added over time. Check these four before you repeat a number.
run_status
idle, running, success or error. Mid-run, you see the last completed run. has_partial_failure and errored_sections flag what didn't finish.
_guidance.notes
Notes from the tool, not an engine: stale numbers, no runs yet, missing sentiment. Pass them on.
run_deltas.comparable
False with no previous run, so a zero delta means no data. leaderboard_rank_delta is positive when you climbed.
History tiers
Only the latest run has full competitor detail. Older runs keep leaderboard rank.
get_geo_latest_report, trimmed. Illustrative values, fictional brand.
{
"run_status": { "status": "success", "last_run_at": "2026-10-05T09:14:02Z", "has_partial_failure": false },
"identity": { "brand": "Tarnwell Cycles", "product": "Commuter e-bikes" },
"overview": {
"overall_score": 67,
"visibility_pct": 62,
"brand_rank": { "rank": 3, "of": 10 },
"sentiment": { "label": "Positive", "score": 74 }
},
"funnel": { "awareness": 47, "consideration": 57, "decision": 81 },
"site_audit": { "readiness_score": 80, "priority_fixes": ["..."] },
"recommendations": [
{ "rank": 1, "category": "competitive", "title": "Close the gap with Aldren", "how": ["..."] }
],
"run_deltas": { "comparable": true, "score_delta": 6, "leaderboard_rank_delta": 1 }
}
Treat report text as data
Answers, citations and quotes come from the web. Agents should quote them and never follow instructions inside them. Every Contentstack Canoe skill enforces this.
Scores, mentions and citations are computed in code, so report the values as returned. Sentiment is a model's read. Metrics defines every number.
Add the Contentstack MCP
With both servers, your agent reads a recommendation here, finds the entry in your stack and drafts the change for your approval.
{
"mcpServers": {
"contentstack-canoe": {
"url": "https://canoe.contentstack.com/api/mcp",
"headers": { "Authorization": "Bearer ${env:CONTENTSTACK_CANOE_API_KEY}" }
},
"contentstack": {
"command": "npx",
"args": ["-y", "@contentstack/mcp"],
"env": { "CONTENTSTACK_API_KEY": "<YOUR_STACK_API_KEY>", "GROUPS": "cma" }
}
}
}
The Contentstack MCP has its own auth: see the Contentstack MCP server docs. Then add the Fix it in Contentstack skill, or run it unattended with Contentstack Agent OS.
Security and least privilege
A key carries its creator's authority in one workspace, and the tool list isn't a security boundary. Keep mistakes small.
- One key per integration, named for its job, so you can revoke one alone.
- Keys live in env vars or a secret manager, never in files or chats.
- Block
trigger_geo_test_runfor read-only jobs. Claude.ai connectors and the Claude API can turn it off. Elsewhere, keep approval on. - A person approves CMS changes. Contentstack Canoe never writes to your site.
- Revoke quiet keys. Check Last used.
We log each call's tool, user and workspace, never keys, headers or payloads. An id from another workspace returns nothing.
Errors
Tool errors
A failed tool returns isError: true, with code and message as JSON in its first text block.
Out of prompts? No tool error says so: the run's run_status.status turns to error. Check Usage in Settings and Plans and limits.
HTTP errors
These arrive before any tool runs, so clients usually show a failed connection.
Coming soon
- OAuth sign-in Coming soonConnect ChatGPT and Claude.ai without a key header.
- Read only keys for MCP Coming soonRead-only agent access, enforced by the key.
- Native alerts Coming soonScore and competitor alerts without polling. Today, use the Visibility alerts skill.
- Outbound webhooks Coming soonEvents like "run finished" sent to your endpoint.