Cacoo Remote MCP Server
Enables managing Cacoo diagrams, folders, organizations, and account information through MCP tools over HTTP, with OAuth-based authentication, email allowlisting, and support for multiple Cacoo accounts.
README
Cacoo Remote MCP Server
A remote MCP server for the Cacoo API, deployable to Cloudflare Workers, AWS Lambda, Google Cloud Run or Azure Container Apps.
Unlike a local stdio MCP server, this runs as a hosted HTTP endpoint: you authenticate once in the browser with OAuth, and your Cacoo API key never leaves the server.
Features
- 14 MCP tools covering diagrams, folders, organizations and account information
- OAuth 2.1 with PKCE — clients authenticate in the browser; no API key on the client
- Email allowlist — application-level authorization on top of the upstream IdP
- Multiple Cacoo accounts — route per call, with a per-account read-only guard
- Four deployment targets sharing the same tool implementations
Choosing a deployment
| Cloudflare | AWS | Google Cloud | Azure | |
|---|---|---|---|---|
| Runtime | Workers (edge) | Lambda + API Gateway | Cloud Run | Container Apps |
| MCP session | Durable Objects | Stateless | Stateless | Stateless |
| OAuth authorization server | @cloudflare/workers-oauth-provider |
src/oauth |
src/oauth |
src/oauth |
| Upstream IdP | Cloudflare Access | Amazon Cognito | Google account | Microsoft Entra ID |
| State storage | Workers KV | DynamoDB (TTL) | Firestore (TTL) | Cosmos DB (TTL) |
| Secrets | Workers Secrets | Secrets Manager | Secret Manager | Key Vault |
| IaC | wrangler | AWS SAM | Terraform | Bicep |
| Config file | .dev.vars |
infra/aws/params.yaml |
infra/gcp/terraform.tfvars |
infra/azure/params.json |
The tools and their behavior are identical on all of them. Every platform can use either Google or Microsoft Entra ID as its upstream IdP; the table shows the default.
Architecture
The same MCP server runs on four platforms. Each platform subgraph holds its own wiring —
gateway, storage and upstream IdP — and the Node-based ones funnel into the shared
src/oauth, which in turn uses src/core.
flowchart TB
subgraph clients["MCP clients"]
direction LR
CC["Claude Code<br/><i>native HTTP transport</i>"]
CD["Claude Desktop / Kiro / Cursor<br/><i>mcp-remote proxy</i>"]
end
subgraph cf["Cloudflare src/platforms/cloudflare"]
direction TB
CFW["Workers <i>OAuthProvider</i>"]
CFA["Cloudflare Access<br/><i>or Google / Entra ID</i>"]
CFKV["KV <i>OAUTH_KV</i>"]
CFDO["Durable Object<br/><i>CacooMCP session</i>"]
CFW -. "OIDC" .-> CFA
CFW --- CFKV
CFW --> CFDO
end
subgraph aws["AWS src/platforms/aws"]
direction TB
APIGW["API Gateway<br/><i>HTTP API + ACM + Route 53</i>"]
LAMBDA["Lambda <i>nodejs22 / arm64</i>"]
COG["Amazon Cognito"]
DDB["DynamoDB <i>OAuth state</i>"]
SM["Secrets Manager<br/><i>Cacoo API keys</i>"]
APIGW --> LAMBDA
LAMBDA -. "OIDC" .-> COG
LAMBDA --- DDB
LAMBDA --- SM
end
subgraph gcp["Google Cloud src/platforms/gcp"]
direction TB
RUN["Cloud Run <i>container</i>"]
GID["Google account"]
FS["Firestore <i>OAuth state</i>"]
GSM["Secret Manager"]
RUN -. "OIDC" .-> GID
RUN --- FS
RUN --- GSM
end
subgraph azure["Azure src/platforms/azure"]
direction TB
ACA["Container Apps <i>container</i>"]
ENT["Entra ID"]
COS["Cosmos DB <i>OAuth state</i>"]
AKV["Key Vault"]
ACA -. "OIDC" .-> ENT
ACA --- COS
ACA --- AKV
end
subgraph oauth["src/oauth shared by Node runtimes"]
OP["provider.ts <i>OAuth authorization server</i>"]
OS["store.ts <i>AuthStore interface</i>"]
OP --- OS
end
subgraph shared["src/core every runtime"]
CS["create-server.ts<br/><i>tool registration + email allowlist</i>"]
TOOLS["tools/ <i>14 MCP tools</i>"]
BC["cacoo-client.ts<br/><i>account routing + readOnly guard</i>"]
CS --> TOOLS --> BC
end
CACOO["Cacoo API <i>/api/v1</i>"]
clients == "Streamable HTTP + OAuth" ==> CFW
clients == "Streamable HTTP + OAuth" ==> APIGW
clients == "Streamable HTTP + OAuth" ==> RUN
clients == "Streamable HTTP + OAuth" ==> ACA
CFDO --> CS
LAMBDA --> OP
RUN --> OP
ACA --> OP
OP --> CS
DDB -. "implements AuthStore" .-> OS
FS -. "implements AuthStore" .-> OS
COS -. "implements AuthStore" .-> OS
BC == "per-account API key" ==> CACOO
Request flow
sequenceDiagram
autonumber
participant C as MCP client
participant S as Worker / Lambda / Container
participant I as Upstream IdP
participant K as Cacoo
C->>S: POST /mcp
S-->>C: 401 + OAuth metadata
C->>S: authorize
S->>I: redirect to upstream OIDC
I-->>S: callback with identity
Note over S: email allowlist check<br/>reject -> access_denied tool only
S-->>C: access token
C->>S: tools/list, tools/call
Note over S: resolve account -> pick API key<br/>readOnly guard blocks writes
S->>K: Cacoo REST API v1
K-->>S: JSON / PNG / XML
S-->>C: MCP result
Authorization happens in two layers. The upstream IdP decides who may sign in, and the
email allowlist decides who gets tools: a user outside the allowlist receives a server
exposing only access_denied. The readOnly flag on an account rejects every non-GET
request in the API client layer, so it cannot be bypassed by an individual tool.
Directory layout
Three layers, by how widely each one can be reused:
src/
core/ Every runtime. Depends only on the MCP SDK and zod
cacoo-client.ts Cacoo API client (account routing + readOnly guard)
tools/ 14 MCP tools
create-server.ts MCP server assembly and authorization
oauth/ Node runtimes. OAuth authorization server (Express)
provider.ts OAuthServerProvider implementation
store.ts AuthStore interface — the persistence port
upstream.ts Upstream OIDC client
consent.ts Consent screen
app.ts Express app exposing /authorize, /token, /mcp, ...
platforms/
cloudflare/ Workers wiring (uses its own Workers OAuth provider)
aws/ Lambda wiring + DynamoDB / Secrets Manager adapters
gcp/ Cloud Run wiring + Firestore / Secret Manager adapters
azure/ Container Apps wiring + Cosmos DB / Key Vault adapters
infra/
aws/ SAM template and parameters
gcp/ Terraform configuration
azure/ Bicep template and parameters
src/platforms/<name> is the only place a cloud SDK appears. Adding another Node-hosted
platform means implementing AuthStore, a secret lookup, and an entry point that hands
the Express app to the runtime.
Configuration
Accounts are configured as a single JSON string, CACOO_ACCOUNTS_CONFIG.
See Cacoo API keys and account configuration for how to issue a
key and find your organizationKey.
{
"accounts": [
{ "name": "main", "apiKey": "xxx", "organizationKey": "your-org-key" },
{ "name": "shared", "apiKey": "yyy", "readOnly": true }
],
"defaultAccount": "main"
}
| Field | Meaning |
|---|---|
name |
Name used by the account argument on every tool |
apiKey |
Cacoo API key. Generate one at https://cacoo.com/profile/api |
organizationKey |
Default organization for diagram and folder tools. Required on non-legacy plans; tools can override it per call |
readOnly |
When true, every non-GET call is rejected |
baseUrl |
Defaults to https://cacoo.com |
Connecting from MCP Clients
Claude Code
claude mcp add --transport http cacoo https://<your-domain>/mcp -s user
Claude Desktop / Kiro / Cursor
{
"mcpServers": {
"cacoo": {
"command": "npx",
"args": ["mcp-remote", "https://<your-domain>/mcp"]
}
}
}
A browser opens on first connection and asks you to authenticate.
Claude Desktop (.mcpb bundle)
Instead of hand-editing the JSON above, you can double-click a .mcpb (MCP Bundle) to
install it. It is generated during deploy and written to dist/.
npm run mcpb:pack # generate on its own
npm run aws:deploy # generated as part of the deploy
The endpoint URL is a user_config field, and the domain you deployed to is baked in as
its default, resolved from --host, MCP_HOSTNAME, ApiDomainName in
infra/aws/params.yaml, or MCP_HOSTNAME in .dev.vars, in that order.
The bundle does not contain the server itself. MCPB is a local-execution format, so
it ships mcp-remote as a stdio proxy that connects to your deployed server. Claude Code
does not use this bundle — it stays on claude mcp add --transport http.
Available Tools
Diagrams
| Tool | Description |
|---|---|
list_diagrams |
List diagrams with filtering, sorting and pagination |
get_diagram |
Details of one diagram, including sheets and comments |
create_diagram |
Create a new empty diagram |
copy_diagram |
Copy an existing diagram |
move_diagram |
Move a diagram to another folder |
delete_diagram |
Delete a diagram |
get_diagram_image |
PNG rendering of a diagram or one sheet |
get_diagram_contents |
Structured contents (shapes, text, lines) as XML |
Workspace
| Tool | Description |
|---|---|
list_accounts |
Configured accounts, the default, and which allow writes |
list_folders |
Folders in the account |
list_organizations |
Organizations, including the key used as organizationKey |
get_account |
Profile of the authenticated account |
get_license |
License/plan details |
get_user |
Public profile of a user by name |
Security
- Authentication: OAuth 2.1 with PKCE (S256) against an upstream IdP
- Authorization:
ALLOWED_EMAILSprovides an application-level email allowlist. Leaving it empty disables the allowlist, so anyone who can sign in through the upstream IdP gets every tool - API key protection: Cacoo API keys stay on the server and are never sent to clients
- Client consent: Dynamic Client Registration is open to anyone, so authorization is
gated behind a consent screen naming the client and its redirect target, with CSRF
protection. Approvals are keyed on
client_id+redirect_uri - Write guard: accounts marked
readOnly: truereject every non-GET call. The check lives insrc/core/cacoo-client.ts, so it does not depend on individual tools - Dependency cooldown:
.npmrcsetsmin-release-age=3, so dependency resolution only considers package versions that have been public for at least three days
Local Development
npm install
npm run type-check # all four platforms
npm test # 108 assertions
| Test | Covers |
|---|---|
npm run test:cacoo-client |
URL building, organizationKey resolution, readOnly guard, error formatting, 4MB image cap |
npm run test:tools |
All 14 tools register; allowlist gating |
npm run test:oauth |
DCR, PKCE, single-use tokens, scopes, revocation |
npm run test:oauth-consent |
HTML escaping, signed cookies, CSRF, approval gate |
npm run test:oauth-upstream |
Endpoint resolution for Cognito / Google / Entra ID |
IaC can be validated without cloud credentials:
npm run aws:validate # sam validate --lint
npm run gcp:validate # terraform validate
npm run azure:validate # az bicep build
Credits
The tool definitions are ported from cacoo-mcp-server (local stdio). The remote server architecture is shared with backlog-remote-mcp-server.
License
MIT
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。