GoCanvas Server (Read Only)
FreeNot checkedExposes read-only GoCanvas API v3 endpoints as MCP tools, enabling listing and retrieval of forms, submissions, reports, and reference data.
About
Exposes read-only GoCanvas API v3 endpoints as MCP tools, enabling listing and retrieval of forms, submissions, reports, and reference data.
README
A minimal Model Context Protocol server that exposes the read-only endpoints of the GoCanvas API v3 as MCP tools. Scope is limited to four areas: Forms, Submissions, Reports, and Reference Data. No create/update/delete operations are exposed.
Tools
Forms
| Tool | Endpoint |
|---|---|
list_forms |
GET /forms |
get_form |
GET /forms/{form_id} |
list_form_assigned_users |
GET /forms/{form_id}/assigned_users |
list_form_shared_departments |
GET /forms/{form_id}/shared_departments |
Submissions
| Tool | Endpoint |
|---|---|
list_submissions |
GET /submissions (requires form_id) |
get_submission |
GET /submissions/{submission_id} |
list_submission_revisions |
GET /submissions/{submission_id}/revisions |
get_submission_value |
GET /submissions/{submission_id}/values/{value_id} |
Reports
| Tool | Endpoint |
|---|---|
list_form_reports |
GET /forms/{form_id}/reports |
get_form_report |
GET /forms/{form_id}/reports/{report_id} |
get_submission_default_pdf |
GET /submissions/{submission_id}/pdf (PDF) |
get_submission_report_pdf |
GET /submissions/{submission_id}/reports/{report_id} (PDF) |
get_submission_standard_pdf |
GET /submissions/{submission_id}/standard_pdf (PDF) |
The three PDF tools return the binary PDF inline as base64 (content_base64,
content_type, size_bytes) — the server is a pure passthrough and never writes
to disk, so the tools work on read-only / ephemeral hosts such as AWS Lambda.
Reference Data
| Tool | Endpoint |
|---|---|
list_reference_data |
GET /reference_data |
get_reference_data |
GET /reference_data/{reference_data_id} |
Authentication
| Tool | Endpoint |
|---|---|
refresh_oauth_token |
POST /oauth/token (client-credentials) |
refresh_oauth_token forces a fresh bearer token to be fetched and cached. It
only applies to server-side OAuth (GOCANVAS_CLIENT_ID /
GOCANVAS_CLIENT_SECRET) mode; in passthrough mode the caller owns the token and
the server cannot refresh it. It is normally unnecessary — the server fetches a
token on startup and refreshes it automatically before expiry and on a 401 — but
it is exposed so the agent can rotate the token explicitly. The returned access
token is masked.
Setup
This project uses uv. With uv installed, no
manual environment setup is required — uv run resolves and installs
dependencies (from pyproject.toml) automatically on first launch.
# optional: pre-create the environment
uv sync
Alternative: plain pip + venv
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Configuration
There are two independent authentication hops, and they use different credentials on purpose.
Hop 1 — caller to this server
Under an HTTP transport, every request must present a shared secret in the
MCP_GATEWAY_HEADER header (default x-mcp-key). Anything else gets 401.
This is what keeps a public endpoint from being usable by the public.
| Variable | Description |
|---|---|
MCP_GATEWAY_KEYS |
Comma-separated list of accepted secrets. A list rather than one value so keys rotate with no downtime: add the new key, repoint the caller, drop the old. Generate with openssl rand -base64 32. |
MCP_GATEWAY_HEADER |
Header the secret is read from (default x-mcp-key). Never set this to Authorization — that header feeds the GoCanvas passthrough path, so reusing it would forward your gateway secret upstream to the GoCanvas API. |
GOCANVAS_REQUIRE_GATEWAY_KEY |
Default true. With no keys configured the endpoint returns 503 rather than serving openly. Set false only for local development bound to loopback. |
stdio is unaffected — the check only applies to the HTTP transports.
Hop 2 — this server to the GoCanvas API
Resolved per request, in priority order:
| Source | Description |
|---|---|
Incoming Authorization header |
Forwarded verbatim. Only when GOCANVAS_ALLOW_PASSTHROUGH=true, which is off by default — otherwise a caller could override the server's own credentials, and any bearer they send would be relayed to a third party. |
GOCANVAS_CLIENT_ID / GOCANVAS_CLIENT_SECRET |
OAuth 2.0 client credentials. A short-lived bearer is fetched from /oauth/token, cached, and auto-refreshed on expiry or 401. The recommended mode for hosted use. |
GOCANVAS_API_TOKEN |
Static bearer token. |
GOCANVAS_USERNAME / GOCANVAS_PASSWORD |
HTTP Basic auth (fallback). |
Secrets Manager
For hosted deployments, keep both hops' credentials in one secret instead of in environment variables:
| Variable | Description |
|---|---|
GOCANVAS_SECRET_ID |
Name or ARN of a Secrets Manager secret whose JSON body may hold client_id, client_secret, scope, gateway_keys. Read once per process (per Lambda container) and cached; takes priority over the equivalent environment variables. |
GOCANVAS_SECRET_REGION |
Defaults to AWS_REGION. |
A failure to read the secret is logged and falls back to the environment variables rather than crashing the server.
Other optional variables
| Variable | Description |
|---|---|
GOCANVAS_OAUTH_SCOPE |
Optional OAuth scope to request (server-side OAuth only). |
GOCANVAS_BASE_URL |
Defaults to https://api.gocanvas.com/api/v3. |
GOCANVAS_TIMEOUT |
HTTP timeout in seconds (default 30). |
GOCANVAS_TRANSPORT |
stdio (default), streamable-http, or sse. |
GOCANVAS_HOST |
Bind host for HTTP transports (default 127.0.0.1). |
GOCANVAS_PORT |
Bind port for HTTP transports (default 8000). |
GOCANVAS_STATELESS_HTTP |
No per-session state between requests (default true; required for Lambda). |
GOCANVAS_JSON_RESPONSE |
Return JSON instead of an SSE stream (default true; required for Lambda). |
GOCANVAS_ALLOWED_HOSTS |
Comma-separated Host allow-list for DNS-rebinding protection. Defaults to localhost only, which returns HTTP 421 behind API Gateway / a Function URL — set your public domain or * when hosting publicly. |
GOCANVAS_ALLOWED_ORIGINS |
Comma-separated Origin allow-list (same semantics). |
If no usable credentials are available for a call, the tool returns a clear error — the server itself still starts fine.
Running
Locally over stdio (default)
GOCANVAS_CLIENT_ID=... GOCANVAS_CLIENT_SECRET=... uv run server.py
Publicly over HTTP
Run with an HTTP transport and a gateway key:
GOCANVAS_TRANSPORT=streamable-http GOCANVAS_HOST=0.0.0.0 GOCANVAS_PORT=8000 \
MCP_GATEWAY_KEYS="$(openssl rand -base64 32)" \
GOCANVAS_CLIENT_ID=... GOCANVAS_CLIENT_SECRET=... \
uv run server.py
The MCP endpoint is served at /mcp. No PDFs or other state are written to
disk, so the server runs cleanly on read-only / ephemeral hosts.
Hosting publicly? Set
GOCANVAS_ALLOWED_HOSTSto your public domain (or*). The default DNS-rebinding protection allows only localhost and returns HTTP421 Misdirected Requestfor any otherHostheader.
Deploying to AWS Lambda
The module exposes an ASGI app (asgi_app()) and a Mangum-wrapped Lambda entry
point (lambda_handler), so it runs on Lambda behind a Lambda Function URL
or an API Gateway HTTP API with no long-running process. Stateless +
JSON-response mode is the default (Lambda containers are ephemeral and don't
share session state, and API Gateway can't proxy an SSE stream).
template.yaml is a complete AWS SAM stack: the function, a Function URL, a Secrets Manager secret, a log group with retention, and a concurrency cap.
Prerequisites
- An AWS account and the AWS CLI,
configured (
aws configure) with permission to create Lambda functions, IAM roles, secrets, and log groups. - The SAM CLI
(
brew install aws-sam-clion macOS). - GoCanvas API client ID and secret for a service account, from the GoCanvas admin console.
1. Build and deploy
sam build
The function uses BuildMethod: makefile, so this runs the
build-GoCanvasMcpFunction target in Makefile rather than SAM's
default Python builder. Two reasons: the default builder copies the entire
CodeUri directory, which here would ship the 49 MB local .venv and the
OpenAPI specs; and the Makefile pins --platform manylinux2014_aarch64 --python-version 3.12 --only-binary=:all: so the correct Linux/arm64 wheels are
installed even when you build from macOS. No Docker or --use-container
needed. If you change Runtime or Architectures in the template, update
those flags to match.
Then deploy:
sam deploy --guided
Accept the defaults, except: answer yes to
GoCanvasMcpFunction Function URL may not have authorization defined, Is this okay?
— AuthType: NONE is required because no MCP client can produce a SigV4
signature. The gateway-key check inside the function is the access control.
Subsequent deploys are just sam build && sam deploy.
Note the stack outputs — McpEndpoint, SecretArn, GatewayHeaderName:
aws cloudformation describe-stacks --stack-name gocanvas-mcp \
--query 'Stacks[0].Outputs' --output table
2. Populate the secret
The stack creates the secret itself but deliberately gives it no value — the
value is not a template-managed property, so that a later stack update can never
reassert a placeholder over your live credentials. Until you run the command
below, the endpoint returns 503 and each cold start logs a warning naming this
step.
Use put-secret-value, not create-secret: the secret already exists by this
point, so create-secret fails with ResourceExistsException. The same command
is also how you rotate later. Generate a gateway key and write both sets of
credentials in one shot:
GATEWAY_KEY="$(openssl rand -base64 32)"
echo "Gateway key (save this, you will paste it into Copilot Studio): $GATEWAY_KEY"
aws secretsmanager put-secret-value \
--secret-id gocanvas-mcp/config \
--secret-string "$(jq -n \
--arg cid "YOUR_GOCANVAS_CLIENT_ID" \
--arg csec "YOUR_GOCANVAS_CLIENT_SECRET" \
--arg key "$GATEWAY_KEY" \
'{client_id:$cid, client_secret:$csec, gateway_keys:[$key]}')"
Because the secret is cached per Lambda container, changes take effect on the next cold start. To apply immediately, force new containers:
aws lambda update-function-configuration --function-name gocanvas-mcp \
--description "secret rotated $(date -u +%FT%TZ)"
3. Verify
Without the key — expect 401:
curl -s -o /dev/null -w '%{http_code}\n' -X POST "$MCP_ENDPOINT" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'
With the key — expect 200 and a serverInfo block:
curl -s -X POST "$MCP_ENDPOINT" \
-H "x-mcp-key: $GATEWAY_KEY" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'
If you get 421 Misdirected Request, GOCANVAS_ALLOWED_HOSTS does not include
the Function URL host. If you get 503 server not configured, the secret has no
gateway_keys (or the function can't read it — check CloudWatch).
4. Tighten the host allow-list
The Function URL hostname isn't known until the stack exists, so the template
bootstraps with AllowedHosts: "*". Once deployed, pin it:
sam deploy --parameter-overrides \
"AllowedHosts=abc123xyz.lambda-url.us-east-1.on.aws"
Connecting Copilot Studio
Copilot Studio reaches an MCP server through a Power Platform custom connector. Its OAuth path is authorization-code-on-behalf-of-user (the PKCE flow GoCanvas doesn't play well with), and it would make each end user sign in — neither of which is what you want here. Use API Key auth instead, which gives the agent a standing credential:
- Create the connector. In Copilot Studio, Tools → Add a tool → New tool →
Model Context Protocol, or import an OpenAPI definition carrying
x-ms-agentic-protocol: mcp-streamable-1.0. Host = your Function URL hostname, base path =/mcp. - Security tab. Authentication type API Key; parameter label anything;
parameter name
x-mcp-key; location Header. - Create the connection once, pasting the gateway key from step 2 above.
- Set the tool to use the maker/author-provided connection, not end-user credentials. This is what makes the agent's access standing — no per-user sign-in prompt, and the agent works for every user of the published agent.
What this trades away
Every Copilot Studio user now acts as one GoCanvas service account. GoCanvas can no longer enforce per-user authorization, because it only ever sees that account. The tools here are read-only, which bounds the blast radius, but scope the service account's GoCanvas permissions to the minimum set of forms and data the agent should ever surface. "The agent won't ask for it" is not a control.
Hardening beyond the key
- WAF IP allow-list. Front the Function URL with CloudFront + AWS WAF and allow only the published Power Platform outbound IP ranges for your region. Treat this as defense in depth, not a primary control: the ranges are shared across all Power Platform tenants and they change, so you have to track the published list. The value is that a leaked key alone stops being enough.
- Concurrency cap.
ReservedConcurrency(default10) bounds what a leaked key can cost you and keeps it from exhausting your GoCanvas rate limit. - Watch the rejections. Failed gateway checks log at
WARNINGto/aws/lambda/gocanvas-mcp. A metric filter onRejected MCP requestplus an alarm turns a leaked or probed endpoint into a page.
Payload-size limit. API Gateway / a buffered Function URL caps a response at
6 MB. The PDF tools return the file base64-encoded inline (~33% overhead), so
a PDF larger than ~4.5 MB can exceed that limit. Raise MemorySize/Timeout for
large forms; for consistently large PDFs, front the function with a Function URL
in RESPONSE_STREAM invoke mode or fetch the PDF out-of-band.
MCP client configuration
Use uv run as the command. --directory points uv at this project so it
uses the right dependencies regardless of the client's working directory:
{
"mcpServers": {
"gocanvas": {
"command": "uv",
"args": [
"run",
"--directory", "/absolute/path/to/GoCanvas",
"server.py"
],
"env": {
"GOCANVAS_CLIENT_ID": "your_client_id",
"GOCANVAS_CLIENT_SECRET": "your_client_secret"
}
}
}
}
If uv isn't on the client's PATH, use its absolute path (e.g.
~/.local/bin/uv) as the command.
Alternative: point at a virtualenv interpreter (pip users)
{
"mcpServers": {
"gocanvas": {
"command": "/absolute/path/to/GoCanvas/.venv/bin/python",
"args": ["/absolute/path/to/GoCanvas/server.py"],
"env": {
"GOCANVAS_CLIENT_ID": "your_client_id",
"GOCANVAS_CLIENT_SECRET": "your_client_secret"
}
}
}
}
On Windows the interpreter is at .venv\Scripts\python.exe. Using a bare
python will fail with ModuleNotFoundError: httpx because the client does not
use your activated shell environment.
Notes
- Pagination: list tools accept a
pageargument. Response pagination headers (link,current-page,page-items,total-count,total-pages) are surfaced under apaginationkey in the tool result. - Warm Lambda containers:
lambda_handlerrebuilds the ASGI app on each invocation. Mangum runs the ASGI lifespan per invocation andStreamableHTTPSessionManager.run()raises if entered twice, so a cached app would make every request after a container's first one fail. Rebuilding costs a route table per request and is free of correctness cost in stateless mode. - Rate limiting: the server honors
429 Too Many Requestsresponses, waiting according to theRateLimit-Reset/RateLimit-Remainingheaders (or a bounded exponential backoff) before retrying, per GoCanvas best practices.
Installing GoCanvas Server (Read Only)
This server has no published package — it is built from source. Open the repository and follow its README.
▸ github.com/kbates97/GoCanvas-Readonly-MCPFAQ
Is GoCanvas Server (Read Only) MCP free?
Yes, GoCanvas Server (Read Only) MCP is free — one-click install via Unyly at no cost.
Does GoCanvas Server (Read Only) need an API key?
No, GoCanvas Server (Read Only) runs without API keys or environment variables.
Is GoCanvas Server (Read Only) hosted or self-hosted?
Self-hosted: the server runs locally on your machine via the install command above.
How do I install GoCanvas Server (Read Only) in Claude Desktop, Claude Code or Cursor?
Open GoCanvas Server (Read Only) on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.
Related MCPs
GitHub
PRs, issues, code search, CI status
by GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Unyly Deploy
Deploy sites and MCP servers to Unyly straight from your AI agent
by UnylyAmap Maps Mcp Server
MCP server for using the AMap Maps API
by duxiaohuiPayMCP
Lightweight payments layer for MCP servers: turn tools into paid endpoints with a two-line decorator. [PyPI](https://pypi.org/project/paymcp/) · [npm](https://w
by blustAIServer For Power BI
Enables exploring Microsoft Fabric / Power BI workspaces and semantic models, and executing ad-hoc DAX queries.
by Mitsubishi-FusoGpt Image 2 Mcp
RunAPI GPT Image 2 MCP server for image generation: create tasks, poll results, and check pricing across 1 model from Claude Code, Codex, Cursor, and VS Code.
by runapi-aiTemplate MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
by mcpdotdirectSupabase
Database, auth and storage
by SupabaseCompare GoCanvas Server (Read Only) with
Not sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All development MCPs
