Cloudflare-hosted, read-only Remote MCP service for Firstsun analytics.
Mode A: ChatGPT / OAuth client Mode B: agent / CI / automation
| Managed OAuth + PKCE | CF-Access-Client-Id / -Secret
+------------------+------------------+
v
Cloudflare Access (one application, https://mcp.firstsun.org/anas/mcp)
| Cf-Access-Jwt-Assertion
v
Cloudflare Worker: anas-mcp
|
| validate the Access JWT BEFORE MCP initialization / tool execution
v
createMcpHandler() --> read-only analytics tools
|
|
+--> Google Analytics Data API
+--> Google Search Console API
+--> Hyperdrive --> PostgreSQL --> Clarity analytics
- HTTP API contract: repository-root
openapi.yamluses OpenAPI 3.2.0 and is intended for Swagger-compatible tooling. It documents the HTTP surface; MCP tools remain MCP-native schemas rather than fake REST endpoints. - MCP authentication: production
/anas/mcpis protected by one Cloudflare Access application that concurrently accepts Mode A, Managed OAuth (interactive clients such as ChatGPT) and Mode B, Access Service Token (headless agents). The Worker validatesCf-Access-Jwt-Assertionbefore any MCP/tool/database work./healthstays public and minimal. Cloudflare configuration is owned byfirstsun-dev/infra-config. - Search Console: direct Search Console API queries with the shared Google OAuth credential and the non-secret
GSC_ALLOWED_SITESproperty allowlist. Tools:gsc_list_sites,gsc_search_analytics(seedocs/gsc.md). Fail-closed ifGSC_ALLOWED_SITESis missing or malformed; configured identifiers are not yet verified against the Google account. - Clarity: read normalized PostgreSQL data populated by
firstsun-dev/windmill-flows; this service never calls the Clarity API directly. Tools:clarity_overview,clarity_pages,clarity_acquisition,clarity_page(seedocs/clarity.md; needs theCLARITY_PROJECT_KEYSvar).healthlistsenabledSourcesandplannedSources. - GA4: read through the Google Analytics Data API with the shared Google OAuth credential (Secrets Store binding
GOOGLE_OAUTH_CREDENTIALS, secretANAS_PROD_GOOGLE_OAUTH_CREDENTIALS) and the non-secretGA4_PROPERTY_IDSallowlist. Tools:ga4_overview,ga4_pages(groupBypathdefault /fullUrl),ga4_acquisition,ga4_run_report(seedocs/ga4.md). Disabled and fail-closed until both are configured. - Credentials: Secrets Store first. Any long-lived secret consumed directly by this Worker should come from Cloudflare Secrets Store whenever supported.
- Google OAuth: one JSON credential stored in Cloudflare Secrets Store.
- Database: read-only PostgreSQL access through Cloudflare Hyperdrive; the database password remains managed by Hyperdrive and is not duplicated into Worker secrets.
- CI/CD: Cloudflare Worker deployment is implemented centrally in
firstsun-dev/.github; this repository must use a thin caller for the shared_cf-worker-v2.ymlworkflow (production-only) rather than duplicating deploy steps.
See:
openapi.yamlanddocs/api.mdfor the HTTP/OpenAPI contractdocs/mcp-authentication.mdanddocs/cloudflare-access.mdfor dual-mode Access authentication, client setup, and rolloutdocs/gsc.mdfor the Search Console tools, data caveats and operator checklistdocs/clarity.mdfor the Clarity tools, data semantics and configurationdocs/credentials.mdfor credential ownership/storage policydocs/cicd.mdfor centralized deployment policy and caller-workflow contract
openapi.yaml is the canonical HTTP API description.
Current documented HTTP surface:
GET /health
POST /anas/mcp
Use Swagger-compatible OpenAPI 3.2 tooling to inspect/render the contract.
Do not represent MCP tools such as clarity_page, future ga4_run_report, or gsc_search_analytics as fake REST routes. Those contracts belong to MCP tool schemas and are discovered through MCP.
Any change to an HTTP route, method, authentication requirement, status code, content type, or stable payload must update openapi.yaml in the same change.
Automated OpenAPI validation is tracked in openspec/changes/adopt-openapi-http-contract/ and must be included in the project check/centralized CI path before being considered complete.
Production /anas/mcp accepts two authentication modes at the same time through one Cloudflare Access application: Mode A, Managed OAuth (authorization code + PKCE) for interactive clients, and Mode B, Access Service Token (CF-Access-Client-Id + CF-Access-Client-Secret) for headless agents. This is not a toggle, fallback chain, or pair of endpoints.
Cloudflare Access owns OAuth and Service Token evaluation. The Worker only verifies the resulting Cf-Access-Jwt-Assertion (signature, issuer, AUD, expiry) and fails closed with a generic 401 (or 503 if its non-secret Access config is missing). Service Tokens identify machines, not users. CLOUDFLARE_API_TOKEN and provider/database/CI credentials are never valid substitutes, credentials are never accepted via query string, and IP allowlisting or User-Agent checks are not authentication. anas-mcp must not add OAuth endpoints, KV, D1, Durable Objects, or PostgreSQL auth tables for MCP client authentication.
Use Cloudflare Secrets Store as the production source of truth for application-held secrets such as Google OAuth credentials, API tokens, client secrets, signing keys, and encryption keys.
Do not put production credentials in:
- Wrangler
vars - committed configuration
.envor.dev.vars- source code
- logs
Do not use wrangler secret when Secrets Store can serve the same production credential unless an accepted OpenSpec change documents why.
Intentional exceptions:
- MCP client authentication holds no Worker secret: Access OAuth tokens, assertions, and Service Token credentials are Access-managed or client-held. The Worker only has non-secret
ACCESS_TEAM_DOMAIN/ACCESS_AUDvars. - PostgreSQL credentials stay in the Hyperdrive connection.
- The Microsoft Clarity API token stays in
firstsun-dev/windmill-flowsbecause this service never calls Clarity directly. - Derived short-lived Google OAuth access tokens stay in runtime memory only.
- CI bootstrap credentials such as the Cloudflare deploy token/account ID may use the protected GitHub Actions mechanism required by the organization-managed reusable deployment workflow.
The deployment implementation belongs to:
firstsun-dev/.github/.github/workflows/_cf-worker-v2.yml
anas-mcp keeps a thin caller (.github/workflows/cicd.yml): deployment_strategy: production-only, uses and tools_ref pinned to the same full commit SHA, push trigger only. See docs/cicd.md for the exact inputs.
Do not copy the shared wrangler versions upload/deploy, rollback, or generic runner logic into this repository.
The project uses pnpm (packageManager pinned, pnpm-lock.yaml committed) to match the central workflow. .github/workflows/cicd.yml calls _cf-worker-v2.yml pinned to a commit SHA (production-only): every branch push runs verification, only main deploys. There is no automatic rollback; post-deploy smoke failures require operator action.
See docs/cicd.md and openspec/changes/adopt-cf-worker-v2-ci/.
The repository currently contains the MCP foundation and architecture specifications:
- stateless
/anas/mcpendpoint using Cloudflare Agents SDK /healthHTTP endpoint- MCP
healthtool - OpenAPI 3.2.0 HTTP contract
- OpenSpec architecture, requirements, design, and implementation tasks
- Secrets Store first credential policy
- Cloudflare Access assertion validation for
/anas/mcp(Worker validation + local tests implemented; Access application, Managed OAuth, Service Token, and production E2E pendinginfra-config#59rollout) - centralized
firstsun-dev/.githubCloudflare Worker CI/CD decision
Cloudflare Access rollout and E2E (ChatGPT OAuth, Service Token, concurrent use), datasource integrations, deployment caller, automated OpenAPI validation, and end-to-end ChatGPT authentication are not considered verified until actually tested.
pnpm install
pnpm run devHealth endpoint:
http://localhost:8787/health
MCP endpoint:
http://localhost:8787/anas
Test MCP locally with:
pnpm dlx @modelcontextprotocol/inspector@latestLocally, /anas/mcp requires an Access assertion that only Cloudflare Access can mint, so the test suite signs assertions with a generated key (see docs/mcp-authentication.md). Do not add an auth bypass for development. Production authentication must be verified against the deployed Worker through Access.
pnpm run checkThe final project check should also validate openapi.yaml with an OpenAPI 3.2-capable validator. Until that validator is actually wired in, OpenAPI automated validation remains pending.
For production auth, follow docs/cloudflare-access.md and verify Managed OAuth and Service Token (concurrently), blocked identities/tokens, and the origin 401 against the deployed system.
Do not make pnpm run deploy or locally copied Wrangler deployment commands the normal production CI path.
Production/development CI deployment must be invoked through the reusable workflow maintained by firstsun-dev/.github. The application repository should contain only the thin caller after package-manager compatibility, versioning, URLs, and Cloudflare bootstrap credentials have been resolved.
See docs/cicd.md.
Do not commit real Cloudflare resource IDs, database credentials, Google OAuth credentials, Access assertions, Service Token secrets, or private analytics payloads.
Start with:
openspec/project.md— project architecture and boundariesopenspec/specs/analytics-mcp/spec.md— baseline capability requirementsopenspec/changes/bootstrap-analytics-mcp/— bootstrap design/tasksopenspec/changes/support-dual-access-auth/— active concurrent Managed OAuth + Service Token authentication decisionopenspec/changes/use-mcp-api-key-auth/,openspec/changes/add-cloudflare-access-auth/— superseded decisions (historical only)openspec/changes/use-centralized-cf-worker-ci/— organization-managed Cloudflare Worker CI/CD decisionopenspec/changes/adopt-openapi-http-contract/— OpenAPI/Swagger HTTP contract decisiondocs/api.md— OpenAPI/Swagger ownership and drift policydocs/mcp-authentication.md— dual-mode Access authentication model and client setupdocs/cloudflare-access.md— Access ownership, rollout ordering, verification matrixdocs/credentials.md— credential ownership, storage, and exception policydocs/cicd.md— reusable deployment workflow policy
See AGENTS.md before making code or architecture changes.