This is page 5 of 27. Use http://codebase.md/cloudflare/mcp-server-cloudflare?lines=false&page={x} to view the full context.
# Directory Structure
```
├── .changeset
│ ├── config.json
│ └── README.md
├── .dockerignore
├── .editorconfig
├── .eslintrc.cjs
├── .github
│ ├── actions
│ │ └── setup
│ │ └── action.yml
│ ├── ISSUE_TEMPLATE
│ │ └── bug_report.md
│ └── workflows
│ ├── branches.yml
│ ├── main.yml
│ └── release.yml
├── .gitignore
├── .npmrc
├── .prettierignore
├── .prettierrc.cjs
├── .syncpackrc.cjs
├── .vscode
│ ├── extensions.json
│ ├── launch.json
│ ├── settings.json
│ └── tasks.json
├── apps
│ ├── ai-gateway
│ │ ├── .dev.vars.example
│ │ ├── .eslintrc.cjs
│ │ ├── CHANGELOG.md
│ │ ├── CONTRIBUTING.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── ai-gateway.app.ts
│ │ │ ├── ai-gateway.context.ts
│ │ │ ├── tools
│ │ │ │ └── ai-gateway.tools.ts
│ │ │ └── types.ts
│ │ ├── tsconfig.json
│ │ ├── types.d.ts
│ │ ├── vitest.config.ts
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.jsonc
│ ├── auditlogs
│ │ ├── .dev.vars.example
│ │ ├── .eslintrc.cjs
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── auditlogs.app.ts
│ │ │ ├── auditlogs.context.ts
│ │ │ └── tools
│ │ │ └── auditlogs.tools.ts
│ │ ├── tsconfig.json
│ │ ├── types.d.ts
│ │ ├── vitest.config.ts
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.jsonc
│ ├── autorag
│ │ ├── .dev.vars.example
│ │ ├── .eslintrc.cjs
│ │ ├── CHANGELOG.md
│ │ ├── CONTRIBUTING.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── autorag.app.ts
│ │ │ ├── autorag.context.ts
│ │ │ ├── tools
│ │ │ │ └── autorag.tools.ts
│ │ │ └── types.ts
│ │ ├── tsconfig.json
│ │ ├── types.d.ts
│ │ ├── vitest.config.ts
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.jsonc
│ ├── browser-rendering
│ │ ├── .dev.vars.example
│ │ ├── .eslintrc.cjs
│ │ ├── CHANGELOG.md
│ │ ├── CONTRIBUTING.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── browser.app.ts
│ │ │ ├── browser.context.ts
│ │ │ └── tools
│ │ │ └── browser.tools.ts
│ │ ├── tsconfig.json
│ │ ├── types.d.ts
│ │ ├── vitest.config.ts
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.jsonc
│ ├── cloudflare-one-casb
│ │ ├── .dev.vars.example
│ │ ├── .eslintrc.cjs
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── cf1-casb.app.ts
│ │ │ ├── cf1-casb.context.ts
│ │ │ └── tools
│ │ │ └── integrations.tools.ts
│ │ ├── tsconfig.json
│ │ ├── types.d.ts
│ │ ├── vitest.config.ts
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.jsonc
│ ├── demo-day
│ │ ├── .eslintrc.cjs
│ │ ├── CHANGELOG.md
│ │ ├── frontend
│ │ │ ├── index.html
│ │ │ ├── public
│ │ │ │ ├── anthropic.svg
│ │ │ │ ├── asana.svg
│ │ │ │ ├── atlassian.svg
│ │ │ │ ├── canva.svg
│ │ │ │ ├── cloudflare_logo.svg
│ │ │ │ ├── cloudflare.svg
│ │ │ │ ├── dina.jpg
│ │ │ │ ├── favicon-16x16.png
│ │ │ │ ├── favicon-32x32.png
│ │ │ │ ├── favicon.ico
│ │ │ │ ├── favicon.png
│ │ │ │ ├── intercom.svg
│ │ │ │ ├── linear.svg
│ │ │ │ ├── matt.jpg
│ │ │ │ ├── mcp_demo_day.svg
│ │ │ │ ├── mcpog.png
│ │ │ │ ├── more.svg
│ │ │ │ ├── paypal.svg
│ │ │ │ ├── pete.jpeg
│ │ │ │ ├── sentry.svg
│ │ │ │ ├── special_guest.png
│ │ │ │ ├── square.svg
│ │ │ │ ├── stripe.svg
│ │ │ │ ├── sunil.jpg
│ │ │ │ └── webflow.svg
│ │ │ ├── script.js
│ │ │ └── styles.css
│ │ ├── package.json
│ │ ├── src
│ │ │ └── demo-day.app.ts
│ │ ├── tsconfig.json
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.json
│ ├── dex-analysis
│ │ ├── .dev.vars.example
│ │ ├── .eslintrc.cjs
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── dex-analysis.app.ts
│ │ │ ├── dex-analysis.context.ts
│ │ │ ├── tools
│ │ │ │ └── dex-analysis.tools.ts
│ │ │ └── warp_diag_reader.ts
│ │ ├── tsconfig.json
│ │ ├── types.d.ts
│ │ ├── vitest.config.ts
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.jsonc
│ ├── dns-analytics
│ │ ├── .dev.vars.example
│ │ ├── .eslintrc.cjs
│ │ ├── CHANGELOG.md
│ │ ├── CONTRIBUTING.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── dns-analytics.app.ts
│ │ │ ├── dns-analytics.context.ts
│ │ │ └── tools
│ │ │ └── dex-analytics.tools.ts
│ │ ├── tsconfig.json
│ │ ├── types.d.ts
│ │ ├── vitest.config.ts
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.jsonc
│ ├── docs-ai-search
│ │ ├── .eslintrc.cjs
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── docs-ai-search.app.ts
│ │ │ └── docs-ai-search.context.ts
│ │ ├── tsconfig.json
│ │ ├── vitest.config.ts
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.jsonc
│ ├── docs-autorag
│ │ ├── .eslintrc.cjs
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── docs-autorag.app.ts
│ │ │ ├── docs-autorag.context.ts
│ │ │ └── tools
│ │ │ └── docs-autorag.tools.ts
│ │ ├── tsconfig.json
│ │ ├── vitest.config.ts
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.jsonc
│ ├── docs-vectorize
│ │ ├── .eslintrc.cjs
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── docs-vectorize.app.ts
│ │ │ └── docs-vectorize.context.ts
│ │ ├── tsconfig.json
│ │ ├── vitest.config.ts
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.jsonc
│ ├── graphql
│ │ ├── .eslintrc.cjs
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── graphql.app.ts
│ │ │ ├── graphql.context.ts
│ │ │ └── tools
│ │ │ └── graphql.tools.ts
│ │ ├── tsconfig.json
│ │ ├── types.d.ts
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.jsonc
│ ├── logpush
│ │ ├── .dev.vars.example
│ │ ├── .eslintrc.cjs
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── logpush.app.ts
│ │ │ ├── logpush.context.ts
│ │ │ └── tools
│ │ │ └── logpush.tools.ts
│ │ ├── tsconfig.json
│ │ ├── types.d.ts
│ │ ├── vitest.config.ts
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.jsonc
│ ├── radar
│ │ ├── .dev.vars.example
│ │ ├── .eslintrc.cjs
│ │ ├── CHANGELOG.md
│ │ ├── CONTRIBUTING.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── radar.app.ts
│ │ │ ├── radar.context.ts
│ │ │ ├── tools
│ │ │ │ ├── radar.tools.ts
│ │ │ │ └── url-scanner.tools.ts
│ │ │ ├── types
│ │ │ │ ├── radar.ts
│ │ │ │ └── url-scanner.ts
│ │ │ └── utils.ts
│ │ ├── tsconfig.json
│ │ ├── types.d.ts
│ │ ├── vitest.config.ts
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.jsonc
│ ├── sandbox-container
│ │ ├── .dev.vars.example
│ │ ├── .eslintrc.cjs
│ │ ├── CHANGELOG.md
│ │ ├── container
│ │ │ ├── fileUtils.spec.ts
│ │ │ ├── fileUtils.ts
│ │ │ ├── sandbox.container.app.ts
│ │ │ └── tsconfig.json
│ │ ├── CONTRIBUTING.md
│ │ ├── Dockerfile
│ │ ├── evals
│ │ │ ├── exec.eval.ts
│ │ │ ├── files.eval.ts
│ │ │ ├── initialize.eval.ts
│ │ │ └── utils.ts
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── server
│ │ │ ├── containerHelpers.ts
│ │ │ ├── containerManager.ts
│ │ │ ├── containerMcp.ts
│ │ │ ├── metrics.ts
│ │ │ ├── prompts.ts
│ │ │ ├── sandbox.server.app.ts
│ │ │ ├── sandbox.server.context.ts
│ │ │ ├── userContainer.ts
│ │ │ ├── utils.spec.ts
│ │ │ └── utils.ts
│ │ ├── shared
│ │ │ ├── consts.ts
│ │ │ └── schema.ts
│ │ ├── tsconfig.json
│ │ ├── types.d.ts
│ │ ├── vitest.config.evals.ts
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.jsonc
│ ├── workers-bindings
│ │ ├── .dev.vars.example
│ │ ├── .eslintrc.cjs
│ │ ├── .gitignore
│ │ ├── CHANGELOG.md
│ │ ├── CONTRIBUTING.md
│ │ ├── evals
│ │ │ ├── accounts.eval.ts
│ │ │ ├── hyperdrive.eval.ts
│ │ │ ├── kv_namespaces.eval.ts
│ │ │ ├── types.d.ts
│ │ │ └── utils.ts
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── bindings.app.ts
│ │ │ └── bindings.context.ts
│ │ ├── tsconfig.json
│ │ ├── vitest.config.evals.ts
│ │ ├── vitest.config.ts
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.jsonc
│ ├── workers-builds
│ │ ├── .dev.vars.example
│ │ ├── .eslintrc.cjs
│ │ ├── CHANGELOG.md
│ │ ├── CONTRIBUTING.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── tools
│ │ │ │ └── workers-builds.tools.ts
│ │ │ ├── workers-builds.app.ts
│ │ │ └── workers-builds.context.ts
│ │ ├── tsconfig.json
│ │ ├── types.d.ts
│ │ ├── vite.config.mts
│ │ ├── vitest.config.ts
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.jsonc
│ └── workers-observability
│ ├── .dev.vars.example
│ ├── .eslintrc.cjs
│ ├── CHANGELOG.md
│ ├── CONTRIBUTING.md
│ ├── package.json
│ ├── README.md
│ ├── src
│ │ ├── tools
│ │ │ └── workers-observability.tools.ts
│ │ ├── workers-observability.app.ts
│ │ └── workers-observability.context.ts
│ ├── tsconfig.json
│ ├── types.d.ts
│ ├── vitest.config.ts
│ ├── worker-configuration.d.ts
│ └── wrangler.jsonc
├── CONTRIBUTING.md
├── implementation-guides
│ ├── evals.md
│ ├── tools.md
│ └── type-validators.md
├── LICENSE
├── package.json
├── packages
│ ├── eslint-config
│ │ ├── CHANGELOG.md
│ │ ├── default.cjs
│ │ ├── package.json
│ │ └── README.md
│ ├── eval-tools
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── runTask.ts
│ │ │ ├── scorers.ts
│ │ │ └── test-models.ts
│ │ ├── tsconfig.json
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.json
│ ├── mcp-common
│ │ ├── .eslintrc.cjs
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── api
│ │ │ │ ├── account.api.ts
│ │ │ │ ├── cf1-integration.api.ts
│ │ │ │ ├── workers-builds.api.ts
│ │ │ │ ├── workers-observability.api.ts
│ │ │ │ ├── workers.api.ts
│ │ │ │ └── zone.api.ts
│ │ │ ├── api-handler.ts
│ │ │ ├── api-token-mode.ts
│ │ │ ├── cloudflare-api.ts
│ │ │ ├── cloudflare-auth.ts
│ │ │ ├── cloudflare-oauth-handler.ts
│ │ │ ├── config.ts
│ │ │ ├── constants.ts
│ │ │ ├── durable-kv-store.ts
│ │ │ ├── durable-objects
│ │ │ │ └── user_details.do.ts
│ │ │ ├── env.ts
│ │ │ ├── format.spec.ts
│ │ │ ├── format.ts
│ │ │ ├── get-props.ts
│ │ │ ├── mcp-error.ts
│ │ │ ├── poll.ts
│ │ │ ├── prompts
│ │ │ │ ├── docs-ai-search.prompts.ts
│ │ │ │ └── docs-vectorize.prompts.ts
│ │ │ ├── scopes.ts
│ │ │ ├── sentry.ts
│ │ │ ├── server.ts
│ │ │ ├── tools
│ │ │ │ ├── account.tools.ts
│ │ │ │ ├── d1.tools.ts
│ │ │ │ ├── docs-ai-search.tools.ts
│ │ │ │ ├── docs-vectorize.tools.ts
│ │ │ │ ├── hyperdrive.tools.ts
│ │ │ │ ├── kv_namespace.tools.ts
│ │ │ │ ├── r2_bucket.tools.ts
│ │ │ │ ├── worker.tools.ts
│ │ │ │ └── zone.tools.ts
│ │ │ ├── types
│ │ │ │ ├── cf1-integrations.types.ts
│ │ │ │ ├── cloudflare-mcp-agent.types.ts
│ │ │ │ ├── d1.types.ts
│ │ │ │ ├── hyperdrive.types.ts
│ │ │ │ ├── kv_namespace.types.ts
│ │ │ │ ├── r2_bucket.types.ts
│ │ │ │ ├── shared.types.ts
│ │ │ │ ├── tools.types.ts
│ │ │ │ ├── workers-builds.types.ts
│ │ │ │ ├── workers-logs.types.ts
│ │ │ │ └── workers.types.ts
│ │ │ ├── utils.spec.ts
│ │ │ ├── utils.ts
│ │ │ ├── v4-api.ts
│ │ │ └── workers-oauth-utils.ts
│ │ ├── tests
│ │ │ └── utils
│ │ │ └── cloudflare-mock.ts
│ │ ├── tsconfig.json
│ │ ├── types.d.ts
│ │ ├── vitest.config.ts
│ │ └── worker-configuration.d.ts
│ ├── mcp-observability
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── analytics-engine.ts
│ │ │ ├── index.ts
│ │ │ └── metrics.ts
│ │ ├── tsconfig.json
│ │ └── worker-configuration.d.ts
│ ├── tools
│ │ ├── .eslintrc.cjs
│ │ ├── bin
│ │ │ ├── run-changeset-new
│ │ │ ├── run-eslint-workers
│ │ │ ├── run-fix-deps
│ │ │ ├── run-tsc
│ │ │ ├── run-turbo
│ │ │ ├── run-vitest
│ │ │ ├── run-vitest-ci
│ │ │ ├── run-wrangler-deploy
│ │ │ ├── run-wrangler-types
│ │ │ └── runx
│ │ ├── CHANGELOG.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── bin
│ │ │ │ └── runx.ts
│ │ │ ├── changesets.spec.ts
│ │ │ ├── changesets.ts
│ │ │ ├── cmd
│ │ │ │ └── deploy-published-packages.ts
│ │ │ ├── proc.ts
│ │ │ ├── test
│ │ │ │ ├── fixtures
│ │ │ │ │ └── changesets
│ │ │ │ │ ├── empty
│ │ │ │ │ │ └── .gitkeep
│ │ │ │ │ ├── invalid-json
│ │ │ │ │ │ └── published-packages.json
│ │ │ │ │ ├── invalid-schema
│ │ │ │ │ │ └── published-packages.json
│ │ │ │ │ └── valid
│ │ │ │ │ └── published-packages.json
│ │ │ │ └── setup.ts
│ │ │ └── tsconfig.ts
│ │ ├── tsconfig.json
│ │ └── vitest.config.ts
│ └── typescript-config
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── tools.json
│ ├── workers-lib.json
│ └── workers.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── README.md
├── server.json
├── tsconfig.json
├── turbo.json
└── vitest.workspace.ts
```
# Files
--------------------------------------------------------------------------------
/packages/mcp-common/src/types/r2_bucket.types.ts:
--------------------------------------------------------------------------------
```typescript
/**
* This file contains the validators for the r2 bucket tools.
*/
import { z } from 'zod'
import type {
CORSDeleteParams,
CORSUpdateParams,
EventNotificationDeleteParams,
EventNotificationGetParams,
EventNotificationUpdateParams,
LockGetParams,
LockUpdateParams,
SippyGetParams,
SippyUpdateParams,
} from 'cloudflare/resources/r2/buckets.mjs'
import type {
CustomCreateParams,
CustomDeleteParams,
CustomListParams,
CustomUpdateParams,
} from 'cloudflare/resources/r2/buckets/domains.mjs'
import type {
ProviderParam,
Sippy,
SippyDeleteParams,
} from 'cloudflare/resources/r2/buckets/sippy.mjs'
import type { CustomGetParams } from 'cloudflare/resources/zero-trust/devices/policies.mjs'
import type {
BucketCreateParams,
TemporaryCredentialCreateParams,
} from 'cloudflare/src/resources/r2.js'
import type { CORSGetParams } from 'cloudflare/src/resources/r2/buckets.js'
import type { CustomGetResponse } from 'cloudflare/src/resources/r2/buckets/domains.js'
export const BucketNameSchema: z.ZodType<BucketCreateParams['name']> = z
.string()
.describe('The name of the r2 bucket')
export const BucketListCursorParam = z
.string()
.nullable()
.optional()
.describe(
'Query param: Pagination cursor received during the last List Buckets call. R2 buckets are paginated using cursors instead of page numbers.'
)
export const BucketListDirectionParam = z
.enum(['asc', 'desc'])
.nullable()
.optional()
.describe('Direction to order buckets')
export const BucketListNameContainsParam = z
.string()
.nullable()
.optional()
.describe(
'Bucket names to filter by. Only buckets with this phrase in their name will be returned.'
)
export const BucketListStartAfterParam = z
.string()
.nullable()
.optional()
.describe('Bucket name to start searching after. Buckets are ordered lexicographically.')
export const AllowedMethodsEnum: z.ZodType<CORSUpdateParams.Rule['allowed']['methods']> = z.array(
z.union([
z.literal('GET'),
z.literal('PUT'),
z.literal('POST'),
z.literal('DELETE'),
z.literal('HEAD'),
])
)
export const JurisdictionEnum: z.ZodType<CORSUpdateParams['jurisdiction']> = z
.enum(['default', 'eu', 'fedramp'])
.describe(
'Use Jurisdictional Restrictions when you need to ensure data is stored and processed within a jurisdiction to meet data residency requirements, including local regulations such as the GDPR or FedRAMP.'
)
// CORS ZOD SCHEMAS
export const CorsAllowedSchema: z.ZodType<CORSUpdateParams.Rule['allowed']> = z
.object({
methods: AllowedMethodsEnum.describe(
'Specifies the value for the Access-Control-Allow-Methods header'
),
origins: z
.array(z.string())
.describe('Specifies the value for the Access-Control-Allow-Origin header'),
headers: z
.array(z.string())
.optional()
.describe('Specifies the value for the Access-Control-Allow-Headers header'),
})
.describe('Object specifying allowed origins, methods and headers for this CORS rule')
export const CorsRuleSchema: z.ZodType<CORSUpdateParams.Rule> = z
.object({
allowed: CorsAllowedSchema,
id: z.string().optional().describe('Identifier for this rule'),
exposeHeaders: z
.array(z.string())
.optional()
.describe('Headers that can be exposed back and accessed by JavaScript'),
maxAgeSeconds: z
.number()
.optional()
.describe('Time in seconds browsers can cache CORS preflight responses (max 86400)'),
})
.describe('Object specifying allowed origins, methods and headers for this CORS rule')
export const CorsRulesSchema: z.ZodType<Omit<CORSUpdateParams, 'account_id'>> = z
.object({
rules: z.array(CorsRuleSchema).optional().describe('Array of CORS rules for the bucket'),
jurisdiction: JurisdictionEnum.optional(),
})
.describe('CORS configuration for the bucket')
export const CorsGetParamsSchema: z.ZodType<Omit<CORSGetParams, 'account_id'>> = z
.object({
jurisdiction: JurisdictionEnum.optional(),
})
.describe('Params for getting CORS configuration for an R2 bucket')
export const CorsDeleteParamsSchema: z.ZodType<Omit<CORSDeleteParams, 'account_id'>> = z
.object({
jurisdiction: JurisdictionEnum.optional(),
})
.describe('Params for deleting CORS configuration for an R2 bucket')
// TEMPORARY CREDENTIALS ZOD SCHEMAS
export const TemporaryCredentialsCreateParamsSchema: z.ZodType<
Omit<TemporaryCredentialCreateParams, 'account_id'>
> = z
.object({
bucket: BucketNameSchema,
ttlSeconds: z.number().describe('The time to live for the temporary credentials'),
permission: z
.enum(['admin-read-write', 'admin-read-only', 'object-read-write', 'object-read-only'])
.describe('The permission for the temporary credentials'),
objects: z
.array(z.string())
.optional()
.describe('The objects to scope the temporary credentials to'),
prefixes: z
.array(z.string())
.optional()
.describe('The prefixes to scope the temporary credentials to'),
parentAccessKeyId: z.string().describe('The parent access key id to use for signing'),
})
.describe('Temporary credentials for the bucket')
// CUSTOM DOMAIN ZOD SCHEMAS
export const CustomDomainListParamsSchema: z.ZodType<Omit<CustomListParams, 'account_id'>> = z
.object({
jurisdiction: JurisdictionEnum.optional(),
})
.describe('Params for listing custom domains for an R2 bucket')
export const CustomDomainNameSchema: z.ZodType<CustomGetResponse['domain']> = z
.string()
.describe('The custom domain')
export const CustomDomainGetParamsSchema: z.ZodType<Omit<CustomGetParams, 'account_id'>> = z
.object({
jurisdiction: JurisdictionEnum.optional(),
})
.describe('Params for getting a custom domain for an R2 bucket')
export const CustomDomainCreateParamsSchema: z.ZodType<Omit<CustomCreateParams, 'account_id'>> = z
.object({
domain: CustomDomainNameSchema,
enabled: z
.boolean()
.describe(
'Whether to enable public bucket access at the custom domain. If undefined, the domain will be enabled.'
),
zoneId: z.string().describe('The zone id of the custom domain'),
minTLS: z
.enum(['1.0', '1.1', '1.2', '1.3'])
.optional()
.describe(
'The minimum TLS version the custom domain will accept for incoming connections. If not set, defaults to 1.0.'
),
jurisdiction: JurisdictionEnum.optional(),
})
.describe('Params for creating a custom domain for an R2 bucket')
export const CustomDomainDeleteParamsSchema: z.ZodType<Omit<CustomDeleteParams, 'account_id'>> = z
.object({
jurisdiction: JurisdictionEnum.optional(),
})
.describe('Params for deleting a custom domain for an R2 bucket')
export const CustomDomainUpdateParamsSchema: z.ZodType<Omit<CustomUpdateParams, 'account_id'>> = z
.object({
enabled: z
.boolean()
.describe(
'Whether to enable public bucket access at the custom domain. If undefined, the domain will be enabled.'
),
zoneId: z.string().describe('The zone id of the custom domain'),
minTLS: z
.enum(['1.0', '1.1', '1.2', '1.3'])
.optional()
.describe(
'The minimum TLS version the custom domain will accept for incoming connections. If not set, defaults to 1.0.'
),
jurisdiction: JurisdictionEnum.optional(),
})
.describe('Params for updating a custom domain for an R2 bucket')
// EVENT NOTIFICATION ZOD SCHEMAS
export const QueueIdSchema = z.string().describe('The queue id of the event notification')
const EventActionEnum = z.enum([
'PutObject',
'CopyObject',
'DeleteObject',
'CompleteMultipartUpload',
'LifecycleDeletion',
])
export const EventNotificationRuleSchema: z.ZodType<EventNotificationUpdateParams.Rule> = z
.object({
actions: z
.array(EventActionEnum)
.describe('Array of R2 object actions that will trigger notifications'),
description: z
.string()
.optional()
.describe(
'A description that can be used to identify the event notification rule after creation'
),
prefix: z
.string()
.optional()
.describe('Notifications will be sent only for objects with this prefix'),
suffix: z
.string()
.optional()
.describe('Notifications will be sent only for objects with this suffix'),
})
.describe('Rule configuration for event notifications')
// Main EventNotificationUpdateParams schema
export const EventNotificationUpdateParamsSchema: z.ZodType<
Omit<EventNotificationUpdateParams, 'account_id'>
> = z
.object({
rules: z
.array(EventNotificationRuleSchema)
.optional()
.describe('Array of rules to drive notifications'),
jurisdiction: JurisdictionEnum.optional(),
})
.describe('Parameters for updating event notification configuration')
export const EventNotificationGetParamsSchema: z.ZodType<
Omit<EventNotificationGetParams, 'account_id'>
> = z
.object({
jurisdiction: JurisdictionEnum.optional(),
})
.describe('Params for getting event notifications for an R2 bucket')
export const EventNotificationDeleteParamsSchema: z.ZodType<
Omit<EventNotificationDeleteParams, 'account_id'>
> = z
.object({
jurisdiction: JurisdictionEnum.optional(),
})
.describe('Params for deleting event notifications for an R2 bucket')
// LOCK ZOD SCHEMAS
export const LockGetParamsSchema: z.ZodType<Omit<LockGetParams, 'account_id'>> = z
.object({
jurisdiction: JurisdictionEnum.optional(),
})
.describe('Params for getting locks for an R2 bucket')
// Condition: Age-based
const R2LockRuleAgeCondition: z.ZodType<LockUpdateParams.Rule.R2LockRuleAgeCondition> = z
.object({
maxAgeSeconds: z
.number()
.describe('Condition to apply a lock rule to an object for how long in seconds'),
type: z.literal('Age').describe('Age-based condition'),
})
.describe('Condition to apply a lock rule to an object for how long in seconds')
// Condition: Date-based
const R2LockRuleDateCondition: z.ZodType<LockUpdateParams.Rule.R2LockRuleDateCondition> = z
.object({
date: z.string().describe('Condition to apply a lock rule to an object until a specific date'),
type: z.literal('Date').describe('Date-based condition'),
})
.describe('Condition to apply a lock rule to an object until a specific date')
// Condition: Indefinite
const R2LockRuleIndefiniteCondition: z.ZodType<LockUpdateParams.Rule.R2LockRuleIndefiniteCondition> =
z
.object({
type: z.literal('Indefinite').describe('Indefinite condition'),
})
.describe('Condition to apply a lock rule indefinitely')
// Union of all possible condition types
const LockRuleCondition = z
.union([R2LockRuleAgeCondition, R2LockRuleDateCondition, R2LockRuleIndefiniteCondition])
.describe('Condition to apply a lock rule to an object')
export const LockRuleSchema: z.ZodType<LockUpdateParams.Rule> = z
.object({
id: z.string().describe('Unique identifier for this rule'),
condition: LockRuleCondition,
enabled: z.boolean().describe('Whether or not this rule is in effect'),
prefix: z
.string()
.optional()
.describe(
'Rule will only apply to objects/uploads in the bucket that start with the given prefix; an empty prefix can be provided to scope rule to all objects/uploads'
),
})
.describe('Lock rule definition')
// Main schema
export const LockUpdateParamsSchema: z.ZodType<Omit<LockUpdateParams, 'account_id'>> = z
.object({
rules: z.array(LockRuleSchema).optional().describe('Body param: Optional list of lock rules'),
jurisdiction: JurisdictionEnum.optional(),
})
.describe('Lock update parameters')
// SIPPY ZOD SCHEMAS
export const SippyGetParamsSchema: z.ZodType<Omit<SippyGetParams, 'account_id'>> = z
.object({
jurisdiction: JurisdictionEnum.optional(),
})
.describe('Params for getting sippy configuration for an R2 bucket')
const ProviderParam: z.ZodType<ProviderParam> = z.literal('r2').describe('Provider parameter')
// Common destination schema for reuse
const BaseDestination: z.ZodType<Sippy.Destination> = z.object({
accessKeyId: z
.string()
.optional()
.describe(
'ID of a Cloudflare API token. This is the value labelled "Access Key ID" when creating an API token from the R2 dashboard. Sippy will use this token when writing objects to R2, so it is best to scope this token to the bucket you\'re enabling Sippy for.'
),
secretAccessKey: z
.string()
.optional()
.describe(
'Value of a Cloudflare API token. This is the value labelled "Secret Access Key" when creating an API token from the R2 dashboard. Sippy will use this token when writing objects to R2, so it is best to scope this token to the bucket you\'re enabling Sippy for.'
),
provider: ProviderParam,
})
// AWS Source schema
const AwsSourceSchema: z.ZodType<SippyUpdateParams.R2EnableSippyAws.Source> = z
.object({
accessKeyId: z
.string()
.optional()
.describe('Access Key ID of an IAM credential (ideally scoped to a single S3 bucket)'),
secretAccessKey: z
.string()
.optional()
.describe('Secret Access Key of an IAM credential (ideally scoped to a single S3 bucket)'),
bucket: z.string().optional().describe('Name of the AWS S3 bucket'),
region: z.string().optional().describe('Name of the AWS availability zone'),
provider: z.literal('aws').optional().describe('AWS provider indicator'),
})
.describe('AWS S3 bucket to copy objects from')
// GCS Source schema
const GcsSourceSchema: z.ZodType<SippyUpdateParams.R2EnableSippyGcs.Source> = z
.object({
bucket: z.string().optional().describe('Name of the GCS bucket'),
clientEmail: z
.string()
.optional()
.describe('Client email of an IAM credential (ideally scoped to a single GCS bucket)'),
privateKey: z
.string()
.optional()
.describe('Private Key of an IAM credential (ideally scoped to a single GCS bucket)'),
provider: z.literal('gcs').optional().describe('GCS provider indicator'),
})
.describe('GCS bucket to copy objects from')
// R2EnableSippyAws schema
const R2EnableSippyAwsSchema: z.ZodType<Omit<SippyUpdateParams.R2EnableSippyAws, 'account_id'>> = z
.object({
destination: BaseDestination.describe('R2 bucket to copy objects to').optional(),
source: AwsSourceSchema.optional(),
jurisdiction: JurisdictionEnum.optional(),
})
.describe('Parameters to enable Sippy with AWS as source')
// R2EnableSippyGcs schema
const R2EnableSippyGcsSchema: z.ZodType<Omit<SippyUpdateParams.R2EnableSippyGcs, 'account_id'>> = z
.object({
destination: BaseDestination.describe('R2 bucket to copy objects to').optional(),
source: GcsSourceSchema.optional(),
jurisdiction: JurisdictionEnum.optional(),
})
.describe('Parameters to enable Sippy with GCS as source')
// Combined SippyUpdateParams namespace schema
export const SippyUpdateParamsSchema = z.union([R2EnableSippyAwsSchema, R2EnableSippyGcsSchema])
export const SippyDeleteParamsSchema: z.ZodType<Omit<SippyDeleteParams, 'account_id'>> = z
.object({
jurisdiction: JurisdictionEnum.optional(),
})
.describe('Parameters to delete Sippy for an R2 bucket')
```
--------------------------------------------------------------------------------
/apps/demo-day/frontend/index.html:
--------------------------------------------------------------------------------
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Primary Meta Tags -->
<title>MCP Demo Day - Preview the Future of Agentic Software</title>
<meta
name="description"
content="Join us on May 1st, 2025 to witness groundbreaking demos from companies like Atlassian, Linear, Stripe, and more. Experience the next evolution of AI-powered software."
/>
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://demo-day.mcp.cloudflare.com/" />
<meta property="og:title" content="MCP Demo Day - Preview the Future of Agentic Software" />
<meta
property="og:description"
content="Join us on May 1st, 2025 to witness groundbreaking demos from companies like Atlassian, Linear, Stripe, and more. Experience the next evolution of AI-powered software."
/>
<meta property="og:image" content="https://demo-day.mcp.cloudflare.com/public/mcpog.png" />
<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="MCP Demo Day - Preview the Future of Agentic Software" />
<meta
name="twitter:description"
content="Join us on May 1st, 2025 to witness groundbreaking demos from companies like Atlassian, Linear, Stripe, and more. Experience the next evolution of AI-powered software."
/>
<meta name="twitter:image" content="https://demo-day.mcp.cloudflare.com/public/mcpog.png" />
<link rel="icon" type="image/png" href="public/favicon.ico" />
<link rel="icon" type="image/png" sizes="32x32" href="public/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="public/favicon-16x16.png" />
<link rel="stylesheet" href="styles.css" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
</head>
<body>
<div class="page-wrapper">
<main class="container">
<section class="left-panel">
<div class="header">
<div class="logo">
<img src="public/mcp_demo_day.svg" alt="MCP Logo" class="cloud-logo" />
</div>
<div class="date-time" onclick="document.getElementById('calendarDialog').showModal()">
<button class="calendar-trigger" aria-label="Add to calendar">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path
d="M6 5V3M14 5V3M5 9H15M5 7.5H15M4.2 17H15.8C16.4627 17 17 16.4627 17 15.8V5.2C17 4.53726 16.4627 4 15.8 4H4.2C3.53726 4 3 4.53726 3 5.2V15.8C3 16.4627 3.53726 17 4.2 17Z"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</button>
<div class="date-time-text">
<h2>MAY 1, 2025</h2>
<h3>ONLINE 10:00 AM PT / 1 PM ET</h3>
</div>
</div>
</div>
<div class="content">
<h1>Preview the Future<br />of Agentic Software</h1>
<p class="description">
Come see how the world's most innovative platforms have connected agents to their
services with MCP to build a new class of product experiences.
</p>
<div class="input-group">
<a class="notify-btn" href="https://www.youtube.com/watch?v=njBGqr-BU54">
Watch the stream
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path
d="M2.5 8H13.5M13.5 8L9 3.5M13.5 8L9 12.5"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</a>
<div class="success-message">
<div class="success-text">
<svg width="18" height="18" viewBox="0 0 18 18" fill="none">
<path
d="M15 5L7 13L3 9"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
You're in! See you on May 1st
</div>
<div class="calendar-actions">
<button class="calendar-action" data-calendar-type="google">
<svg
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
width="100"
height="100"
viewBox="0,0,256,256"
style="fill: #ffffff"
>
<g
fill="#ffffff"
fill-rule="nonzero"
stroke="none"
stroke-width="1"
stroke-linecap="butt"
stroke-linejoin="miter"
stroke-miterlimit="10"
stroke-dasharray=""
stroke-dashoffset="0"
font-family="none"
font-weight="none"
font-size="none"
text-anchor="none"
style="mix-blend-mode: normal"
>
<g transform="scale(5.12,5.12)">
<path
d="M25.99609,48c-12.68359,0 -23.00391,-10.31641 -23.00391,-23c0,-12.68359 10.32031,-23 23.00391,-23c5.74609,0 11.24609,2.12891 15.49219,5.99609l0.77344,0.70703l-7.58594,7.58594l-0.70312,-0.60156c-2.22656,-1.90625 -5.05859,-2.95703 -7.97656,-2.95703c-6.76562,0 -12.27344,5.50391 -12.27344,12.26953c0,6.76563 5.50781,12.26953 12.27344,12.26953c4.87891,0 8.73438,-2.49219 10.55078,-6.73828h-11.55078v-10.35547l22.55078,0.03125l0.16797,0.79297c1.17578,5.58203 0.23438,13.79297 -4.53125,19.66797c-3.94531,4.86328 -9.72656,7.33203 -17.1875,7.33203z"
></path>
</g>
</g>
</svg>
Google Calendar
</button>
<button class="calendar-action" data-calendar-type="outlook">
<svg
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
width="100"
height="100"
viewBox="0,0,256,256"
style="fill: #ffffff"
>
<g
fill="#ffffff"
fill-rule="nonzero"
stroke="none"
stroke-width="1"
stroke-linecap="butt"
stroke-linejoin="miter"
stroke-miterlimit="10"
stroke-dasharray=""
stroke-dashoffset="0"
font-family="none"
font-weight="none"
font-size="none"
text-anchor="none"
style="mix-blend-mode: normal"
>
<g transform="scale(5.12,5.12)">
<path
d="M5,4c-0.552,0 -1,0.447 -1,1v19h20v-20zM26,4v20h20v-19c0,-0.553 -0.448,-1 -1,-1zM4,26v19c0,0.553 0.448,1 1,1h19v-20zM26,26v20h19c0.552,0 1,-0.447 1,-1v-19z"
></path>
</g>
</g>
</svg>
Outlook
</button>
<button class="calendar-action" data-calendar-type="ics">
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
>
<path
d="M12 15V3m0 12l-4-4m4 4l4-4M3 17l.6 2.6c.2.8.8 1.4 1.6 1.4h13.6c.8 0 1.4-.6 1.6-1.4l.6-2.6"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
Download .ics
</button>
</div>
</div>
</div>
<div class="attendees">
<div class="attendee-avatars">
<div class="attendee-avatar" data-tooltip="Special Guest">
<img src="public/special_guest.png" alt="Special Guest" />
</div>
<div class="attendee-avatar" data-tooltip="Sunil Pai">
<img src="public/sunil.jpg" alt="Sunil Pai" />
</div>
<div class="attendee-avatar" data-tooltip="Dina Kozlov">
<img src="public/dina.jpg" alt="Dina Kozlov" />
</div>
</div>
<span class="attendee-count"><strong>+ all the other cool kids</strong> went</span>
</div>
</div>
</section>
<section class="right-panel">
<div class="demos-section">
<h4>DEMOS FROM</h4>
<ul class="demo-companies">
<li>Asana</li>
<li>Atlassian</li>
<li>Cloudflare</li>
<li>Intercom</li>
<li>Linear</li>
<li>Paypal</li>
<li>Sentry</li>
<li>Square</li>
<li>Stripe</li>
<li>Webflow</li>
<li>+ More</li>
</ul>
</div>
</section>
</main>
<footer>
<div class="footer-left">
<img src="public/cloudflare_logo.svg" alt="Cloudflare" class="cloudflare-logo" />
<div class="footer-links"></div>
</div>
<a
href="https://developers.cloudflare.com/agents/guides/remote-mcp-server/?utm_source=website&utm_medium=reg+page&utm_campaign=MCP+Demo+Day"
target="_blank"
class="build-btn"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="24" height="24">
<path d="M8.16 23h21.177v-5.86l-4.023-2.307-.694-.3-16.46.113z" fill="transparent" />
<path
d="M22.012 22.222c.197-.675.122-1.294-.206-1.754-.3-.422-.807-.666-1.416-.694l-11.545-.15c-.075 0-.14-.038-.178-.094s-.047-.13-.028-.206c.038-.113.15-.197.272-.206l11.648-.15c1.38-.066 2.88-1.182 3.404-2.55l.666-1.735a.38.38 0 0 0 .02-.225c-.75-3.395-3.78-5.927-7.4-5.927-3.34 0-6.17 2.157-7.184 5.15-.657-.488-1.5-.75-2.392-.666-1.604.16-2.9 1.444-3.048 3.048a3.58 3.58 0 0 0 .084 1.191A4.84 4.84 0 0 0 0 22.1c0 .234.02.47.047.703.02.113.113.197.225.197H21.58a.29.29 0 0 0 .272-.206l.16-.572z"
fill="currentColor"
/>
<path
d="M25.688 14.803l-.32.01c-.075 0-.14.056-.17.13l-.45 1.566c-.197.675-.122 1.294.206 1.754.3.422.807.666 1.416.694l2.457.15c.075 0 .14.038.178.094s.047.14.028.206c-.038.113-.15.197-.272.206l-2.56.15c-1.388.066-2.88 1.182-3.404 2.55l-.188.478c-.038.094.028.188.13.188h8.797a.23.23 0 0 0 .225-.169A6.41 6.41 0 0 0 32 21.106a6.32 6.32 0 0 0-6.312-6.302"
fill="currentColor"
/>
</svg>
Build An MCP Server
</a>
</footer>
</div>
<dialog id="calendarDialog" class="calendar-popover">
<h4>Add to Calendar</h4>
<button class="close-button" onclick="document.getElementById('calendarDialog').close()">
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1"
>
<path d="M6 18L18 6M6 6l12 12" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</button>
<div class="calendar-options">
<button class="calendar-option" data-calendar-type="google">
<svg
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
width="100"
height="100"
viewBox="0,0,256,256"
style="fill: #ffffff"
>
<g
fill="#ffffff"
fill-rule="nonzero"
stroke="none"
stroke-width="1"
stroke-linecap="butt"
stroke-linejoin="miter"
stroke-miterlimit="10"
stroke-dasharray=""
stroke-dashoffset="0"
font-family="none"
font-weight="none"
font-size="none"
text-anchor="none"
style="mix-blend-mode: normal"
>
<g transform="scale(5.12,5.12)">
<path
d="M25.99609,48c-12.68359,0 -23.00391,-10.31641 -23.00391,-23c0,-12.68359 10.32031,-23 23.00391,-23c5.74609,0 11.24609,2.12891 15.49219,5.99609l0.77344,0.70703l-7.58594,7.58594l-0.70312,-0.60156c-2.22656,-1.90625 -5.05859,-2.95703 -7.97656,-2.95703c-6.76562,0 -12.27344,5.50391 -12.27344,12.26953c0,6.76563 5.50781,12.26953 12.27344,12.26953c4.87891,0 8.73438,-2.49219 10.55078,-6.73828h-11.55078v-10.35547l22.55078,0.03125l0.16797,0.79297c1.17578,5.58203 0.23438,13.79297 -4.53125,19.66797c-3.94531,4.86328 -9.72656,7.33203 -17.1875,7.33203z"
></path>
</g>
</g>
</svg>
<span>Google Calendar</span>
</button>
<button class="calendar-option" data-calendar-type="outlook">
<svg
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
width="100"
height="100"
viewBox="0,0,256,256"
style="fill: #ffffff"
>
<g
fill="#ffffff"
fill-rule="nonzero"
stroke="none"
stroke-width="1"
stroke-linecap="butt"
stroke-linejoin="miter"
stroke-miterlimit="10"
stroke-dasharray=""
stroke-dashoffset="0"
font-family="none"
font-weight="none"
font-size="none"
text-anchor="none"
style="mix-blend-mode: normal"
>
<g transform="scale(5.12,5.12)">
<path
d="M5,4c-0.552,0 -1,0.447 -1,1v19h20v-20zM26,4v20h20v-19c0,-0.553 -0.448,-1 -1,-1zM4,26v19c0,0.553 0.448,1 1,1h19v-20zM26,26v20h19c0.552,0 1,-0.447 1,-1v-19z"
></path>
</g>
</g>
</svg>
<span>Outlook Calendar</span>
</button>
<button class="calendar-option" data-calendar-type="apple">
<svg
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
width="100"
height="100"
viewBox="0,0,256,256"
style="fill: #ffffff"
>
<g
fill="#ffffff"
fill-rule="nonzero"
stroke="none"
stroke-width="1"
stroke-linecap="butt"
stroke-linejoin="miter"
stroke-miterlimit="10"
stroke-dasharray=""
stroke-dashoffset="0"
font-family="none"
font-weight="none"
font-size="none"
text-anchor="none"
style="mix-blend-mode: normal"
>
<g transform="scale(5.12,5.12)">
<path
d="M44.52734,34.75c-1.07812,2.39453 -1.59766,3.46484 -2.98437,5.57813c-1.94141,2.95313 -4.67969,6.64063 -8.0625,6.66406c-3.01172,0.02734 -3.78906,-1.96484 -7.87891,-1.92969c-4.08594,0.01953 -4.9375,1.96875 -7.95312,1.9375c-3.38672,-0.03125 -5.97656,-3.35156 -7.91797,-6.30078c-5.42969,-8.26953 -6.00391,-17.96484 -2.64844,-23.12109c2.375,-3.65625 6.12891,-5.80469 9.65625,-5.80469c3.59375,0 5.85156,1.97266 8.82031,1.97266c2.88281,0 4.63672,-1.97656 8.79297,-1.97656c3.14063,0 6.46094,1.71094 8.83594,4.66406c-7.76562,4.25781 -6.50391,15.34766 1.33984,18.31641zM31.19531,8.46875c1.51172,-1.94141 2.66016,-4.67969 2.24219,-7.46875c-2.46484,0.16797 -5.34766,1.74219 -7.03125,3.78125c-1.52734,1.85938 -2.79297,4.61719 -2.30078,7.28516c2.69141,0.08594 5.47656,-1.51953 7.08984,-3.59766z"
></path>
</g>
</g>
</svg>
<span>Apple Calendar</span>
</button>
<button class="calendar-option" data-calendar-type="ics">
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1"
>
<path
d="M12 15V3m0 12l-4-4m4 4l4-4M2 17l.621 2.485A2 2 0 004.561 21h14.878a2 2 0 001.94-1.515L22 17"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<span>Download .ics File</span>
</button>
</div>
</dialog>
<script src="script.js"></script>
</body>
</html>
```
--------------------------------------------------------------------------------
/apps/radar/src/tools/radar.tools.ts:
--------------------------------------------------------------------------------
```typescript
import { z } from 'zod'
import { getCloudflareClient } from '@repo/mcp-common/src/cloudflare-api'
import { getProps } from '@repo/mcp-common/src/get-props'
import {
PaginationLimitParam,
PaginationOffsetParam,
} from '@repo/mcp-common/src/types/shared.types'
import {
AiDimensionParam,
AsnArrayParam,
AsnParam,
AsOrderByParam,
ContinentArrayParam,
DateEndArrayParam,
DateEndParam,
DateListParam,
DateRangeArrayParam,
DateRangeParam,
DateStartArrayParam,
DateStartParam,
DnsDimensionParam,
DomainParam,
DomainRankingTypeParam,
EmailRoutingDimensionParam,
EmailSecurityDimensionParam,
HttpDimensionParam,
InternetQualityMetricParam,
InternetServicesCategoryParam,
InternetSpeedDimensionParam,
InternetSpeedOrderByParam,
IpParam,
L3AttackDimensionParam,
L7AttackDimensionParam,
LocationArrayParam,
LocationListParam,
LocationParam,
} from '../types/radar'
import { resolveAndInvoke } from '../utils'
import type { RadarMCP } from '../radar.app'
export function registerRadarTools(agent: RadarMCP) {
agent.server.tool(
'list_autonomous_systems',
'List Autonomous Systems',
{
limit: PaginationLimitParam,
offset: PaginationOffsetParam,
location: LocationParam.optional(),
orderBy: AsOrderByParam,
},
async ({ limit, offset, location, orderBy }) => {
try {
const props = getProps(agent)
const client = getCloudflareClient(props.accessToken)
const r = await client.radar.entities.asns.list({
limit,
offset,
location,
orderBy,
})
return {
content: [
{
type: 'text',
text: JSON.stringify({
result: r.asns,
}),
},
],
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error listing ASes: ${error instanceof Error && error.message}`,
},
],
}
}
}
)
agent.server.tool(
'get_as_details',
'Get Autonomous System details by ASN',
{
asn: AsnParam,
},
async ({ asn }) => {
try {
const props = getProps(agent)
const client = getCloudflareClient(props.accessToken)
const r = await client.radar.entities.asns.get(asn)
return {
content: [
{
type: 'text',
text: JSON.stringify({
result: r.asn,
}),
},
],
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error getting AS details: ${error instanceof Error && error.message}`,
},
],
}
}
}
)
agent.server.tool(
'get_ip_details',
'Get IP address information',
{
ip: IpParam,
},
async ({ ip }) => {
try {
const props = getProps(agent)
const client = getCloudflareClient(props.accessToken)
const r = await client.radar.entities.get({ ip })
return {
content: [
{
type: 'text',
text: JSON.stringify({
result: r.ip,
}),
},
],
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error getting IP details: ${error instanceof Error && error.message}`,
},
],
}
}
}
)
agent.server.tool(
'get_traffic_anomalies',
'Get traffic anomalies and outages',
{
limit: PaginationLimitParam,
offset: PaginationOffsetParam,
asn: AsnParam.optional(),
location: LocationParam.optional(),
dateRange: DateRangeParam.optional(),
dateStart: DateStartParam.optional(),
dateEnd: DateEndParam.optional(),
},
async ({ limit, offset, asn, location, dateStart, dateEnd, dateRange }) => {
try {
const props = getProps(agent)
const client = getCloudflareClient(props.accessToken)
const r = await client.radar.trafficAnomalies.get({
limit,
offset,
asn,
location,
dateRange,
dateStart,
dateEnd,
status: 'VERIFIED',
})
return {
content: [
{
type: 'text',
text: JSON.stringify({
result: r.trafficAnomalies,
}),
},
],
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error getting IP details: ${error instanceof Error && error.message}`,
},
],
}
}
}
)
agent.server.tool(
'get_internet_services_ranking',
'Get top Internet services',
{
limit: PaginationLimitParam,
date: DateListParam.optional(),
serviceCategory: InternetServicesCategoryParam.optional(),
},
async ({ limit, date, serviceCategory }) => {
try {
const props = getProps(agent)
const client = getCloudflareClient(props.accessToken)
const r = await client.radar.ranking.internetServices.top({
limit,
date,
serviceCategory,
})
return {
content: [
{
type: 'text',
text: JSON.stringify({
result: r,
}),
},
],
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error getting Internet services ranking: ${error instanceof Error && error.message}`,
},
],
}
}
}
)
agent.server.tool(
'get_domains_ranking',
'Get top or trending domains',
{
limit: PaginationLimitParam,
date: DateListParam.optional(),
location: LocationListParam.optional(),
rankingType: DomainRankingTypeParam.optional(),
},
async ({ limit, date, location, rankingType }) => {
try {
const props = getProps(agent)
const client = getCloudflareClient(props.accessToken)
const r = await client.radar.ranking.top({
limit,
date,
location,
rankingType,
})
return {
content: [
{
type: 'text',
text: JSON.stringify({
result: r,
}),
},
],
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error getting domains ranking: ${error instanceof Error && error.message}`,
},
],
}
}
}
)
agent.server.tool(
'get_domain_rank_details',
'Get domain rank details',
{
domain: DomainParam,
date: DateListParam.optional(),
},
async ({ domain, date }) => {
try {
const props = getProps(agent)
const client = getCloudflareClient(props.accessToken)
const r = await client.radar.ranking.domain.get(domain, { date })
return {
content: [
{
type: 'text',
text: JSON.stringify({
result: r,
}),
},
],
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error getting domain ranking details: ${error instanceof Error && error.message}`,
},
],
}
}
}
)
agent.server.tool(
'get_http_data',
'Retrieve HTTP traffic trends.',
{
dateRange: DateRangeArrayParam.optional(),
dateStart: DateStartArrayParam.optional(),
dateEnd: DateEndArrayParam.optional(),
asn: AsnArrayParam,
continent: ContinentArrayParam,
location: LocationArrayParam,
dimension: HttpDimensionParam,
},
async ({ dateStart, dateEnd, dateRange, asn, location, continent, dimension }) => {
try {
const props = getProps(agent)
const client = getCloudflareClient(props.accessToken)
const r = await resolveAndInvoke(client.radar.http, dimension, {
asn,
continent,
location,
dateRange,
dateStart,
dateEnd,
})
return {
content: [
{
type: 'text',
text: JSON.stringify({
result: r,
}),
},
],
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error getting HTTP data: ${error instanceof Error && error.message}`,
},
],
}
}
}
)
agent.server.tool(
'get_dns_queries_data',
'Retrieve trends in DNS queries to the 1.1.1.1 resolver.',
{
dateRange: DateRangeArrayParam.optional(),
dateStart: DateStartArrayParam.optional(),
dateEnd: DateEndArrayParam.optional(),
asn: AsnArrayParam,
continent: ContinentArrayParam,
location: LocationArrayParam,
dimension: DnsDimensionParam,
},
async ({ dateStart, dateEnd, dateRange, asn, location, continent, dimension }) => {
try {
const props = getProps(agent)
const client = getCloudflareClient(props.accessToken)
const r = await resolveAndInvoke(client.radar.dns, dimension, {
asn,
continent,
location,
dateRange,
dateStart,
dateEnd,
})
return {
content: [
{
type: 'text',
text: JSON.stringify({
result: r,
}),
},
],
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error getting DNS data: ${error instanceof Error && error.message}`,
},
],
}
}
}
)
agent.server.tool(
'get_l7_attack_data',
'Retrieve application layer (L7) attack trends.',
{
dateRange: DateRangeArrayParam.optional(),
dateStart: DateStartArrayParam.optional(),
dateEnd: DateEndArrayParam.optional(),
asn: AsnArrayParam,
continent: ContinentArrayParam,
location: LocationArrayParam,
dimension: L7AttackDimensionParam,
},
async ({ dateStart, dateEnd, dateRange, asn, location, continent, dimension }) => {
try {
const props = getProps(agent)
const client = getCloudflareClient(props.accessToken)
const r = await resolveAndInvoke(client.radar.attacks.layer7, dimension, {
asn,
continent,
location,
dateRange,
dateStart,
dateEnd,
})
return {
content: [
{
type: 'text',
text: JSON.stringify({
result: r,
}),
},
],
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error getting L7 attack data: ${error instanceof Error && error.message}`,
},
],
}
}
}
)
agent.server.tool(
'get_l3_attack_data',
'Retrieve application layer (L3) attack trends.',
{
dateRange: DateRangeArrayParam.optional(),
dateStart: DateStartArrayParam.optional(),
dateEnd: DateEndArrayParam.optional(),
asn: AsnArrayParam,
continent: ContinentArrayParam,
location: LocationArrayParam,
dimension: L3AttackDimensionParam,
},
async ({ dateStart, dateEnd, dateRange, asn, location, continent, dimension }) => {
try {
const props = getProps(agent)
const client = getCloudflareClient(props.accessToken)
const r = await resolveAndInvoke(client.radar.attacks.layer3, dimension, {
asn,
continent,
location,
dateRange,
dateStart,
dateEnd,
})
return {
content: [
{
type: 'text',
text: JSON.stringify({
result: r,
}),
},
],
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error getting L3 attack data: ${error instanceof Error && error.message}`,
},
],
}
}
}
)
agent.server.tool(
'get_email_routing_data',
'Retrieve Email Routing trends.',
{
dateRange: DateRangeArrayParam.optional(),
dateStart: DateStartArrayParam.optional(),
dateEnd: DateEndArrayParam.optional(),
dimension: EmailRoutingDimensionParam,
},
async ({ dateStart, dateEnd, dateRange, dimension }) => {
try {
const props = getProps(agent)
const client = getCloudflareClient(props.accessToken)
const r = await resolveAndInvoke(client.radar.email.routing, dimension, {
dateRange,
dateStart,
dateEnd,
})
return {
content: [
{
type: 'text',
text: JSON.stringify({
result: r,
}),
},
],
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error getting Email Routing data: ${error instanceof Error && error.message}`,
},
],
}
}
}
)
agent.server.tool(
'get_email_security_data',
'Retrieve Email Security trends.',
{
dateRange: DateRangeArrayParam.optional(),
dateStart: DateStartArrayParam.optional(),
dateEnd: DateEndArrayParam.optional(),
dimension: EmailSecurityDimensionParam,
},
async ({ dateStart, dateEnd, dateRange, dimension }) => {
try {
const props = getProps(agent)
const client = getCloudflareClient(props.accessToken)
const r = await resolveAndInvoke(client.radar.email.security, dimension, {
dateRange,
dateStart,
dateEnd,
})
return {
content: [
{
type: 'text',
text: JSON.stringify({
result: r,
}),
},
],
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error getting Email Security data: ${error instanceof Error && error.message}`,
},
],
}
}
}
)
agent.server.tool(
'get_internet_speed_data',
'Retrieve summary of bandwidth, latency, jitter, and packet loss, from the previous 90 days of Cloudflare Speed Test.',
{
dateEnd: DateEndArrayParam.optional(),
asn: AsnArrayParam,
continent: ContinentArrayParam,
location: LocationArrayParam,
dimension: InternetSpeedDimensionParam,
orderBy: InternetSpeedOrderByParam.optional(),
},
async ({ dateEnd, asn, location, continent, dimension, orderBy }) => {
if (orderBy && dimension === 'summary') {
throw new Error('Order by is only allowed for top locations and ASes')
}
try {
const props = getProps(agent)
const client = getCloudflareClient(props.accessToken)
const r = await resolveAndInvoke(client.radar.quality.speed, dimension, {
asn,
continent,
location,
dateEnd,
})
return {
content: [
{
type: 'text',
text: JSON.stringify({
result: r,
}),
},
],
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error getting Internet speed data: ${error instanceof Error && error.message}`,
},
],
}
}
}
)
agent.server.tool(
'get_internet_quality_data',
'Retrieves a summary or time series of bandwidth, latency, or DNS response time percentiles from the Radar Internet Quality Index (IQI).',
{
dateRange: DateRangeArrayParam.optional(),
dateStart: DateStartArrayParam.optional(),
dateEnd: DateEndArrayParam.optional(),
asn: AsnArrayParam,
continent: ContinentArrayParam,
location: LocationArrayParam,
format: z.enum(['summary', 'timeseriesGroups']),
metric: InternetQualityMetricParam,
},
async ({ dateRange, dateStart, dateEnd, asn, location, continent, format, metric }) => {
try {
const props = getProps(agent)
const client = getCloudflareClient(props.accessToken)
const r = await client.radar.quality.iqi[format]({
asn,
continent,
location,
dateRange,
dateStart,
dateEnd,
metric,
})
return {
content: [
{
type: 'text',
text: JSON.stringify({
result: r,
}),
},
],
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error getting Internet quality data: ${error instanceof Error && error.message}`,
},
],
}
}
}
)
agent.server.tool(
'get_ai_data',
'Retrieves AI-related data, including traffic from AI user agents, as well as popular models and model tasks specifically from Cloudflare Workers AI.',
{
dateRange: DateRangeArrayParam.optional(),
dateStart: DateStartArrayParam.optional(),
dateEnd: DateEndArrayParam.optional(),
asn: AsnArrayParam,
continent: ContinentArrayParam,
location: LocationArrayParam,
dimension: AiDimensionParam,
},
async ({ dateRange, dateStart, dateEnd, asn, location, continent, dimension }) => {
try {
const props = getProps(agent)
const client = getCloudflareClient(props.accessToken)
const r = await resolveAndInvoke(client.radar.ai, dimension, {
asn,
continent,
location,
dateRange,
dateStart,
dateEnd,
})
return {
content: [
{
type: 'text',
text: JSON.stringify({
result: r,
}),
},
],
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error getting AI data: ${error instanceof Error && error.message}`,
},
],
}
}
}
)
}
```
--------------------------------------------------------------------------------
/apps/demo-day/frontend/script.js:
--------------------------------------------------------------------------------
```javascript
document.addEventListener('DOMContentLoaded', () => {
const container = document.querySelector('.page-wrapper')
const starfield = document.createElement('div')
starfield.className = 'starfield'
container.appendChild(starfield)
// Create initial stars
const numberOfStars = 300
const stars = []
function createStar() {
const star = document.createElement('div')
star.className = 'star'
// Random position
const x = Math.random() * window.innerWidth
const y = Math.random() * window.innerHeight
// Random size (more variation in sizes)
const size = Math.random() * 2 + (Math.random() > 0.95 ? 1.5 : 0)
// More subtle initial opacity
const opacity = Math.random() * 0.15 + 0.05
star.style.cssText = `
left: ${x}px;
top: ${y}px;
width: ${size}px;
height: ${size}px;
opacity: ${opacity};
`
return star
}
// Initialize stars
for (let i = 0; i < numberOfStars; i++) {
const star = createStar()
starfield.appendChild(star)
stars.push(star)
}
// Animate stars
function twinkle() {
stars.forEach((star) => {
// Random chance to twinkle
if (Math.random() > 0.98) {
const currentOpacity = parseFloat(star.style.opacity)
const targetOpacity =
currentOpacity < 0.1
? Math.random() * 0.15 + 0.05 // Brighter
: Math.random() * 0.05 + 0.02 // Dimmer
// Slower transition for more subtle effect
star.style.transition = 'opacity 1.5s ease-in-out'
star.style.opacity = targetOpacity
}
})
requestAnimationFrame(twinkle)
}
// Start animation
twinkle()
// Form handling
const emailForm = document.querySelector('.input-group')
//const //emailInput = null //emailForm.querySelector('input[type="email"]')
const honeypotInput = emailForm.querySelector('input[name="contact_me_by_fax"]')
//const notifyButton = emailForm.querySelector('.notify-btn')
// Check if user has already signed up
/*if (localStorage.getItem('mcp_demo_signup')) {
const savedEmail = localStorage.getItem('mcp_demo_signup')
showSuccessState(savedEmail)
}*/
/*function showSuccessState(email) {
const inputGroup = //emailInput.closest('.input-group')
inputGroup.classList.add('success')
//emailInput.value = email
//emailInput.disabled = true
notifyButton.disabled = true
// Update button
notifyButton.innerHTML = `
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M13.5 4.5L6 12L2.5 8.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
`
notifyButton.classList.add('success')
createConfetti(notifyButton)
}*/
// Rate limiting configuration
const RATE_LIMIT_DURATION = 60000 // 1 minute
const MAX_ATTEMPTS = 5
let attemptCount = 0
let lastAttemptTime = 0
function checkRateLimit() {
const now = Date.now()
if (now - lastAttemptTime > RATE_LIMIT_DURATION) {
attemptCount = 0
}
if (attemptCount >= MAX_ATTEMPTS) {
return false
}
attemptCount++
lastAttemptTime = now
return true
}
// Enhanced email validation
function isValidEmail(email) {
if (email.length > 254) return false
const re =
/^(?=[a-zA-Z0-9@._%+-]{6,254}$)[a-zA-Z0-9._%+-]{1,64}@(?:[a-zA-Z0-9-]{1,63}\.){1,8}[a-zA-Z]{2,63}$/
return re.test(email)
}
// XSS Prevention
function sanitizeInput(str) {
const div = document.createElement('div')
div.textContent = str
return div.innerHTML
}
// Debounced email validation
const debounce = (fn, delay) => {
let timeoutId
return (...args) => {
clearTimeout(timeoutId)
timeoutId = setTimeout(() => fn(...args), delay)
}
}
// Retry logic for API calls
async function retryFetch(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url, options)
const data = await response.json()
return { response, data }
} catch (error) {
if (i === maxRetries - 1) throw error
await new Promise((resolve) => setTimeout(resolve, 1000 * Math.pow(2, i)))
}
}
}
// Enhanced toast function with accessibility
function showToast(message, duration = 3000) {
const existingToast = document.querySelector('.toast')
if (existingToast) {
existingToast.remove()
}
const toast = document.createElement('div')
toast.className = 'toast'
toast.setAttribute('role', 'alert')
toast.setAttribute('aria-live', 'polite')
toast.textContent = sanitizeInput(message)
document.body.appendChild(toast)
toast.offsetHeight
toast.classList.add('show')
setTimeout(() => {
toast.classList.remove('show')
setTimeout(() => toast.remove(), 300)
}, duration)
}
// Setup accessibility attributes
/*function setupAccessibility() {
//emailInput.setAttribute('aria-label', 'Email address for notification')
notifyButton.setAttribute('aria-label', 'Sign up for notification')
document.querySelector('.success-message')?.setAttribute('role', 'status')
}*/
// Debounced email validation on input
const validateEmailDebounced = debounce((email) => {
const isValid = isValidEmail(email)
//emailInput.style.border = isValid ? '' : '1px solid red'
}, 300)
//emailInput.addEventListener('input', (e) => validateEmailDebounced(e.target.value))
// Enhanced click handler with all improvements
/*notifyButton.addEventListener('click', async (e) => {
e.preventDefault()
//const email = //emailInput.value.trim()
// Rate limit check
if (!checkRateLimit()) {
showToast('Please wait a minute before trying again.')
return
}
// Basic validation
if (!email) {
//emailInput.style.border = '1px solid red'
return
}
if (!isValidEmail(email)) {
//emailInput.style.border = '1px solid red'
showToast('Please enter a valid email address.')
return
}
// Check if already signed up
/*if (localStorage.getItem('mcp_demo_signup')) {
return
}*/
// Honeypot check
/*if (honeypotInput.value) {
console.log('Bot detected')
return
}
try {
const { response, data } = await retryFetch(
'https://starbasedb-3285.outerbase.workers.dev/query',
{
method: 'POST',
headers: {
'X-Starbase-Source': 'internal',
Authorization: 'Bearer 8gmjguywgvsy2hvxnqpqzapwjq896ke3',
'Content-Type': 'application/json',
},
body: JSON.stringify({
sql: 'INSERT INTO signups (email, status, created_at) VALUES (?, ?, CURRENT_TIMESTAMP)',
params: [sanitizeInput(email), 'pending'],
}),
}
)
if (!response.ok) {
throw new Error(data.error || 'Signup failed')
}
localStorage.setItem('mcp_demo_signup', email)
showSuccessState(email)
setupCalendarActions()
setupAccessibility()
} catch (error) {
console.error('Error:', error)
//emailInput.style.border = '1px solid red'
if (error.message.includes('UNIQUE constraint failed')) {
showToast('This email is already registered for the demo. Check your inbox for details.')
localStorage.setItem('mcp_demo_signup', email)
showSuccessState(email)
setupCalendarActions()
setupAccessibility()
} else {
showToast('Something went wrong. Please try again.')
}
}
})*/
// Initialize accessibility
//()
function setupCalendarActions() {
const calendarActions = document.querySelectorAll(
'.success-message .calendar-action, .calendar-option'
)
const eventDetails =
"Get a preview of the future of agentic software. See how the world's most innovative platforms have connected agents to their services with MCP to build a new class of product experiences.\n\nJoin Live: https://cloudflare.tv/mcp-demo-day"
const location = 'https://cloudflare.tv/mcp-demo-day'
calendarActions.forEach((option) => {
option.addEventListener('click', () => {
const calendarType = option.dataset.calendarType
switch (calendarType) {
case 'google':
window.open(
'https://calendar.google.com/calendar/render?action=TEMPLATE&text=MCP+Demo+Day&details=Get+a+preview+of+the+future+of+agentic+software.+See+how+the+world%27s+most+innovative+platforms+have+connected+agents+to+their+services+with+MCP+to+build+a+new+class+of+product+experiences.%0A%0AJoin+Live%3A+https%3A%2F%2Fcloudflare.tv%2Fmcp-demo-day&location=https%3A%2F%2Fcloudflare.tv%2Fmcp-demo-day&dates=20250501T170000Z%2F20250501T183000Z',
'_blank'
)
break
case 'outlook':
window.open(
'https://outlook.live.com/calendar/0/deeplink/compose?subject=MCP+Demo+Day&body=Get+a+preview+of+the+future+of+agentic+software.+See+how+the+world%27s+most+innovative+platforms+have+connected+agents+to+their+services+with+MCP+to+build+a+new+class+of+product+experiences.%0A%0AJoin+Live%3A+https%3A%2F%2Fcloudflare.tv%2Fmcp-demo-day&startdt=2025-05-01T17%3A00%3A00Z&enddt=2025-05-01T18%3A30%3A00Z&location=https%3A%2F%2Fcloudflare.tv%2Fmcp-demo-day',
'_blank'
)
break
case 'apple':
case 'ics':
const icsContent = `BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//MCP Demo Day//EN
CALSCALE:GREGORIAN
BEGIN:VEVENT
SUMMARY:MCP Demo Day
DESCRIPTION:${eventDetails.replace(/\n/g, '\\n')}
LOCATION:${location}
DTSTART:20250501T170000Z
DTEND:20250501T183000Z
STATUS:CONFIRMED
SEQUENCE:0
END:VEVENT
END:VCALENDAR`
if (calendarType === 'apple') {
const dataUri = 'data:text/calendar;charset=utf-8,' + encodeURIComponent(icsContent)
window.open(dataUri)
} else {
const blob = new Blob([icsContent], { type: 'text/calendar;charset=utf-8' })
const link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = 'mcp_demo_day.ics'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
break
}
// Close the dialog if it's open
const dialog = document.getElementById('calendarDialog')
if (dialog) {
dialog.close()
}
})
})
}
// Call setupCalendarActions immediately if needed
setupCalendarActions()
// Remove red border on input focus
//emailInput.addEventListener('focus', () => {
//emailInput.style.border = 'none'
//})
// Company list hover effect
const companies = document.querySelectorAll('.demo-companies li')
companies.forEach((company) => {
company.addEventListener('mouseenter', () => {
companies.forEach((c) => {
if (c !== company) {
c.style.opacity = '0.5'
}
})
})
company.addEventListener('mouseleave', () => {
companies.forEach((c) => {
c.style.opacity = '1'
})
})
})
// Setup company backgrounds
const companyNames = [
'asana',
'atlassian',
'cloudflare',
'intercom',
'linear',
'paypal',
'sentry',
'square',
'stripe',
'webflow',
'more',
]
const demoList = document.querySelector('.demo-companies')
const companyItems = demoList.querySelectorAll('li')
// Add data attributes to company items
companyItems.forEach((item, index) => {
const company = companyNames[index]
if (company) {
item.setAttribute('data-company', company)
}
console.log('add attrib')
})
// Create background containers for each company
companyNames.forEach((company) => {
console.log('add container')
const background = document.createElement('div')
background.className = `company-background ${company}`
// Load SVG from file
fetch(`/public/${company}.svg`)
.then((response) => response.text())
.then((svgContent) => {
// Clean up the SVG to remove any fill paths
const parser = new DOMParser()
const doc = parser.parseFromString(svgContent, 'image/svg+xml')
const svg = doc.querySelector('svg')
// Remove any fill attributes and set stroke
svg.querySelectorAll('path, circle, rect').forEach((path) => {
path.setAttribute('fill', 'none')
path.setAttribute('stroke', 'currentColor')
path.setAttribute('stroke-width', '.5')
})
background.innerHTML = svg.outerHTML
})
.catch((error) => console.error(`Error loading ${company}.svg:`, error))
demoList.appendChild(background)
})
// Add cycling animation for company backgrounds
let currentBackgroundIndex = 0
let isHovering = false
let hoverCompany = null
const backgrounds = document.querySelectorAll('.company-background')
function cycleBackgrounds() {
// Remove active class from all backgrounds and company names
backgrounds.forEach((bg) => bg.classList.remove('active'))
companyItems.forEach((item) => item.classList.remove('active'))
// If hovering, show the hovered company's background and highlight its name
if (isHovering && hoverCompany) {
const hoverBackground = Array.from(backgrounds).find((bg) =>
bg.classList.contains(hoverCompany)
)
const hoverItem = Array.from(companyItems).find(
(item) => item.getAttribute('data-company') === hoverCompany
)
if (hoverBackground) {
hoverBackground.classList.add('active')
if (hoverItem) hoverItem.classList.add('active')
return
}
}
// Otherwise, show the next background in the cycle and highlight its name
backgrounds[currentBackgroundIndex].classList.add('active')
companyItems[currentBackgroundIndex].classList.add('active')
currentBackgroundIndex = (currentBackgroundIndex + 1) % backgrounds.length
console.log('cucle')
}
// Start cycling every 3 seconds
setInterval(cycleBackgrounds, 3000)
// Show first background immediately
cycleBackgrounds()
// Handle hover states
companyItems.forEach((item) => {
item.addEventListener('mouseenter', () => {
isHovering = true
hoverCompany = item.getAttribute('data-company')
cycleBackgrounds() // Show the hovered company immediately
})
item.addEventListener('mouseleave', () => {
isHovering = false
hoverCompany = null
cycleBackgrounds() // Resume normal cycling
})
})
function parseDateTime() {
const dateText = document.querySelector('.date-time h2').textContent.trim() // e.g., "APRIL 30TH, 2025"
const timeText = document.querySelector('.date-time h3').textContent.trim() // e.g., "ONLINE, 1:00 PM PT"
// Remove ordinal indicators (st, nd, rd, th) and parse date
const cleanDateText = dateText.replace(/(ST|ND|RD|TH),/i, ',')
// Create a more precise regex to match the date format
const dateTimeParts = cleanDateText.match(/^([A-Za-z]+)\s+(\d{1,2}),\s*(\d{4})$/i)
// Handle "ONLINE, " prefix in time
const timeParts = timeText
.replace(/^ONLINE,\s*/i, '')
.match(/^(\d{1,2}):(\d{2})\s*(AM|PM)\s*(PST|PDT|EST|EDT|CST|CDT|MST|MDT|PT)?$/i)
if (!dateTimeParts || !timeParts) {
throw new Error('Invalid date/time format')
}
const [, month, day, year] = dateTimeParts
const [, hours, minutes, ampm, timezone] = timeParts
let hour24 = parseInt(hours)
// Convert to 24-hour format
if (ampm.toUpperCase() === 'PM' && hour24 < 12) hour24 += 12
if (ampm.toUpperCase() === 'AM' && hour24 === 12) hour24 = 0
// Create date in UTC
const date = new Date(
Date.UTC(parseInt(year), getMonthIndex(month), parseInt(day), hour24, parseInt(minutes), 0)
)
// Since the time is specified in PT (Pacific Time), adjust for PT (-7 hours from UTC during PDT)
date.setUTCHours(date.getUTCHours() - 7)
if (isNaN(date.getTime())) {
throw new Error('Invalid date/time format')
}
return date
}
// Helper function to get month index (0-11) from month name
function getMonthIndex(month) {
const months = {
JANUARY: 0,
JAN: 0,
FEBRUARY: 1,
FEB: 1,
MARCH: 2,
MAR: 2,
APRIL: 3,
APR: 3,
MAY: 4,
JUNE: 5,
JUN: 5,
JULY: 6,
JUL: 6,
AUGUST: 7,
AUG: 7,
SEPTEMBER: 8,
SEP: 8,
OCTOBER: 9,
OCT: 9,
NOVEMBER: 10,
NOV: 10,
DECEMBER: 11,
DEC: 11,
}
return months[month.toUpperCase()]
}
})
// Helper functions
function isValidEmail(email) {
const re =
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
return re.test(email.toLowerCase())
}
// Particle System
function createParticle() {
const particle = document.createElement('div')
particle.className = 'particle'
// Random size between 3-6px
const size = Math.random() * 3 + 3
particle.style.width = `${size}px`
particle.style.height = `${size}px`
// Random starting position, but keep particles within viewport bounds
const startX = Math.random() * (window.innerWidth - size)
const startY = window.innerHeight + size
particle.style.left = `${startX}px`
particle.style.top = `${startY}px`
// Random animation duration between 6-10 seconds
const duration = Math.random() * 4000 + 6000
particle.style.animation = `floatUp ${duration}ms cubic-bezier(0.4, 0, 0.2, 1) forwards`
document.body.appendChild(particle)
// Cleanup after animation
setTimeout(() => {
if (particle && particle.parentNode) {
particle.parentNode.removeChild(particle)
}
}, duration)
}
// Particle manager
let particleInterval
const startParticles = () => {
// Create initial batch of particles
for (let i = 0; i < 5; i++) {
setTimeout(() => createParticle(), i * 200)
}
// Create a new particle every 400ms
particleInterval = setInterval(() => {
// Limit to 15 particles at a time for better performance
if (document.querySelectorAll('.particle').length < 15) {
createParticle()
}
}, 400)
}
// Cleanup function
const cleanupParticles = () => {
if (particleInterval) {
clearInterval(particleInterval)
particleInterval = null
}
document.querySelectorAll('.particle').forEach((particle) => {
if (particle.parentNode) {
particle.parentNode.removeChild(particle)
}
})
}
// Start particles when page loads
startParticles()
// Cleanup on page unload
window.addEventListener('unload', cleanupParticles)
// Pause particles when page is not visible
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
cleanupParticles()
} else {
startParticles()
}
})
// Restart particles on window resize
let resizeTimeout
window.addEventListener('resize', () => {
if (resizeTimeout) {
clearTimeout(resizeTimeout)
}
resizeTimeout = setTimeout(() => {
cleanupParticles()
startParticles()
}, 200)
})
function createConfetti(button) {
const colors = ['#FF6633', '#FF8533', '#FF9966', '#FFAA80']
const confettiCount = 20
for (let i = 0; i < confettiCount; i++) {
const confetti = document.createElement('div')
confetti.className = 'confetti'
// Random size between 4-8px
const size = Math.random() * 4 + 4
confetti.style.width = `${size}px`
confetti.style.height = `${size}px`
// Random color from our palette
confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)]
// Random position behind the button
const startX = Math.random() * button.offsetWidth
confetti.style.left = `${startX}px`
confetti.style.top = '50%'
// Random animation duration and delay
const duration = Math.random() * 400 + 600
const delay = Math.random() * 200
confetti.style.animation = `confettiFall ${duration}ms cubic-bezier(0.4, 0, 0.2, 1) ${delay}ms forwards`
button.appendChild(confetti)
// Cleanup
setTimeout(() => {
if (confetti.parentNode === button) {
button.removeChild(confetti)
}
}, duration + delay)
}
}
```
--------------------------------------------------------------------------------
/apps/demo-day/frontend/public/intercom.svg:
--------------------------------------------------------------------------------
```
<svg width="491" height="490" viewBox="0 0 491 490" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="path-1-inside-1_1164_3" fill="white">
<path d="M425.137 269.635C425.137 274.005 423.416 278.193 420.353 281.282C417.29 284.37 413.138 286.106 408.806 286.106C404.476 286.106 400.321 284.37 397.258 281.282C394.195 278.193 392.475 274.005 392.475 269.635V122.5C392.475 118.133 394.195 113.944 397.258 110.856C400.321 107.765 404.476 106.031 408.806 106.031C413.138 106.031 417.29 107.765 420.353 110.856C423.416 113.944 425.137 118.133 425.137 122.5V269.635ZM419.468 371.584C417.31 373.761 356.576 424.803 245.229 424.803C133.885 424.803 73.5572 374.034 70.9925 371.855C69.3751 370.481 68.0455 368.799 67.0746 366.905C66.1059 365.01 65.5152 362.942 65.3391 360.817C65.1651 358.693 65.4057 356.555 66.0522 354.524C66.6987 352.495 67.7341 350.613 69.1023 348.988C71.9205 345.7 75.9157 343.669 80.2138 343.336C84.5119 343.005 88.7648 344.402 92.0469 347.219C92.992 347.9 147.248 392.271 245.096 392.271C342.945 392.271 397.604 347.627 398.143 347.219C401.462 344.425 405.73 343.048 410.039 343.379C414.35 343.71 418.362 345.721 421.223 348.988C423.977 352.229 425.352 356.435 425.047 360.69C424.744 364.945 422.787 368.91 419.604 371.719L419.468 371.584ZM65.1887 122.5C65.4336 118.12 67.3861 114.013 70.6231 111.079C73.8601 108.146 78.1173 106.624 82.4648 106.847C86.4923 107.077 90.2942 108.805 93.136 111.696C95.9777 114.587 97.6553 118.436 97.8507 122.5V269.365C97.8507 273.732 96.1302 277.92 93.0672 281.009C90.0042 284.099 85.85 285.833 81.5197 285.833C77.1873 285.833 73.0353 284.099 69.9723 281.009C66.9092 277.92 65.1887 273.732 65.1887 269.365V122.5ZM147.112 89.8328C147.355 85.4529 149.31 81.3459 152.547 78.4124C155.784 75.4788 160.041 73.9594 164.386 74.1807C168.416 74.4128 172.218 76.1386 175.059 79.0292C177.899 81.9197 179.579 85.7688 179.772 89.8328V307.611C179.772 311.98 178.052 316.168 174.991 319.257C171.928 322.345 167.774 324.079 163.443 324.079C159.111 324.079 154.957 322.345 151.896 319.257C148.833 316.168 147.112 311.98 147.112 307.611V89.8328ZM229.44 81.6661C229.44 77.2991 231.16 73.1105 234.223 70.0222C237.286 66.9339 241.438 65.1974 245.771 65.1974C250.101 65.1974 254.255 66.9339 257.318 70.0222C260.381 73.1105 262.102 77.2991 262.102 81.6661V318.5C262.102 322.867 260.381 327.056 257.318 330.146C254.255 333.235 250.101 334.969 245.771 334.969C241.438 334.969 237.286 333.235 234.223 330.146C231.16 327.056 229.44 322.867 229.44 318.5V81.6661ZM310.418 89.8328C310.418 85.4658 312.139 81.2772 315.202 78.1889C318.262 75.1006 322.417 73.3641 326.749 73.3641C331.079 73.3641 335.234 75.1006 338.297 78.1889C341.357 81.2772 343.078 85.4658 343.078 89.8328V307.611C343.078 311.98 341.357 316.168 338.297 319.257C335.234 322.345 331.079 324.079 326.749 324.079C322.417 324.079 318.262 322.345 315.202 319.257C312.139 316.168 310.418 311.98 310.418 307.611V89.8328ZM429.592 0.00135809H61.4104C53.3878 -0.0523702 45.4338 1.48856 38.0019 4.54032C30.572 7.58994 23.8102 12.0902 18.1074 17.779C12.4024 23.4677 7.86801 30.2375 4.7599 37.6971C1.65394 45.1546 0.0365155 53.1601 0 61.2516V428.751C0.0365155 436.84 1.65394 444.845 4.7599 452.305C7.86801 459.763 12.4024 466.532 18.1074 472.221C23.8102 477.91 30.572 482.41 38.0019 485.46C45.4338 488.511 53.3878 490.052 61.4104 489.999H429.592C437.602 490.052 445.545 488.516 452.966 485.473C460.389 482.432 467.143 477.944 472.845 472.268C478.548 466.595 483.087 459.842 486.201 452.398C489.316 444.955 490.946 436.965 491 428.886V61.2516C490.963 53.1708 489.35 45.1782 486.253 37.7272C483.153 30.2762 478.63 23.515 472.94 17.8262C467.252 12.1396 460.505 7.63722 453.091 4.579C445.676 1.52079 437.737 -0.0351771 429.725 0.00135809"/>
</mask>
<path d="M397.258 110.856L397.968 111.56L397.969 111.56L397.258 110.856ZM420.353 110.856L419.643 111.56L419.643 111.56L420.353 110.856ZM419.468 371.584L420.176 370.877L419.466 370.166L418.758 370.88L419.468 371.584ZM70.9925 371.855L71.6401 371.093L71.6398 371.092L70.9925 371.855ZM67.0746 366.905L66.1842 367.36L66.1848 367.361L67.0746 366.905ZM65.3391 360.817L64.3424 360.898L64.3425 360.899L65.3391 360.817ZM66.0522 354.524L65.0994 354.22L65.0993 354.221L66.0522 354.524ZM69.1023 348.988L68.343 348.337L68.3374 348.344L69.1023 348.988ZM80.2138 343.336L80.137 342.339L80.1365 342.339L80.2138 343.336ZM92.0469 347.219L91.3956 347.978L91.4278 348.005L91.4622 348.03L92.0469 347.219ZM398.143 347.219L398.747 348.016L398.768 348.001L398.787 347.984L398.143 347.219ZM410.039 343.379L409.962 344.376L409.962 344.376L410.039 343.379ZM421.223 348.988L421.985 348.34L421.98 348.335L421.976 348.329L421.223 348.988ZM425.047 360.69L424.049 360.618L424.049 360.619L425.047 360.69ZM419.604 371.719L418.896 372.426L419.561 373.091L420.265 372.469L419.604 371.719ZM65.1887 122.5L64.1903 122.444L64.1887 122.472V122.5H65.1887ZM82.4648 106.847L82.5218 105.849L82.5162 105.849L82.4648 106.847ZM97.8507 122.5H98.8507V122.476L98.8496 122.452L97.8507 122.5ZM93.0672 281.009L92.3572 280.305L92.357 280.305L93.0672 281.009ZM69.9723 281.009L70.6825 280.305L70.6823 280.305L69.9723 281.009ZM147.112 89.8328L146.114 89.7775L146.112 89.8052V89.8328H147.112ZM164.386 74.1807L164.444 73.1824L164.437 73.182L164.386 74.1807ZM175.059 79.0292L175.773 78.3284L175.773 78.3281L175.059 79.0292ZM179.772 89.8328H180.772V89.8091L180.771 89.7853L179.772 89.8328ZM174.991 319.257L175.701 319.961L175.701 319.961L174.991 319.257ZM151.896 319.257L152.606 318.553L152.606 318.553L151.896 319.257ZM257.318 70.0222L256.608 70.7264L257.318 70.0222ZM257.318 330.146L258.028 330.851L258.028 330.85L257.318 330.146ZM234.223 330.146L233.513 330.85L233.513 330.851L234.223 330.146ZM315.202 78.1889L315.912 78.893L315.912 78.8928L315.202 78.1889ZM338.297 78.1889L339.007 77.4849L339.007 77.4847L338.297 78.1889ZM338.297 319.257L339.007 319.961L339.007 319.961L338.297 319.257ZM315.202 319.257L315.912 318.553L315.912 318.553L315.202 319.257ZM61.4104 0.00135809L61.4037 1.00136H61.4104V0.00135809ZM38.0019 4.54032L38.3816 5.46542L38.3817 5.46537L38.0019 4.54032ZM18.1074 17.779L18.8135 18.4871L18.8136 18.4869L18.1074 17.779ZM4.7599 37.6971L3.83682 37.3125L3.83677 37.3126L4.7599 37.6971ZM0 61.2516L-1 61.247V61.2516H0ZM0 428.751H-1.00001L-0.99999 428.755L0 428.751ZM4.7599 452.305L3.83673 452.689L3.83686 452.69L4.7599 452.305ZM18.1074 472.221L18.8136 471.513L18.8135 471.513L18.1074 472.221ZM38.0019 485.46L38.3817 484.535L38.3816 484.535L38.0019 485.46ZM61.4104 489.999V488.999L61.4037 488.999L61.4104 489.999ZM429.592 489.999L429.598 488.999H429.592V489.999ZM452.966 485.473L452.587 484.547L452.587 484.547L452.966 485.473ZM472.845 472.268L472.14 471.559L472.14 471.56L472.845 472.268ZM486.201 452.398L485.279 452.011L485.279 452.012L486.201 452.398ZM491 428.886L492 428.893V428.886H491ZM491 61.2516L492 61.2516L492 61.247L491 61.2516ZM486.253 37.7272L487.176 37.3433L487.176 37.3431L486.253 37.7272ZM472.94 17.8262L473.647 17.1191L473.647 17.1191L472.94 17.8262ZM425.137 269.635H424.137C424.137 273.742 422.52 277.677 419.643 280.577L420.353 281.282L421.063 281.986C424.313 278.709 426.137 274.267 426.137 269.635H425.137ZM420.353 281.282L419.643 280.577C416.767 283.478 412.87 285.106 408.806 285.106V286.106V287.106C413.407 287.106 417.814 285.262 421.063 281.986L420.353 281.282ZM408.806 286.106V285.106C404.744 285.106 400.845 283.478 397.968 280.577L397.258 281.282L396.548 281.986C399.798 285.262 404.207 287.106 408.806 287.106V286.106ZM397.258 281.282L397.968 280.577C395.092 277.677 393.475 273.742 393.475 269.635H392.475H391.475C391.475 274.267 393.299 278.709 396.548 281.986L397.258 281.282ZM392.475 269.635H393.475V122.5H392.475H391.475V269.635H392.475ZM392.475 122.5H393.475C393.475 118.395 395.092 114.46 397.968 111.56L397.258 110.856L396.548 110.151C393.299 113.428 391.475 117.87 391.475 122.5H392.475ZM397.258 110.856L397.969 111.56C400.845 108.658 404.743 107.031 408.806 107.031V106.031V105.031C404.208 105.031 399.798 106.873 396.548 110.152L397.258 110.856ZM408.806 106.031V107.031C412.87 107.031 416.767 108.658 419.643 111.56L420.353 110.856L421.064 110.152C417.814 106.873 413.406 105.031 408.806 105.031V106.031ZM420.353 110.856L419.643 111.56C422.52 114.46 424.137 118.395 424.137 122.5H425.137H426.137C426.137 117.87 424.313 113.428 421.063 110.151L420.353 110.856ZM425.137 122.5H424.137V269.635H425.137H426.137V122.5H425.137ZM419.468 371.584L418.758 370.88C416.764 372.891 356.277 423.803 245.229 423.803V424.803V425.803C356.875 425.803 417.856 374.63 420.178 372.288L419.468 371.584ZM245.229 424.803V423.803C134.205 423.803 74.0994 373.182 71.6401 371.093L70.9925 371.855L70.345 372.617C73.015 374.885 133.565 425.803 245.229 425.803V424.803ZM70.9925 371.855L71.6398 371.092C70.1235 369.805 68.876 368.226 67.9645 366.449L67.0746 366.905L66.1848 367.361C67.2151 369.371 68.6267 371.158 70.3453 372.617L70.9925 371.855ZM67.0746 366.905L67.9651 366.45C67.0555 364.67 66.501 362.729 66.3357 360.734L65.3391 360.817L64.3425 360.899C64.5294 363.155 65.1563 365.349 66.1842 367.36L67.0746 366.905ZM65.3391 360.817L66.3357 360.735C66.1723 358.741 66.3983 356.733 67.0051 354.827L66.0522 354.524L65.0993 354.221C64.413 356.377 64.1579 358.646 64.3424 360.898L65.3391 360.817ZM66.0522 354.524L67.005 354.828C67.6121 352.922 68.584 351.156 69.8672 349.632L69.1023 348.988L68.3374 348.344C66.8841 350.069 65.7854 352.068 65.0994 354.22L66.0522 354.524ZM69.1023 348.988L69.8616 349.639C72.5079 346.551 76.2582 344.645 80.291 344.333L80.2138 343.336L80.1365 342.339C75.5731 342.692 71.333 344.848 68.343 348.337L69.1023 348.988ZM80.2138 343.336L80.2905 344.333C84.3226 344.022 88.3139 345.332 91.3956 347.978L92.0469 347.219L92.6983 346.46C89.2157 343.471 84.7011 341.987 80.137 342.339L80.2138 343.336ZM92.0469 347.219L91.4622 348.03C91.4594 348.028 91.4717 348.037 91.517 348.073C91.5568 348.103 91.6087 348.144 91.6767 348.197C91.8112 348.302 91.9988 348.448 92.2385 348.631C92.7179 348.998 93.4041 349.515 94.2947 350.16C96.0758 351.45 98.6726 353.252 102.066 355.392C108.853 359.672 118.827 365.307 131.837 370.919C157.857 382.143 196.017 393.271 245.096 393.271V392.271V391.271C147.571 391.271 93.5301 347.056 92.6317 346.408L92.0469 347.219ZM245.096 392.271V393.271C294.176 393.271 332.438 382.074 358.507 370.816C371.541 365.188 381.528 359.544 388.296 355.277C391.68 353.143 394.259 351.353 396.013 350.082C396.89 349.446 397.56 348.94 398.021 348.585C398.251 348.407 398.429 348.268 398.554 348.169C398.616 348.12 398.665 348.081 398.7 348.053C398.743 348.02 398.751 348.014 398.747 348.016L398.143 347.219L397.54 346.422C397.069 346.778 342.624 391.271 245.096 391.271V392.271ZM398.143 347.219L398.787 347.984C401.906 345.358 405.916 344.065 409.962 344.376L410.039 343.379L410.115 342.382C405.544 342.03 401.018 343.492 397.499 346.454L398.143 347.219ZM410.039 343.379L409.962 344.376C414.011 344.687 417.781 346.576 420.471 349.647L421.223 348.988L421.976 348.329C418.943 344.866 414.688 342.733 410.115 342.382L410.039 343.379ZM421.223 348.988L420.461 349.635C423.045 352.676 424.336 356.623 424.049 360.618L425.047 360.69L426.044 360.761C426.368 356.246 424.909 351.781 421.985 348.34L421.223 348.988ZM425.047 360.69L424.049 360.619C423.765 364.614 421.928 368.335 418.942 370.969L419.604 371.719L420.265 372.469C423.646 369.486 425.723 365.276 426.044 360.761L425.047 360.69ZM419.604 371.719L420.311 371.012L420.176 370.877L419.468 371.584L418.761 372.291L418.896 372.426L419.604 371.719ZM65.1887 122.5L66.1872 122.555C66.4174 118.436 68.2536 114.576 71.2946 111.82L70.6231 111.079L69.9516 110.338C66.5186 113.449 64.4497 117.803 64.1903 122.444L65.1887 122.5ZM70.6231 111.079L71.2946 111.82C74.3353 109.064 78.3328 107.636 82.4135 107.846L82.4648 106.847L82.5162 105.849C77.9019 105.612 73.3848 107.227 69.9516 110.338L70.6231 111.079ZM82.4648 106.847L82.4078 107.846C86.1858 108.062 89.7542 109.683 92.4228 112.397L93.136 111.696L93.8491 110.995C90.8341 107.928 86.7988 106.093 82.5218 105.849L82.4648 106.847ZM93.136 111.696L92.4228 112.397C95.0913 115.111 96.6682 118.727 96.8519 122.548L97.8507 122.5L98.8496 122.452C98.6424 118.144 96.8641 114.062 93.8491 110.995L93.136 111.696ZM97.8507 122.5H96.8507V269.365H97.8507H98.8507V122.5H97.8507ZM97.8507 269.365H96.8507C96.8507 273.469 95.2336 277.404 92.3572 280.305L93.0672 281.009L93.7772 281.713C97.0269 278.436 98.8507 273.994 98.8507 269.365H97.8507ZM93.0672 281.009L92.357 280.305C89.481 283.207 85.5822 284.833 81.5197 284.833V285.833V286.833C86.1179 286.833 90.5275 284.992 93.7775 281.713L93.0672 281.009ZM81.5197 285.833V284.833C77.4552 284.833 73.5586 283.207 70.6825 280.305L69.9723 281.009L69.262 281.713C72.512 284.992 76.9193 286.833 81.5197 286.833V285.833ZM69.9723 281.009L70.6823 280.305C67.8059 277.404 66.1887 273.469 66.1887 269.365H65.1887H64.1887C64.1887 273.994 66.0126 278.436 69.2622 281.713L69.9723 281.009ZM65.1887 269.365H66.1887V122.5H65.1887H64.1887V269.365H65.1887ZM147.112 89.8328L148.111 89.8882C148.339 85.7698 150.177 81.9095 153.218 79.1534L152.547 78.4124L151.875 77.6714C148.442 80.7824 146.371 85.1361 146.114 89.7775L147.112 89.8328ZM152.547 78.4124L153.218 79.1534C156.259 76.3979 160.256 74.9716 164.335 75.1794L164.386 74.1807L164.437 73.182C159.826 72.9471 155.309 74.5598 151.875 77.6714L152.547 78.4124ZM164.386 74.1807L164.329 75.1791C168.11 75.3968 171.678 77.0161 174.346 79.7302L175.059 79.0292L175.773 78.3281C172.757 75.2611 168.722 73.4288 164.444 73.1824L164.386 74.1807ZM175.059 79.0292L174.346 79.73C177.013 82.4446 178.592 86.0609 178.773 89.8804L179.772 89.8328L180.771 89.7853C180.566 85.4768 178.785 81.3949 175.773 78.3284L175.059 79.0292ZM179.772 89.8328H178.772V307.611H179.772H180.772V89.8328H179.772ZM179.772 307.611H178.772C178.772 311.717 177.155 315.653 174.28 318.553L174.991 319.257L175.701 319.961C178.948 316.684 180.772 312.242 180.772 307.611H179.772ZM174.991 319.257L174.281 318.553C171.405 321.452 167.506 323.079 163.443 323.079V324.079V325.079C168.041 325.079 172.451 323.238 175.701 319.961L174.991 319.257ZM163.443 324.079V323.079C159.379 323.079 155.48 321.452 152.606 318.553L151.896 319.257L151.186 319.961C154.434 323.238 158.843 325.079 163.443 325.079V324.079ZM151.896 319.257L152.606 318.553C149.729 315.652 148.112 311.717 148.112 307.611H147.112H146.112C146.112 312.242 147.936 316.684 151.186 319.961L151.896 319.257ZM147.112 307.611H148.112V89.8328H147.112H146.112V307.611H147.112ZM229.44 81.6661H230.44C230.44 77.5617 232.057 73.6265 234.933 70.7264L234.223 70.0222L233.513 69.318C230.264 72.5944 228.44 77.0366 228.44 81.6661H229.44ZM234.223 70.0222L234.933 70.7264C237.81 67.8262 241.706 66.1974 245.771 66.1974V65.1974V64.1974C241.17 64.1974 236.763 66.0415 233.513 69.318L234.223 70.0222ZM245.771 65.1974V66.1974C249.833 66.1974 253.732 67.8263 256.608 70.7264L257.318 70.0222L258.028 69.318C254.778 66.0414 250.369 64.1974 245.771 64.1974V65.1974ZM257.318 70.0222L256.608 70.7264C259.484 73.6265 261.102 77.5617 261.102 81.6661H262.102H263.102C263.102 77.0366 261.278 72.5944 258.028 69.318L257.318 70.0222ZM262.102 81.6661H261.102V318.5H262.102H263.102V81.6661H262.102ZM262.102 318.5H261.102C261.102 322.605 259.485 326.54 256.608 329.442L257.318 330.146L258.028 330.85C261.278 327.572 263.102 323.13 263.102 318.5H262.102ZM257.318 330.146L256.608 329.442C253.732 332.342 249.833 333.969 245.771 333.969V334.969V335.969C250.369 335.969 254.778 334.127 258.028 330.851L257.318 330.146ZM245.771 334.969V333.969C241.706 333.969 237.809 332.342 234.933 329.442L234.223 330.146L233.513 330.851C236.763 334.127 241.17 335.969 245.771 335.969V334.969ZM234.223 330.146L234.933 329.442C232.057 326.54 230.44 322.605 230.44 318.5H229.44H228.44C228.44 323.13 230.264 327.572 233.513 330.85L234.223 330.146ZM229.44 318.5H230.44V81.6661H229.44H228.44V318.5H229.44ZM310.418 89.8328H311.418C311.418 85.7284 313.035 81.7932 315.912 78.893L315.202 78.1889L314.492 77.4847C311.242 80.7611 309.418 85.2033 309.418 89.8328H310.418ZM315.202 78.1889L315.912 78.8928C318.786 75.9931 322.685 74.3641 326.749 74.3641V73.3641V72.3641C322.149 72.3641 317.739 74.2081 314.491 77.4849L315.202 78.1889ZM326.749 73.3641V74.3641C330.811 74.3641 334.71 75.993 337.587 78.893L338.297 78.1889L339.007 77.4847C335.757 74.2081 331.348 72.3641 326.749 72.3641V73.3641ZM338.297 78.1889L337.586 78.8928C340.461 81.793 342.078 85.7283 342.078 89.8328H343.078H344.078C344.078 85.2033 342.254 80.7613 339.007 77.4849L338.297 78.1889ZM343.078 89.8328H342.078V307.611H343.078H344.078V89.8328H343.078ZM343.078 307.611H342.078C342.078 311.717 340.461 315.653 337.586 318.553L338.297 319.257L339.007 319.961C342.254 316.684 344.078 312.242 344.078 307.611H343.078ZM338.297 319.257L337.587 318.553C334.711 321.452 330.812 323.079 326.749 323.079V324.079V325.079C331.347 325.079 335.757 323.238 339.007 319.961L338.297 319.257ZM326.749 324.079V323.079C322.684 323.079 318.786 321.452 315.912 318.553L315.202 319.257L314.491 319.961C317.739 323.238 322.149 325.079 326.749 325.079V324.079ZM315.202 319.257L315.912 318.553C313.035 315.652 311.418 311.717 311.418 307.611H310.418H309.418C309.418 312.242 311.242 316.684 314.492 319.961L315.202 319.257ZM310.418 307.611H311.418V89.8328H310.418H309.418V307.611H310.418ZM429.592 0.00135809V-0.998642H61.4104V0.00135809V1.00136H429.592V0.00135809ZM61.4104 0.00135809L61.4171 -0.998619C53.2619 -1.05324 45.1766 0.513168 37.622 3.61527L38.0019 4.54032L38.3817 5.46537C45.6911 2.46394 53.5136 0.948495 61.4037 1.00134L61.4104 0.00135809ZM38.0019 4.54032L37.6222 3.61522C30.0698 6.71513 23.1971 11.2894 17.4012 17.071L18.1074 17.779L18.8136 18.4869C24.4234 12.8911 31.0743 8.46474 38.3816 5.46542L38.0019 4.54032ZM18.1074 17.779L17.4013 17.0708C11.603 22.8526 6.99511 29.7324 3.83682 37.3125L4.7599 37.6971L5.68298 38.0817C8.74092 30.7425 13.2018 24.0828 18.8135 18.4871L18.1074 17.779ZM4.7599 37.6971L3.83677 37.3126C0.680544 44.8908 -0.962887 53.0254 -0.99999 61.247L0 61.2516L0.99999 61.2561C1.03592 53.2948 2.62733 45.4184 5.68304 38.0816L4.7599 37.6971ZM0 61.2516H-1V428.751H0H1V61.2516H0ZM0 428.751L-0.99999 428.755C-0.962886 436.975 0.680557 445.109 3.83673 452.689L4.7599 452.305L5.68307 451.921C2.62732 444.582 1.03592 436.705 0.99999 428.746L0 428.751ZM4.7599 452.305L3.83686 452.69C6.99514 460.268 11.603 467.147 17.4013 472.929L18.1074 472.221L18.8135 471.513C13.2018 465.917 8.74089 459.258 5.68294 451.92L4.7599 452.305ZM18.1074 472.221L17.4012 472.929C23.1971 478.711 30.0698 483.285 37.6222 486.385L38.0019 485.46L38.3816 484.535C31.0743 481.535 24.4234 477.109 18.8136 471.513L18.1074 472.221ZM38.0019 485.46L37.622 486.385C45.1766 489.487 53.2619 491.053 61.4171 490.999L61.4104 489.999L61.4037 488.999C53.5136 489.052 45.6911 487.536 38.3817 484.535L38.0019 485.46ZM61.4104 489.999V490.999H429.592V489.999V488.999H61.4104V489.999ZM429.592 489.999L429.585 490.999C437.727 491.053 445.802 489.491 453.345 486.398L452.966 485.473L452.587 484.547C445.288 487.54 437.476 489.052 429.598 488.999L429.592 489.999ZM452.966 485.473L453.345 486.398C460.891 483.307 467.755 478.746 473.551 472.977L472.845 472.268L472.14 471.56C466.53 477.143 459.888 481.556 452.587 484.547L452.966 485.473ZM472.845 472.268L473.551 472.977C479.347 467.211 483.959 460.349 487.124 452.783L486.201 452.398L485.279 452.012C482.215 459.336 477.75 465.978 472.14 471.559L472.845 472.268ZM486.201 452.398L487.124 452.784C490.289 445.221 491.945 437.101 492 428.893L491 428.886L490 428.879C489.947 436.828 488.343 444.689 485.279 452.011L486.201 452.398ZM491 428.886H492V61.2516H491H490V428.886H491ZM491 61.2516L492 61.247C491.963 53.0362 490.324 44.9148 487.176 37.3433L486.253 37.7272L485.33 38.111C488.377 45.4417 489.964 53.3054 490 61.2561L491 61.2516ZM486.253 37.7272L487.176 37.3431C484.027 29.7716 479.43 22.9006 473.647 17.1191L472.94 17.8262L472.233 18.5334C477.83 24.1294 482.28 30.7807 485.33 38.1113L486.253 37.7272ZM472.94 17.8262L473.647 17.1191C467.866 11.3395 461.009 6.76314 453.472 3.65455L453.091 4.579L452.709 5.50346C460.002 8.51129 466.638 12.9398 472.233 18.5334L472.94 17.8262ZM453.091 4.579L453.472 3.65455C445.935 0.545961 437.865 -1.03577 429.72 -0.998632L429.725 0.00135809L429.729 1.00135C437.609 0.965417 445.417 2.49563 452.709 5.50346L453.091 4.579Z" fill="white" mask="url(#path-1-inside-1_1164_3)"/>
</svg>
```
--------------------------------------------------------------------------------
/apps/demo-day/frontend/styles.css:
--------------------------------------------------------------------------------
```css
:root {
--primary-color: rgb(255, 102, 51);
--text-color: #ffffff;
--background-color: #0a0a0a;
--input-background: rgba(255, 255, 255, 0.05);
--button-hover: #ff8533;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
background-color: var(--background-color);
color: var(--text-color);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
min-height: 100vh;
position: relative;
overflow-x: hidden;
overflow-y: auto;
width: 100%;
}
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background:
radial-gradient(
circle at 30% 50%,
rgba(255, 255, 255, 0.02) 0%,
rgba(255, 255, 255, 0.01) 30%,
transparent 60%
),
linear-gradient(
45deg,
rgba(10, 10, 10, 0.98) 0%,
rgba(15, 15, 15, 0.98) 50%,
rgba(10, 10, 10, 0.98) 100%
);
z-index: 0;
animation: gradientFlow 30s ease infinite;
}
@keyframes gradientFlow {
0% {
background-position:
0% 50%,
0% 50%;
}
50% {
background-position:
100% 50%,
100% 50%;
}
100% {
background-position:
0% 50%,
0% 50%;
}
}
.page-wrapper {
position: relative;
min-height: 100vh;
display: flex;
flex-direction: column;
z-index: 2;
min-width: 0;
width: 100%;
overflow-x: hidden;
overflow-y: hidden;
}
.container {
display: grid;
grid-template-columns: 1fr 1fr;
flex: 1;
min-width: 0;
width: 100%;
overflow: hidden;
}
/* Left Panel Styles */
.left-panel {
padding: 3rem 4rem;
display: flex;
flex-direction: column;
}
.header {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.logo {
display: flex;
align-items: flex-start;
}
.cloud-logo {
width: 140px;
height: auto;
}
.logo-text {
display: flex;
flex-direction: column;
font-weight: 700;
font-size: 1.75rem;
line-height: 1;
letter-spacing: -0.02em;
}
.content {
margin-top: 4rem;
}
.date-time {
display: inline-flex;
align-items: center;
gap: 1rem;
border-radius: 14px;
cursor: pointer;
transition: all 0.2s ease;
}
.date-time-text {
display: flex;
flex-direction: column;
gap: 0.25rem;
order: 2;
}
.date-time h2 {
font-size: 0.75rem;
font-weight: 600;
letter-spacing: 0.02em;
line-height: 1.2;
color: var(--text-color);
}
.date-time h3 {
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.6);
font-weight: 500;
letter-spacing: 0.02em;
line-height: 1.2;
}
.calendar-trigger {
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.1);
padding: 0.5rem;
color: rgba(255, 255, 255, 0.5);
transition: all 0.2s ease;
border-radius: 10px;
order: 1;
}
.date-time:hover .calendar-trigger {
color: var(--text-color);
transform: scale(1.05);
background: rgba(255, 255, 255, 0.06);
border-color: rgba(255, 255, 255, 0.2);
}
h1 {
font-size: 4rem;
line-height: 1;
margin-bottom: 2rem;
font-weight: 700;
letter-spacing: -0.03em;
}
.description {
font-size: 1.25rem;
color: rgba(255, 255, 255, 0.7);
max-width: 85%;
margin-bottom: 2.5rem;
line-height: 1.4;
}
.input-group {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
max-width: 560px;
position: relative;
flex-wrap: nowrap;
}
.input-group.success {
animation: successSlide 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
@keyframes successSlide {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-20px);
}
}
.success-message {
position: absolute;
left: 0;
top: calc(100% + 24px);
width: 100%;
opacity: 0;
transform: translateY(10px);
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
display: flex;
flex-direction: column;
gap: 1rem;
pointer-events: none;
}
.input-group.success .success-message {
opacity: 1;
transform: translateY(0);
pointer-events: all;
}
.success-text {
display: flex;
align-items: center;
gap: 0.75rem;
color: var(--text-color);
font-size: 0.875rem;
font-weight: 500;
}
.success-text svg {
width: 18px;
height: 18px;
color: var(--primary-color);
}
.calendar-actions {
display: flex;
gap: 0.75rem;
align-items: center;
flex-wrap: wrap;
}
.calendar-action {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 0.75rem;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 6px;
color: rgba(255, 255, 255, 0.8);
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
}
.calendar-action:hover {
background: rgba(255, 255, 255, 0.06);
border-color: rgba(255, 255, 255, 0.2);
transform: translateY(-1px);
}
.calendar-action svg {
width: 16px;
height: 16px;
opacity: 0.7;
transition: all 0.2s ease;
}
.calendar-action:hover svg {
opacity: 1;
}
input[type='email'] {
flex: 1;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.2);
min-height: 38px;
padding: 0 16px;
color: var(--text-color);
font-size: 14px;
border-radius: 6px;
font-family: 'Inter', sans-serif;
}
input[type='email']::placeholder {
color: rgba(255, 255, 255, 0.5);
}
input[type='email']:focus {
outline: none;
border-color: rgba(255, 255, 255, 0.4);
}
/* Add keyframes for glow animation */
@keyframes glowPulse {
0% {
box-shadow: 0 0 0 0 rgba(255, 107, 0, 0.4);
}
70% {
box-shadow: 0 0 0 10px rgba(255, 107, 0, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(255, 107, 0, 0);
}
}
.notify-btn {
text-decoration: none;
background: transparent;
border: 1px solid var(--primary-color);
color: var(--primary-color);
height: 38px;
padding: 8px 16px;
border-radius: 6px;
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
font-size: 14px;
font-weight: 500;
position: relative;
overflow: visible;
}
.notify-btn.success {
background: var(--primary-color);
color: var(--text-color);
border-color: var(--primary-color);
pointer-events: none;
}
.notify-btn.success svg {
animation: checkmark 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
@keyframes checkmark {
0% {
transform: scale(0);
opacity: 0;
}
50% {
transform: scale(1.2);
}
100% {
transform: scale(1);
opacity: 1;
}
}
.notify-btn:hover {
background: rgba(255, 107, 0, 0.1);
border-color: var(--primary-color);
animation: glowPulse 2s infinite;
}
.notify-btn svg {
transition: all 0.3s ease;
}
.notify-btn:hover svg {
filter: drop-shadow(0 0 8px rgba(255, 107, 0, 0.6));
transform: scale(1.02);
opacity: 1;
}
.calendar-links {
display: flex;
align-items: center;
gap: 1rem;
color: rgba(255, 255, 255, 0.6);
font-size: 0.875rem;
}
.calendar-icons {
display: flex;
gap: 0.75rem;
}
.calendar-icon {
color: rgba(255, 255, 255, 0.6);
transition: all 0.2s ease;
display: flex;
}
.calendar-icon:hover {
color: var(--text-color);
}
.calendar-icon svg {
transition: all 0.3s ease;
}
.calendar-icon:hover svg {
filter: drop-shadow(0 0 8px rgba(255, 255, 255, 0.4));
transform: scale(1.02);
}
/* Right Panel Styles */
.right-panel {
padding: 3rem 4rem;
position: relative;
border-left: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
flex-direction: column;
overflow: hidden;
}
.company-background {
position: absolute;
bottom: -120%;
right: -80%;
width: 200%;
height: 200%;
opacity: 0;
transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
pointer-events: none;
z-index: 1;
transform: scale(0.95);
}
.company-background.cloudflare {
right: -100%;
width: 180%;
height: 180%;
}
.company-background.active {
opacity: 1;
transform: scale(1);
}
.company-background svg {
width: 100%;
height: 100%;
object-fit: contain;
opacity: 0.15;
fill: none;
stroke: #ffffff;
filter: drop-shadow(0 0 20px rgba(255, 255, 255, 0.2))
drop-shadow(0 0 40px rgba(255, 255, 255, 0.1));
transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
.company-background svg path,
.company-background svg circle,
.company-background svg rect {
stroke: #ffffff;
stroke-width: 1;
transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
.company-background.active svg path,
.company-background.active svg circle,
.company-background.active svg rect {
stroke: #ffffff;
stroke-width: 1.5;
filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.3));
}
/* Add a subtle pulse animation for active backgrounds */
@keyframes pulseGlow {
0% {
filter: drop-shadow(0 0 20px rgba(255, 255, 255, 0.2))
drop-shadow(0 0 40px rgba(255, 255, 255, 0.1));
}
50% {
filter: drop-shadow(0 0 30px rgba(255, 255, 255, 0.3))
drop-shadow(0 0 60px rgba(255, 255, 255, 0.15));
}
100% {
filter: drop-shadow(0 0 20px rgba(255, 255, 255, 0.2))
drop-shadow(0 0 40px rgba(255, 255, 255, 0.1));
}
}
.company-background.active svg {
animation: pulseGlow 3s infinite ease-in-out;
}
.demos-section {
position: relative;
z-index: 3;
}
.demos-section h4 {
color: rgba(255, 255, 255, 0.5);
font-size: 0.875rem;
margin-bottom: 2.5rem;
font-weight: 500;
letter-spacing: 0.05em;
}
.demo-companies {
list-style: none;
position: relative;
z-index: 2;
}
.demo-companies li {
font-size: 2rem;
font-weight: 600;
margin-bottom: 0rem;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
letter-spacing: -0.02em;
line-height: 1.1;
cursor: pointer;
color: rgba(255, 255, 255, 0.3);
text-shadow: 0 0 8px rgba(255, 255, 255, 0.1);
}
.demo-companies li.active {
color: var(--primary-color);
text-shadow:
0 0 15px rgba(255, 107, 0, 0.5),
0 0 30px rgba(255, 107, 0, 0.2);
transform: scale(1.02);
}
.demo-companies li:hover {
color: var(--primary-color);
text-shadow:
0 0 15px rgba(255, 107, 0, 0.5),
0 0 30px rgba(255, 107, 0, 0.2);
transform: scale(1.02);
}
/* Company-specific background visibility */
.demo-companies li[data-company='atlassian']:hover ~ .company-background.atlassian,
.demo-companies li[data-company='canva']:hover ~ .company-background.canva,
.demo-companies li[data-company='linear']:hover ~ .company-background.linear,
.demo-companies li[data-company='paypal']:hover ~ .company-background.paypal,
.demo-companies li[data-company='sentry']:hover ~ .company-background.sentry,
.demo-companies li[data-company='cloudflare']:hover ~ .company-background.cloudflare,
.demo-companies li[data-company='webflow']:hover ~ .company-background.webflow {
opacity: 1;
}
/* Footer Styles */
footer {
padding: 2rem 4rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
}
.footer-left {
display: flex;
align-items: center;
gap: 3rem;
}
.cloudflare-logo {
height: 20px;
width: auto;
filter: brightness(0) invert(1);
opacity: 1;
}
.footer-links {
position: absolute;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 2rem;
}
.footer-links a {
color: rgba(255, 255, 255, 0.6);
text-decoration: none;
transition: color 0.2s ease;
font-size: 0.875rem;
}
.footer-links a:hover {
color: var(--text-color);
}
.build-btn {
background: transparent;
border: 1px solid var(--primary-color);
color: var(--primary-color);
height: 38px;
padding: 8px 16px;
border-radius: 6px;
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
text-decoration: none;
transition: all 0.2s ease;
font-size: 14px;
font-weight: 500;
position: relative;
overflow: visible;
}
.build-btn:hover {
background: rgba(255, 107, 0, 0.1);
border-color: var(--primary-color);
animation: glowPulse 2s infinite;
}
.build-btn img {
width: 24px;
height: 24px;
opacity: 0.7;
transition: all 0.3s ease;
}
.build-btn:hover img {
filter: drop-shadow(0 0 8px rgba(255, 107, 0, 0.6));
transform: scale(1.02);
opacity: 1;
}
/* Responsive Design */
@media (max-width: 1400px) {
h1 {
font-size: 4rem;
}
.demo-companies li {
font-size: 2.75rem;
}
}
@media (max-width: 1200px) {
h1 {
font-size: 3.5rem;
}
.demo-companies li {
font-size: 2.5rem;
}
.description {
max-width: 100%;
}
.input-group {
max-width: 560px;
}
}
@media (max-width: 968px) {
body {
height: auto;
overflow: auto;
}
.container {
grid-template-columns: 1fr;
}
.right-panel {
padding: 2rem;
border-left: none;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.left-panel {
padding: 2rem;
}
h1 {
font-size: 3rem;
}
.input-group {
max-width: 100%;
flex-wrap: nowrap;
gap: 0.75rem;
}
input[type='email'] {
min-width: 0;
flex: 1;
}
.notify-btn {
width: fit-content;
min-height: 38px;
}
footer {
padding: 2rem;
flex-direction: column;
gap: 2rem;
}
.footer-links {
position: static;
transform: none;
margin: 0 auto;
}
}
@media (max-width: 480px) {
.left-panel,
.right-panel {
padding: 1rem;
min-width: 0;
width: 100%;
}
.container {
min-width: 0;
width: 100%;
overflow: hidden;
}
.page-wrapper {
min-width: 0;
width: 100%;
overflow-x: hidden;
}
.date-time {
padding: 0.5rem;
}
/*.date-time-text {
display: none;
}*/
.calendar-trigger {
order: 1;
margin: 0;
}
.input-group {
flex-direction: column;
gap: 0.75rem;
width: 100%;
}
.input-group.success {
margin-bottom: 125px;
}
input[type='email'] {
width: 100%;
min-width: 0;
height: 38px;
}
.notify-btn {
width: 100%;
justify-content: center;
height: 38px;
}
h1 {
font-size: 2.5rem;
max-width: 100%;
}
.description {
font-size: 1rem;
max-width: 100%;
}
.demo-companies li {
font-size: 2rem;
}
.footer-left {
flex-direction: column;
gap: 1.5rem;
}
.footer-links {
flex-direction: column;
align-items: center;
gap: 1rem;
}
}
/* Add a subtle light effect */
.left-panel::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(
90deg,
transparent 0%,
rgba(255, 255, 255, 0.01) 30%,
rgba(255, 255, 255, 0.005) 50%,
transparent 100%
);
z-index: -1;
pointer-events: none;
animation: lightBeam 15s ease-in-out infinite;
transform-origin: left;
filter: blur(40px);
}
@keyframes lightBeam {
0%,
100% {
opacity: 0.3;
transform: scaleX(0.9) translateX(-5%);
}
50% {
opacity: 0.4;
transform: scaleX(1.1) translateX(5%);
}
}
/* Add a subtle depth effect */
.left-panel::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: radial-gradient(circle at 30% 50%, rgba(255, 255, 255, 0.01) 0%, transparent 60%);
z-index: -2;
pointer-events: none;
animation: depthPulse 20s ease-in-out infinite;
filter: blur(50px);
}
@keyframes depthPulse {
0%,
100% {
opacity: 0.2;
transform: scale(1);
}
50% {
opacity: 0.3;
transform: scale(1.05);
}
}
/* Remove the old animations */
.starfield,
.floating-particle,
body::after {
display: none;
}
.calendar-popover {
background: rgb(23, 23, 23);
border-radius: 12px;
padding: 1.5rem;
width: 320px;
box-shadow:
0 0 0 1px rgba(255, 255, 255, 0.1),
0 8px 32px rgba(0, 0, 0, 0.4),
0 2px 8px rgba(0, 0, 0, 0.2);
position: relative;
animation: modalAppear 0.2s cubic-bezier(0.21, 1.02, 0.73, 1);
transform-origin: center center;
}
@keyframes modalAppear {
from {
opacity: 0;
transform: scale(0.98);
}
to {
opacity: 1;
transform: scale(1);
}
}
.calendar-popover h4 {
font-size: 1.125rem;
font-weight: 600;
margin-bottom: 1rem;
color: var(--text-color);
padding-right: 2rem;
}
.calendar-options {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.calendar-option {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem;
border-radius: 8px;
background: transparent;
border: none;
cursor: pointer;
transition: all 0.15s ease;
color: rgba(255, 255, 255, 0.8);
width: 100%;
text-align: left;
}
.calendar-option:hover {
background: rgba(255, 255, 255, 0.06);
color: var(--text-color);
}
.calendar-option svg {
width: 20px;
height: 20px;
opacity: 0.7;
transition: all 0.2s ease;
}
.calendar-option:hover svg {
opacity: 1;
}
.calendar-option span {
font-size: 0.875rem;
font-weight: 500;
}
.close-button {
position: absolute;
top: 1rem;
right: 1rem;
background: transparent;
border: none;
color: rgba(255, 255, 255, 0.5);
padding: 0.375rem;
cursor: pointer;
transition: all 0.2s ease;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
}
.close-button:hover {
background: rgba(255, 255, 255, 0.06);
color: var(--text-color);
}
.close-button svg {
width: 16px;
height: 16px;
}
dialog {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) !important;
margin: 0;
background: transparent;
border: none;
padding: 0;
width: fit-content;
height: fit-content;
}
dialog::backdrop {
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(4px);
animation: backdropFade 0.2s ease;
}
@keyframes backdropFade {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.attendees {
display: flex;
align-items: center;
gap: 0.75rem;
margin-top: 1rem;
font-size: 0.875rem;
color: rgba(255, 255, 255, 0.7);
opacity: 1;
transform: translateY(0);
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.input-group.success ~ .attendees {
opacity: 0;
transform: translateY(-10px);
pointer-events: none;
}
.attendee-avatars {
display: flex;
align-items: center;
}
.attendee-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
overflow: visible;
position: relative;
border: 2px solid rgba(255, 255, 255, 0.1);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
margin-right: -8px;
background: var(--background-color);
}
.attendee-avatar:hover {
transform: translateY(-2px);
border-color: rgba(255, 255, 255, 0.3);
z-index: 2;
margin-right: 4px;
}
.attendee-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 50%;
}
.attendee-avatar[data-tooltip] {
cursor: pointer;
}
.attendee-avatar[data-tooltip]::before {
content: attr(data-tooltip);
position: absolute;
bottom: calc(100% + 8px);
left: 50%;
transform: translateX(-50%);
padding: 0.5rem 0.75rem;
background: rgba(23, 23, 23, 0.95);
color: white;
font-size: 0.875rem;
border-radius: 6px;
white-space: nowrap;
opacity: 0;
visibility: hidden;
transition: all 0.2s ease;
backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.1);
z-index: 1000;
pointer-events: none;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
width: max-content;
}
.attendee-avatar[data-tooltip]::after {
content: '';
position: absolute;
bottom: calc(100% + 4px);
left: 50%;
transform: translateX(-50%);
border: 6px solid transparent;
border-top-color: rgba(23, 23, 23, 0.95);
opacity: 0;
visibility: hidden;
transition: all 0.2s ease;
z-index: 1000;
pointer-events: none;
}
.attendee-avatar[data-tooltip]:hover::before,
.attendee-avatar[data-tooltip]:hover::after {
opacity: 1;
visibility: visible;
transform: translateX(-50%) translateY(-4px);
pointer-events: none;
}
.attendee-count {
font-weight: 500;
margin-left: 4px;
}
.attendee-count strong {
color: var(--text-color);
font-weight: 600;
}
.particle {
position: fixed;
pointer-events: none;
border-radius: 50%;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.05));
box-shadow: 0 0 4px rgba(255, 255, 255, 0.1);
opacity: 0;
z-index: 10;
will-change: transform, opacity;
}
@keyframes floatUp {
0% {
opacity: 0;
transform: translateY(0) scale(1);
}
15% {
opacity: 0.3;
}
50% {
opacity: 0.2;
}
85% {
opacity: 0.1;
}
100% {
opacity: 0;
transform: translateY(-100vh) scale(0.8);
}
}
/* Confetti piece */
.confetti {
position: absolute;
pointer-events: none;
transform-origin: center;
mix-blend-mode: screen;
will-change: transform, opacity;
}
@keyframes confettiFall {
0% {
transform: translateY(0) rotate(0deg) scale(0);
opacity: 1;
}
100% {
transform: translateY(20px) rotate(360deg) scale(1);
opacity: 0;
}
}
@keyframes successPop {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
.notify-btn.success {
animation: successPop 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Honeypot field - hide it from users but keep it visible to bots */
.contact-field {
display: none !important;
position: absolute !important;
left: -9999px !important;
}
.toast {
position: fixed;
bottom: 24px;
left: 50%;
transform: translateX(-50%);
background: rgba(17, 17, 17, 0.95);
color: #fff;
padding: 12px 24px;
border-radius: 8px;
font-size: 14px;
z-index: 1000;
opacity: 0;
transition: opacity 0.3s ease;
backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.2);
pointer-events: none;
}
.toast.show {
opacity: 1;
}
```
--------------------------------------------------------------------------------
/apps/dex-analysis/src/tools/dex-analysis.tools.ts:
--------------------------------------------------------------------------------
```typescript
import { z } from 'zod'
import { fetchCloudflareApi } from '@repo/mcp-common/src/cloudflare-api'
import { getEnv } from '@repo/mcp-common/src/env'
import { getProps } from '@repo/mcp-common/src/get-props'
import { getReader } from '../warp_diag_reader'
import type { ToolCallback } from '@modelcontextprotocol/sdk/server/mcp.js'
import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js'
import type { ZodRawShape, ZodTypeAny } from 'zod'
import type { CloudflareDEXMCP } from '../dex-analysis.app'
import type { Env } from '../dex-analysis.context'
const env = getEnv<Env>()
export function registerDEXTools(agent: CloudflareDEXMCP) {
registerTool({
name: 'dex_test_statistics',
description: 'Analyze Cloudflare DEX Test Results by quartile given a Test ID',
schema: {
testId: z.string().describe('The DEX Test ID to analyze details of.'),
from: timeStartParam,
to: timeEndParam,
},
llmContext:
"The quartiles are sorted by 'resource fetch time' from LEAST performant in quartile 1 to MOST performant in quartile 4. For each quartile-based entry, it provides extensive information about the up-to-20 specific test results that are within that quartile of performance.",
agent,
callback: async ({ accountId, accessToken, ...params }) => {
return await fetchCloudflareApi({
endpoint: `/dex/test-results/by-quartile?${new URLSearchParams({ ...(params as Record<string, string>) })}`,
accountId,
apiToken: accessToken,
options: {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
},
})
},
})
registerTool({
name: 'dex_list_tests',
description: 'Retrieve a list of all Cloudflare DEX Tests configured.',
agent,
schema: { page: pageParam },
callback: async ({ accountId, accessToken, page }) => {
return await fetchCloudflareApi({
endpoint: `/dex/tests/overview?page=${page}&per_page=50`,
accountId,
apiToken: accessToken,
options: {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
},
})
},
})
registerTool({
name: 'dex_http_test_details',
description: 'Retrieve detailed time series results for an HTTP DEX test by id.',
schema: {
testId: z.string().describe('The HTTP DEX Test ID to get details for.'),
deviceId: z
.string()
.optional()
.describe(
"Optionally limit results to specific device(s). Can't be used in conjunction with the colo parameter."
),
colo: z
.string()
.optional()
.describe('Optionally limit results to a specific Cloudflare colo.'),
from: timeStartParam,
to: timeEndParam,
interval: aggregationIntervalParam,
},
agent,
callback: async ({ testId, accountId, accessToken, ...params }) => {
return await fetchCloudflareApi({
endpoint: `/dex/http-tests/${testId}?${new URLSearchParams({ ...(params as Record<string, string>) })}`,
accountId,
apiToken: accessToken,
options: {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
},
})
},
})
registerTool({
name: 'dex_traceroute_test_details',
description: 'Retrieve detailed time series results for a Traceroute DEX test by id.',
schema: {
testId: z.string().describe('The traceroute DEX Test ID to get details for.'),
deviceId: z
.string()
.optional()
.describe(
"Optionally limit results to specific device(s). Can't be used in conjunction with the colo parameter."
),
colo: z
.string()
.optional()
.describe('Optionally limit results to a specific Cloudflare colo.'),
timeStart: timeStartParam,
timeEnd: timeEndParam,
interval: aggregationIntervalParam,
},
agent,
callback: async ({ testId, accountId, accessToken, ...params }) => {
return await fetchCloudflareApi({
endpoint: `/dex/traceroute-tests/${testId}?${new URLSearchParams({ ...(params as Record<string, string>) })}`,
accountId,
apiToken: accessToken,
options: {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
},
})
},
})
registerTool({
name: 'dex_traceroute_test_network_path',
description:
'Retrieve aggregate network path data for a Traceroute DEX test by id. Use the dex_traceroute_test_result_network_path tool to further explore individual test runs hop-by-hop.',
schema: {
testId: z.string().describe('The traceroute DEX Test ID to get network path details for.'),
deviceId: z.string().describe('The ID of the device to get network path details for.'),
from: timeStartParam,
to: timeEndParam,
interval: aggregationIntervalParam,
},
agent,
callback: async ({ testId, accountId, accessToken, ...params }) => {
return await fetchCloudflareApi({
endpoint: `/dex/traceroute-tests/${testId}/network-path?${new URLSearchParams({ ...(params as unknown as Record<string, string>) })}`,
accountId,
apiToken: accessToken,
options: {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
},
})
},
})
registerTool({
name: 'dex_traceroute_test_result_network_path',
description:
'Retrieve the hop-by-hop network path for a specific Traceroute DEX test result by id.',
schema: {
testResultId: z
.string()
.describe('The traceroute DEX Test Result ID to get network path details for.'),
},
agent,
callback: async ({ testResultId, accountId, accessToken }) => {
return await fetchCloudflareApi({
endpoint: `/dex/traceroute-test-results/${testResultId}/network-path`,
accountId,
apiToken: accessToken,
options: {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
},
})
},
})
registerTool({
name: 'dex_list_remote_capture_eligible_devices',
description:
"Retrieve a list of devices eligible for remote captures. You'll need the device_id and user_email from this " +
'response in order to create a remote capture for a specific device. It can also be used as a generic source to find ' +
'devices registered to the account, filtering by user email if necessary.',
schema: {
page: pageParam,
search: z.string().optional().describe('Filter devices by name or email.'),
},
agent,
callback: async ({ accountId, accessToken, ...params }) => {
return await fetchCloudflareApi({
endpoint: `/dex/commands/devices?${new URLSearchParams({ ...(params as unknown as Record<string, string>) })}&per_page=50`,
accountId,
apiToken: accessToken,
options: {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
},
})
},
})
registerTool({
name: 'dex_create_remote_pcap',
description:
'Create a remote packet capture (PCAP) for a device. This is a resource intensive and privacy-sensitive operation on a real user device.' +
'Always ask for confirmation from the user that the targeted email and device are correct before executing a capture',
schema: {
device_id: z.string().describe('The device ID to target.'),
user_email: z.string().describe('The email of the user associated with the device.'),
'max-file-size-mb': z
.number()
.min(1)
.default(5)
.optional()
.describe(
'Maximum file size in MB for the capture file. Specifies the maximum file size of the warp-daig zip artifact that can be uploaded. ' +
'If the zip artifact exceeds the specified max file size it will NOT be uploaded.'
),
'packet-size-bytes': z
.number()
.min(1)
.default(160)
.optional()
.describe('Maximum number of bytes to save for each packet.'),
'time-limit-min': z
.number()
.min(1)
.default(5)
.describe('Limit on capture duration in minutes'),
},
agent,
llmContext:
'If the request was successful, the capture has been initiated. You can poll the dex_list_remote_commands tool periodically to check on the completion status.',
callback: async ({ accountId, accessToken, device_id, user_email, ...command_args }) => {
return await fetchCloudflareApi({
endpoint: `/dex/commands`,
accountId,
apiToken: accessToken,
options: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
commands: [
{
type: 'pcap',
device_id,
user_email,
args: command_args,
version: 1,
},
],
}),
},
})
},
})
registerTool({
name: 'dex_create_remote_warp_diag',
description:
'Create a remote Warp Diagnostic (WARP-diag) for a device. This is a resource intensive and privacy-sensitive operation on a real user device.' +
'Always ask for confirmation from the user that the targeted email and device are correct before executing a capture',
schema: {
device_id: z.string().describe('The device ID to target.'),
user_email: z.string().describe('The email of the user associated with the device.'),
'test-all-routes': z
.boolean()
.default(true)
.describe(
'Test an IP address from all included or excluded ranges. Tests an IP address from all included or excluded ranges.' +
"Essentially the same as running 'route get '' and collecting the results. This option may increase the time taken to collect the warp-diag"
),
},
agent,
llmContext:
'If the request was successful, the diagnostic has been initiated. You can poll the dex_list_remote_commands tool periodically to check on the completion status.' +
'See https://developers.cloudflare.com/cloudflare-one/connections/connect-devices/warp/troubleshooting/warp-logs/ for more info on warp-diags',
callback: async ({ accountId, accessToken, device_id, user_email, ...command_args }) => {
return await fetchCloudflareApi({
endpoint: `/dex/commands`,
accountId,
apiToken: accessToken,
options: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
commands: [
{
type: 'warp-diag',
device_id,
user_email,
args: command_args,
version: 1,
},
],
}),
},
})
},
})
registerTool({
name: 'dex_list_remote_captures',
description:
'Retrieve a list of remote captures for device debugging, like PCAPs or WARP Diags.',
schema: { page: pageParam },
agent,
callback: async ({ accountId, accessToken, page }) => {
return await fetchCloudflareApi({
endpoint: `/dex/commands?page=${page}&per_page=50`,
accountId,
apiToken: accessToken,
options: {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
},
})
},
})
registerTool({
name: 'dex_fleet_status_live',
description:
'Retrieve details about the real-time status of the fleet of devices broken down by dimension (mode, status, colo, platform, version)',
schema: {
since_minutes: z
.number()
.min(1)
.max(60)
.default(10)
.describe(
'Number of minutes before current time to use as cutoff for device states to include.'
),
colo: z
.string()
.optional()
.describe('Optionally filter results to a specific Cloudflare colo.'),
},
agent,
callback: async ({ accountId, accessToken, ...params }) => {
return await fetchCloudflareApi({
endpoint: `/dex/fleet-status/live?${new URLSearchParams({ ...(params as unknown as Record<string, string>) })}`,
accountId,
apiToken: accessToken,
options: {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
},
})
},
})
registerTool({
name: 'dex_fleet_status_over_time',
description:
'Retrieve aggregate time series details about the status of the fleet of devices, or performance metrics for a specific device, over the specified time period.',
schema: {
from: timeStartParam,
to: timeEndParam,
interval: aggregationIntervalParam,
colo: z
.string()
.optional()
.describe('Filter results to WARP devices connected to a specific colo.'),
device_id: z.string().optional().describe('Filter results to a specific device.'),
},
agent,
callback: async ({ accountId, accessToken, ...params }) => {
return await fetchCloudflareApi({
endpoint: `/dex/fleet-status/over-time?${new URLSearchParams({ ...(params as Record<string, string>) })}`,
accountId,
apiToken: accessToken,
options: {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
},
})
},
})
registerTool({
name: 'dex_fleet_status_logs',
description:
'Retrieve raw fleet status device logs with a variety of levels of granularity and filtering. Use `source=last_seen` to view logs showing the last known ' +
'state per device within the specified time period. Use `source=hourly` to view logs showing an hourly rollup per device where values are the average value of all' +
'events within the time period. Use `source=raw` to view all logs for the specified period.',
schema: {
page: pageParam,
from: timeStartParam,
to: timeEndParam,
source: z
.enum(['last_seen', 'hourly', 'raw'])
.describe('Specifies the granularity of results.'),
colo: z
.string()
.optional()
.describe('Filter results to WARP devices connected to a specific colo.'),
device_id: z.string().optional().describe('Filter results to a specific device.'),
mode: z.string().optional().describe('Filter results to devices with a specific WARP mode.'),
platform: z
.string()
.optional()
.describe('Filter results to devices on a specific operating system.'),
status: z
.string()
.optional()
.describe('Filter results to devices with a specific WARP connection status.'),
version: z
.string()
.optional()
.describe('Filter results to devices with a specific WARP client version.'),
},
agent,
callback: async ({ accountId, accessToken, ...params }) => {
return await fetchCloudflareApi({
endpoint: `/dex/fleet-status/devices?${new URLSearchParams({ ...(params as unknown as Record<string, string>) })}&per_page=50`,
accountId,
apiToken: accessToken,
options: {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
},
})
},
})
registerTool({
name: 'dex_list_warp_change_events',
description: 'View logs of events when users toggle WARP on or off, or change configurations.',
schema: {
from: timeStartParam,
to: timeEndParam,
page: pageParam,
account_name: z.string().optional().describe('Optionally filter events by account name.'),
config_name: z
.string()
.optional()
.describe(
'Optionally filter events by WARP configuration name changed from or to. Applicable to `type=config` events only.'
),
sort_order: z
.enum(['ASC', 'DESC'])
.optional()
.default('ASC')
.describe('Set timestamp sort order.'),
toggle: z
.enum(['on', 'off'])
.optional()
.describe(
'Optionally filter events by toggle value. Applicable to `type=toggle` events only.'
),
type: z.enum(['config', 'toggle']).optional().describe('Optionally filter events by type.'),
},
agent,
callback: async ({ accountId, accessToken, ...params }) => {
return await fetchCloudflareApi({
endpoint: `/dex/warp-change-events?${new URLSearchParams({ ...(params as unknown as Record<string, string>) })}&per_page=50`,
accountId,
apiToken: accessToken,
options: {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
},
})
},
})
registerTool({
name: 'dex_list_colos',
description:
'View a list of Cloudflare colos sorted alphabetically or by frequency encountered in fleet status or DEX test data.',
schema: {
from: timeStartParam,
to: timeEndParam,
sortBy: z
.enum(['fleet-status-usage', 'application-tests-usage'])
.optional()
.describe(
'Use `fleet-status-usage` to sort by frequency seen in device state checkins.' +
'Use `application-tests-usage` to sort by frequency seen in DEX test results. Omit to sort alphabetically.'
),
},
agent,
callback: async ({ accountId, accessToken, ...params }) => {
return await fetchCloudflareApi({
endpoint: `/dex/colos?${new URLSearchParams({ ...(params as unknown as Record<string, string>) })}`,
accountId,
apiToken: accessToken,
options: {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
},
})
},
})
registerTool({
name: 'dex_list_remote_warp_diag_contents',
description:
'Given a WARP diag remote capture download url, returns a list of the files contained in the archive.',
schema: {
download: z
.string()
.describe(
'The `filename` url from the dex_list_remote_captures response for successful WARP diag captures.'
),
},
llmContext:
'Use the dex_explore_remote_warp_diag_output tool for specific file paths to explore the file contents for analysis. ' +
'Hint: you can call dex_explore_remote_warp_diag_output multiple times in parallel if necessary to take advantage of in-memory caching for best performance.' +
'See https://developers.cloudflare.com/cloudflare-one/connections/connect-devices/warp/troubleshooting/warp-logs/ for more info on warp-diags',
agent,
callback: async ({ accessToken, download }) => {
const reader = await getReader(env, accessToken, download)
return await reader.list(accessToken, download)
},
})
registerTool({
name: 'dex_explore_remote_warp_diag_output',
description:
'Explore the contents of remote capture WARP diag archive filepaths returned by the dex_list_remote_warp_diag_contents tool for analysis.',
schema: {
download: z
.string()
.describe(
'The `filename` url from the dex_list_remote_captures response for successful WARP diag captures.'
),
filepath: z.string().describe('The file path from the archive to retrieve contents for.'),
},
llmContext:
'To avoid hitting conversation and memory limits, avoid outputting the whole contents of these files to the user unless specifically asked to. Instead prefer to show relevant snippets only.',
agent,
callback: async ({ accessToken, download, filepath }) => {
const reader = await getReader(env, accessToken, download)
return await reader.read(accessToken, download, filepath)
},
})
registerTool({
name: 'dex_analyze_warp_diag',
description:
'Analyze successful WARP-diag remote captures for common issues. This should be the first place you start when trying to narrow down device-level issues with WARP.',
schema: {
command_id: z
.string()
.describe('The command_id of the successful WARP-diag remote capture to analyze.'),
},
llmContext:
'Detections with 0 occurences can be ruled out. Focus on detections with the highest severity.',
agent,
callback: async ({ accessToken, accountId, command_id }) => {
return await fetchCloudflareApi({
endpoint: `/dex/commands/${command_id}/analysis`,
accountId,
apiToken: accessToken,
options: {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
},
})
},
})
}
// Helper to simplify tool registration by reducing boilerplate for accountId and accessToken
const registerTool = <T extends ZodRawShape, U = unknown>({
name,
description,
agent,
callback,
schema = {},
llmContext = '',
}: {
name: string
description: string
schema?: T | ToolAnnotations
llmContext?: string
agent: CloudflareDEXMCP
callback: (
p: { extra: unknown; accountId: string; accessToken: string } & z.objectOutputType<
T,
ZodTypeAny
>
) => Promise<U>
}) => {
agent.server.tool<T>(name, description, schema, (async (params, extra) => {
const accountId = await agent.getActiveAccountId()
if (!accountId) {
return {
content: [
{
type: 'text',
text: 'No currently active accountId. Try listing your accounts (accounts_list) and then setting an active account (set_active_account)',
},
],
}
}
try {
const props = getProps(agent)
const accessToken = props.accessToken
const res = await callback({ ...(params as T), extra, accountId, accessToken })
return {
content: [
{
type: 'text',
text: JSON.stringify({
data: res,
llmContext,
}),
},
],
}
} catch (error) {
return {
content: [
{
type: 'text',
text: JSON.stringify({
error: `Error with tool ${name}: ${error instanceof Error && error.message}`,
}),
},
],
}
}
}) as ToolCallback<T>)
}
// Shared parameter schemas
const timeStartParam = z
.string()
.describe(
'The datetime of the beginning point of time range for results. Must be in ISO 8601 datetime string in the extended format with UTC time (e.g, 2025-04-21T18:00:00Z).'
)
const timeEndParam = z
.string()
.describe(
'The datetime of the ending point of time range for results. Must be in ISO 8601 datetime string in the extended format with UTC time (e.g, 2025-04-22T00:00:00Z).'
)
const aggregationIntervalParam = z
.enum(['minute', 'hour'])
.describe('The time interval to group results by.')
const pageParam = z.number().min(1).describe('The page of results to retrieve.')
```