├── .changeset ├── README.md └── config.json ├── .dockerignore ├── .editorconfig ├── .eslintrc.cjs ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md ├── actions │ └── setup │ │ └── action.yml └── workflows │ ├── branches.yml │ ├── evals.yml │ ├── main.yml │ └── release.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc.cjs ├── .syncpackrc.cjs ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── apps ├── ai-gateway │ ├── .dev.vars.example │ ├── .eslintrc.cjs │ ├── CHANGELOG.md │ ├── CONTRIBUTING.md │ ├── README.md │ ├── package.json │ ├── 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 │ ├── README.md │ ├── package.json │ ├── 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 │ ├── README.md │ ├── package.json │ ├── 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 │ ├── README.md │ ├── package.json │ ├── 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 │ ├── README.md │ ├── package.json │ ├── 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.svg │ │ │ ├── cloudflare_logo.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 │ ├── README.md │ ├── package.json │ ├── src │ │ ├── api │ │ │ └── dex.ts │ │ ├── dex-analysis.app.ts │ │ ├── dex-analysis.context.ts │ │ └── tools │ │ │ └── dex-analysis.tools.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 │ ├── README.md │ ├── package.json │ ├── 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-autorag │ ├── .eslintrc.cjs │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── 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 │ ├── README.md │ ├── package.json │ ├── src │ │ ├── docs-vectorize.app.ts │ │ ├── docs-vectorize.context.ts │ │ ├── prompts │ │ │ └── docs-vectorize.prompts.ts │ │ └── tools │ │ │ └── docs-vectorize.tools.ts │ ├── tsconfig.json │ ├── vitest.config.ts │ ├── worker-configuration.d.ts │ └── wrangler.jsonc ├── graphql │ ├── .eslintrc.cjs │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── 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 │ ├── README.md │ ├── package.json │ ├── 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 │ ├── README.md │ ├── package.json │ ├── 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 │ ├── CONTRIBUTING.md │ ├── Dockerfile │ ├── README.md │ ├── container │ │ ├── fileUtils.spec.ts │ │ ├── fileUtils.ts │ │ ├── sandbox.container.app.ts │ │ └── tsconfig.json │ ├── evals │ │ ├── exec.eval.ts │ │ ├── files.eval.ts │ │ ├── initialize.eval.ts │ │ └── utils.ts │ ├── package.json │ ├── 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 │ ├── README.md │ ├── evals │ │ ├── accounts.eval.ts │ │ ├── hyperdrive.eval.ts │ │ ├── kv_namespaces.eval.ts │ │ ├── types.d.ts │ │ └── utils.ts │ ├── package.json │ ├── 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 │ ├── README.md │ ├── package.json │ ├── 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 │ ├── README.md │ ├── package.json │ ├── 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 ├── implementation-guides ├── evals.md ├── tools.md └── type-validators.md ├── package.json ├── packages ├── eslint-config │ ├── README.md │ ├── default.cjs │ └── package.json ├── eval-tools │ ├── package.json │ ├── src │ │ ├── runTask.ts │ │ ├── scorers.ts │ │ └── test-models.ts │ ├── tsconfig.json │ ├── worker-configuration.d.ts │ └── wrangler.json ├── mcp-common │ ├── .eslintrc.cjs │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── api-handler.ts │ │ ├── api-token-mode.ts │ │ ├── api │ │ │ ├── account.api.ts │ │ │ ├── cf1-integration.api.ts │ │ │ ├── workers-builds.api.ts │ │ │ ├── workers-observability.api.ts │ │ │ ├── workers.api.ts │ │ │ └── zone.api.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 │ │ ├── mcp-error.ts │ │ ├── poll.ts │ │ ├── scopes.ts │ │ ├── sentry.ts │ │ ├── server.ts │ │ ├── tools │ │ │ ├── account.tools.ts │ │ │ ├── d1.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 │ │ └── v4-api.ts │ ├── tests │ │ └── utils │ │ │ └── cloudflare-mock.ts │ ├── tsconfig.json │ ├── types.d.ts │ ├── vitest.config.ts │ └── worker-configuration.d.ts ├── mcp-observability │ ├── package.json │ ├── src │ │ ├── analytics-engine.ts │ │ ├── index.ts │ │ └── metrics.ts │ ├── tsconfig.json │ └── worker-configuration.d.ts ├── tools │ ├── .eslintrc.cjs │ ├── CHANGELOG.md │ ├── README.md │ ├── 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 │ ├── package.json │ ├── 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 ├── tsconfig.json ├── turbo.json └── vitest.workspace.ts /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "restricted", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [], 11 | "privatePackages": { 12 | "version": true, 13 | "tag": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ 2 | **/.gitignore 3 | **/README.md 4 | **/.vscode/ 5 | **/Dockerfile 6 | **/out/ -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = tab 8 | indent_size = 2 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | 14 | [*.{yml,yaml,mdx}] 15 | indent_style = space 16 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | // This configuration only applies to the package manager root. 2 | /** @type {import("eslint").Linter.Config} */ 3 | module.exports = { 4 | ignorePatterns: ['apps/**', 'packages/**'], 5 | extends: ['@repo/eslint-config/default.cjs'], 6 | } 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Client Information** 11 | - LLM Client: [e.g. Claude Desktop, claude.ai, Cursor, VSCode] 12 | - Client Config: [e.g. claude_desktop_config.json or screenshot of client configuration UI] 13 | - Using MCP Remote?: Yes/No 14 | - MCP Server: [e.g. https://observability.mcp.cloudflare.com, https://docs.mcp.cloudflare.com] 15 | - Prompt: [if applicable, add the prompt used] 16 | 17 | **Describe the bug** 18 | A clear and concise description of what the bug is. Any error logs are helpful. 19 | 20 | **To Reproduce** 21 | Steps to reproduce the behavior: 22 | 1. Connect to MCP Server... 23 | 2. Provide LLM prompt "..." 24 | 4. See error... 25 | 26 | **Expected behavior** 27 | A clear and concise description of what you expected to happen. 28 | 29 | **Additional context** 30 | Add any other context about the problem here. 31 | -------------------------------------------------------------------------------- /.github/actions/setup/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Setup Node.js Environment' 2 | description: 'Install pnpm, Node.js, and project dependencies' 3 | 4 | inputs: 5 | node-version: 6 | description: 'Node.js version to use' 7 | required: false 8 | default: '22' 9 | 10 | runs: 11 | using: "composite" 12 | steps: 13 | - name: Install pnpm 14 | # note: version is inferred from the packageManager field in package.json 15 | uses: pnpm/action-setup@v4 16 | 17 | - name: Use Node.js 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: ${{ inputs.node-version }} 21 | cache: 'pnpm' 22 | 23 | - name: Install dependencies 24 | shell: bash 25 | run: pnpm install --frozen-lockfile --child-concurrency=10 26 | -------------------------------------------------------------------------------- /.github/workflows/branches.yml: -------------------------------------------------------------------------------- 1 | name: Branches 2 | on: 3 | push: 4 | branches-ignore: ['main'] 5 | 6 | env: 7 | FORCE_COLOR: 1 8 | 9 | jobs: 10 | test: 11 | name: Test & Check 12 | runs-on: ubuntu-24.04 13 | permissions: 14 | contents: read 15 | timeout-minutes: 10 16 | strategy: 17 | matrix: 18 | node-version: [20, 22] 19 | steps: 20 | - uses: actions/checkout@v4 21 | - uses: ./.github/actions/setup 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | 25 | - name: Syncpack lint 26 | run: pnpm check:deps 27 | - name: Run linter 28 | run: pnpm check:turbo 29 | - name: Run linter (formatting) 30 | run: pnpm check:format 31 | - name: Run tests 32 | run: pnpm test 33 | 34 | build-workers: 35 | name: Build Workers 36 | runs-on: ubuntu-24.04 37 | permissions: 38 | contents: read 39 | timeout-minutes: 10 40 | steps: 41 | - uses: actions/checkout@v4 42 | - uses: ./.github/actions/setup 43 | 44 | - name: Build Workers 45 | run: pnpm turbo deploy -- -e staging --dry-run -------------------------------------------------------------------------------- /.github/workflows/evals.yml: -------------------------------------------------------------------------------- 1 | name: Evals 2 | on: 3 | push: 4 | 5 | env: 6 | FORCE_COLOR: 1 7 | 8 | jobs: 9 | eval: 10 | name: Eval 11 | runs-on: ubuntu-24.04 12 | permissions: 13 | contents: read 14 | timeout-minutes: 10 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: ./.github/actions/setup 18 | - name: Create .dev.vars file 19 | run: | 20 | echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}" > ./apps/sandbox-container/.dev.vars 21 | echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}" > ./apps/workers-bindings/.dev.vars 22 | echo "DEV_CLOUDFLARE_API_TOKEN=${{ secrets.DEV_CLOUDFLARE_API_TOKEN }}" >> ./apps/sandbox-container/.dev.vars 23 | echo "DEV_CLOUDFLARE_API_TOKEN=${{ secrets.DEV_CLOUDFLARE_API_TOKEN }}" >> ./apps/workers-bindings/.dev.vars 24 | echo "AI_GATEWAY_TOKEN=${{ secrets.AI_GATEWAY_TOKEN }}" >> ./apps/sandbox-container/.dev.vars 25 | echo "AI_GATEWAY_TOKEN=${{ secrets.AI_GATEWAY_TOKEN }}" >> ./apps/workers-bindings/.dev.vars 26 | echo "CLOUDFLARE_ACCOUNT_ID=${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" >> ./apps/sandbox-container/.dev.vars 27 | echo "CLOUDFLARE_ACCOUNT_ID=${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" >> ./apps/workers-bindings/.dev.vars 28 | echo "AI_GATEWAY_ID=${{ secrets.AI_GATEWAY_ID }}" >> ./apps/sandbox-container/.dev.vars 29 | echo "AI_GATEWAY_ID=${{ secrets.AI_GATEWAY_ID }}" >> ./apps/workers-bindings/.dev.vars 30 | - name: Verify .dev.vars file 31 | run: | 32 | du -h ./apps/sandbox-container/.dev.vars 33 | du -h ./apps/workers-bindings/.dev.vars 34 | - name: Run evals 35 | run: pnpm eval:ci 36 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Main 2 | 3 | on: 4 | push: 5 | branches: ['main'] 6 | 7 | env: 8 | FORCE_COLOR: 1 9 | 10 | jobs: 11 | deploy-staging: 12 | name: Deploy (staging) 13 | runs-on: ubuntu-24.04 14 | permissions: 15 | contents: read 16 | timeout-minutes: 10 17 | concurrency: ${{ github.workflow }}-deploy-staging 18 | steps: 19 | - name: Checkout Repo 20 | uses: actions/checkout@v4 21 | - uses: ./.github/actions/setup 22 | 23 | # Run tests & checks before deploying 24 | - name: Syncpack lint 25 | run: pnpm check:deps 26 | - name: Run linter 27 | run: pnpm check:turbo 28 | - name: Run linter (formatting) 29 | run: pnpm check:format 30 | - name: Run tests 31 | run: pnpm test 32 | 33 | - name: Deploy Workers (staging) 34 | run: pnpm turbo deploy -- -e staging 35 | env: 36 | CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} 37 | CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | # Wrangler 3 | .wrangler 4 | .dev.vars 5 | 6 | # Astro generated types 7 | .astro/ 8 | 9 | # Dependencies 10 | node_modules 11 | .pnp 12 | .pnp.js 13 | 14 | .eslintcache 15 | 16 | # Local env files 17 | .env 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | .secret 23 | *.env 24 | 25 | # Testing 26 | coverage 27 | 28 | # Turbo 29 | .turbo 30 | 31 | # Vercel 32 | .vercel 33 | 34 | # Build Outputs 35 | .next/ 36 | out/ 37 | dist 38 | dist2 39 | 40 | # Debug 41 | npm-debug.log* 42 | yarn-debug.log* 43 | yarn-error.log* 44 | 45 | # Misc 46 | .DS_Store 47 | *.pem 48 | .sentryclirc.lock/ 49 | tmp.json 50 | tmp.ts 51 | .idea 52 | 53 | apps/sandbox-container/workdir 54 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | auto-install-peers=true 2 | public-hoist-pattern[]=*eslint* 3 | public-hoist-pattern[]=*prettier* 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .changeset 2 | .github/ 3 | pnpm-lock.yaml 4 | vitest.config.ts.timestamp* 5 | vite.config.ts.timestamp* 6 | worker-configuration.d.ts 7 | **/dist/** 8 | packages/tools/src/test/fixtures/changesets/invalid-json/*.json 9 | -------------------------------------------------------------------------------- /.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | const codeImports = [ 4 | // Groups 5 | '', 6 | '', 7 | '', 8 | '^(@repo)(/.*)$', // Workspace imports 9 | '', 10 | // Local (relative) imports 11 | '^[.]{2}$', // .. 12 | '^[.]{2}/', // ../ 13 | '^[.]/(?!index)', // ./foo (but not ./index) 14 | '^[.]$', // . 15 | '^[.]/index$', // ./index 16 | '', 17 | ] 18 | 19 | // Type imports are ordered the same way, but without separators. 20 | // We also need a catch-all here to prevent prettier from failing. 21 | const typeImports = [''].concat( 22 | codeImports.filter((i) => i !== '').map((i) => `${i}`) 23 | ) 24 | 25 | /** @type {import("prettier").Config} */ 26 | const config = { 27 | trailingComma: 'es5', 28 | tabWidth: 2, 29 | useTabs: true, 30 | semi: false, 31 | singleQuote: true, 32 | printWidth: 100, 33 | plugins: ['@ianvs/prettier-plugin-sort-imports'], 34 | importOrder: [...codeImports, ...typeImports], 35 | importOrderTypeScriptVersion: '5.5.4', 36 | overrides: [ 37 | { 38 | files: '*.mdx', 39 | options: { 40 | parser: 'mdx', 41 | }, 42 | }, 43 | { 44 | files: ['*.jsonc', '*.code-workspace'], 45 | options: { 46 | trailingComma: 'none', 47 | }, 48 | }, 49 | ], 50 | } 51 | 52 | module.exports = config 53 | -------------------------------------------------------------------------------- /.syncpackrc.cjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | /** @type {import("syncpack").RcFile} */ 3 | const config = { 4 | indent: '\t', 5 | lintFormatting: false, // handled by prettier 6 | versionGroups: [ 7 | { 8 | label: 'local packages', 9 | packages: ['**'], 10 | dependencies: ['@repo/*'], 11 | dependencyTypes: ['!local'], // Exclude the local package itself 12 | pinVersion: 'workspace:*', 13 | }, 14 | { 15 | label: 'Sentry types that are compatible with toucan-js', 16 | dependencies: ['@sentry/types', '@sentry/tracing'], 17 | pinVersion: '8.9.2', 18 | }, 19 | { 20 | label: 'toucan-js that is compatible with pinned sentry types', 21 | dependencies: ['toucan-js'], 22 | pinVersion: '4.1.1', 23 | }, 24 | { 25 | label: 'pin vitest compatible with @cloudflare/vitest-pool-workers', 26 | dependencies: ['vitest', '@vitest/ui'], 27 | pinVersion: '3.0.9', 28 | }, 29 | { 30 | label: 'pin typescript for eslint', 31 | dependencies: ['typescript'], 32 | pinVersion: '5.5.4', 33 | }, 34 | { 35 | label: `pin eslint and all it's plugins for eslint v8`, 36 | dependencies: [ 37 | 'eslint', 38 | '@types/eslint', 39 | 'eslint-config-prettier', 40 | 'eslint-plugin-react-hooks', 41 | 'eslint-plugin-unused-imports', 42 | '@typescript-eslint/eslint-plugin', 43 | '@typescript-eslint/parser', 44 | ], 45 | // snapTo removes it from syncpack update list, which is the main goal 46 | snapTo: ['@repo/eslint-config'], 47 | }, 48 | { 49 | label: 'use zod v4 in packages/tools', 50 | dependencies: ['zod'], 51 | pinVersion: '4.0.0-beta.20250505T195954', 52 | packages: ['@repo/tools'], 53 | }, 54 | ], 55 | semverGroups: [ 56 | { 57 | label: 'pin all deps', 58 | range: '', 59 | dependencies: ['**'], 60 | packages: ['**'], 61 | }, 62 | ], 63 | } 64 | 65 | module.exports = config 66 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"], 7 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 8 | "unwantedRecommendations": [] 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Wrangler", 5 | "type": "node", 6 | "request": "attach", 7 | "port": 9229, 8 | "cwd": "/", 9 | "resolveSourceMapLocations": null, 10 | "attachExistingChildren": false, 11 | "autoAttachChildProcesses": false, 12 | "sourceMaps": true // works with or without this line 13 | }, 14 | { 15 | "type": "node", 16 | "request": "launch", 17 | "name": "Open inspector with Vitest", 18 | "runtimeExecutable": "npm", 19 | "runtimeArgs": ["run", "eval:dev"], 20 | "console": "integratedTerminal", 21 | "cwd": "${workspaceFolder}/apps/workers-bindings" 22 | }, 23 | { 24 | "name": "Attach to Workers Runtime", 25 | "type": "node", 26 | "request": "attach", 27 | "port": 9229, 28 | "cwd": "/", 29 | "resolveSourceMapLocations": null, 30 | "attachExistingChildren": false, 31 | "autoAttachChildProcesses": false 32 | } 33 | ], 34 | "compounds": [ 35 | { 36 | "name": "Debug Workers tests", 37 | "configurations": ["Open inspector with Vitest", "Attach to Workers Runtime"], 38 | "stopAll": true 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.detectIndentation": true, 3 | // https://arktype.io/docs/intro/setup#settings 4 | // allow autocomplete for ArkType expressions like "string | num" 5 | "editor.quickSuggestions": { 6 | "strings": "on" 7 | }, 8 | // prioritize ArkType's "type" for autoimports 9 | "typescript.preferences.autoImportSpecifierExcludeRegexes": ["^(node:)?os$"], 10 | "typescript.preferences.autoImportFileExcludePatterns": ["**/vitest/dist/**"], 11 | "typescript.preferences.importModuleSpecifier": "non-relative", 12 | "files.associations": { 13 | "**/packages/tools/bin/*": "shellscript", 14 | "**/*.css": "tailwindcss", 15 | "turbo.json": "jsonc", 16 | "**/packages/typescript-config/*.json": "jsonc" 17 | }, 18 | "eslint.workingDirectories": [ 19 | { 20 | "mode": "auto" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "changeset:new", 7 | "group": "none", 8 | "label": "changeset: new", 9 | "detail": "Create changeset and commit all changes", 10 | "runOptions": { "instanceLimit": 1 }, 11 | "presentation": { 12 | "reveal": "always", 13 | "focus": true, 14 | "panel": "new", 15 | "close": true 16 | } 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Developing 2 | 3 | We welcome contributions to all of our MCP servers! Here's a quick run down on how to get started. 4 | 5 | ## Architecture 6 | 7 | This monorepo has two top-level directories: `/apps` and `/packages`. 8 | 9 | - **/apps**: Containing directories for each server. Within each server, you'll find a `CONTRIBUTING.md` with any special instructions on how to get set up: 10 | - [apps/workers-observability](apps/workers-observability) 11 | - [apps/workers-bindings](apps/workers-bindings) 12 | - [apps/radar](apps/radar) 13 | - [apps/cloudflare-one-casb](apps/cloudflare-one-casb) 14 | - **/packages**: Containing shared packages used across our various apps. 15 | - packages/eslint-config: Eslint config used by all apps and packages. 16 | - packages/typescript-config: tsconfig used by all apps and packages. 17 | - packages/mcp-common: Shared common tools and scripts to help manage this repo. 18 | 19 | We use [TurboRepo](https://turbo.build/) and [pnpm](https://pnpm.io/) to manage this repository. TurboRepo manages the monorepo by ensuring commands are run across all apps. 20 | 21 | ## Getting Started 22 | 23 | This section will guide you through setting up your developer environment and running tests. 24 | 25 | ### Installation 26 | 27 | Install dependencies: 28 | 29 | ```bash 30 | pnpm install 31 | ``` 32 | 33 | ### Testing 34 | 35 | The project uses Vitest as the testing framework with [fetchMock](https://developers.cloudflare.com/workers/testing/vitest-integration/test-apis/) for API mocking. 36 | 37 | #### Running Tests 38 | 39 | To run all tests: 40 | 41 | ```bash 42 | pnpm test 43 | ``` 44 | 45 | To run a specific test file: 46 | 47 | ```bash 48 | pnpm test -- tests/tools/queues.test.ts 49 | ``` 50 | 51 | To run tests in watch mode (useful during development): 52 | 53 | ```bash 54 | pnpm test:watch 55 | ``` 56 | -------------------------------------------------------------------------------- /apps/ai-gateway/.dev.vars.example: -------------------------------------------------------------------------------- 1 | CLOUDFLARE_CLIENT_ID= 2 | CLOUDFLARE_CLIENT_SECRET= 3 | DEV_DISABLE_OAUTH= 4 | DEV_CLOUDFLARE_API_TOKEN= 5 | DEV_CLOUDFLARE_EMAIL= 6 | -------------------------------------------------------------------------------- /apps/ai-gateway/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ['@repo/eslint-config/default.cjs'], 5 | } 6 | -------------------------------------------------------------------------------- /apps/ai-gateway/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # cloudflare-ai-gateway-mcp-server 2 | 3 | ## 0.0.4 4 | 5 | ### Patch Changes 6 | 7 | - 3677a18: Remove extraneous log 8 | - Updated dependencies [3677a18] 9 | - @repo/mcp-common@0.16.3 10 | 11 | ## 0.0.3 12 | 13 | ### Patch Changes 14 | 15 | - 86c2e4f: Add API token passthrough auth 16 | - Updated dependencies [86c2e4f] 17 | - @repo/mcp-common@0.16.2 18 | 19 | ## 0.0.2 20 | 21 | ### Patch Changes 22 | 23 | - cf3771b: chore: add suffixes to common files in apps and packages 24 | 25 | It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. 26 | 27 | - Updated dependencies [cf3771b] 28 | - @repo/mcp-common@0.16.1 29 | - @repo/mcp-observability@0.31.1 30 | -------------------------------------------------------------------------------- /apps/ai-gateway/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | If you'd like to iterate and test your MCP server, you can do so in local development. 4 | 5 | ## Local Development 6 | 7 | 1. Create a `.dev.vars` file in your project root: 8 | 9 | If you're a Cloudflare employee: 10 | 11 | ``` 12 | CLOUDFLARE_CLIENT_ID=your_development_cloudflare_client_id 13 | CLOUDFLARE_CLIENT_SECRET=your_development_cloudflare_client_secret 14 | ``` 15 | 16 | If you're an external contributor, you can provide a development API token: 17 | 18 | ``` 19 | DEV_DISABLE_OAUTH=true 20 | # This is your global api token 21 | DEV_CLOUDFLARE_API_TOKEN=your_development_api_token 22 | ``` 23 | 24 | 2. Start the local development server: 25 | 26 | ```bash 27 | npx wrangler dev 28 | ``` 29 | 30 | 3. To test locally, open Inspector, and connect to `http://localhost:8976/sse`. 31 | Once you follow the prompts, you'll be able to "List Tools". You can also connect with any MCP client. 32 | 33 | ## Deploying the Worker ( Cloudflare employees only ) 34 | 35 | Set secrets via Wrangler: 36 | 37 | ```bash 38 | npx wrangler secret put CLOUDFLARE_CLIENT_ID -e 39 | npx wrangler secret put CLOUDFLARE_CLIENT_SECRET -e 40 | ``` 41 | 42 | ## Set up a KV namespace 43 | 44 | Create the KV namespace: 45 | 46 | ```bash 47 | npx wrangler kv namespace create "OAUTH_KV" 48 | ``` 49 | 50 | Then, update the Wrangler file with the generated KV namespace ID. 51 | 52 | ## Deploy & Test 53 | 54 | Deploy the MCP server to make it available on your workers.dev domain: 55 | 56 | ```bash 57 | npx wrangler deploy -e 58 | ``` 59 | 60 | Test the remote server using [Inspector](https://modelcontextprotocol.io/docs/tools/inspector): 61 | 62 | ```bash 63 | npx @modelcontextprotocol/inspector@latest 64 | ``` 65 | -------------------------------------------------------------------------------- /apps/ai-gateway/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloudflare-ai-gateway-mcp-server", 3 | "version": "0.0.4", 4 | "private": true, 5 | "scripts": { 6 | "check:lint": "run-eslint-workers", 7 | "check:types": "run-tsc", 8 | "deploy": "run-wrangler-deploy", 9 | "dev": "wrangler dev", 10 | "start": "wrangler dev", 11 | "types": "wrangler types --include-env=false", 12 | "test": "vitest run" 13 | }, 14 | "dependencies": { 15 | "@cloudflare/workers-oauth-provider": "0.0.5", 16 | "@hono/zod-validator": "0.4.3", 17 | "@modelcontextprotocol/sdk": "1.10.2", 18 | "@repo/mcp-common": "workspace:*", 19 | "@repo/mcp-observability": "workspace:*", 20 | "agents": "0.0.67", 21 | "cloudflare": "4.2.0", 22 | "hono": "4.7.6", 23 | "zod": "3.24.2" 24 | }, 25 | "devDependencies": { 26 | "@cloudflare/vitest-pool-workers": "0.8.14", 27 | "@types/node": "22.14.1", 28 | "prettier": "3.5.3", 29 | "typescript": "5.5.4", 30 | "vitest": "3.0.9", 31 | "wrangler": "4.10.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /apps/ai-gateway/src/ai-gateway.context.ts: -------------------------------------------------------------------------------- 1 | import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' 2 | import type { AIGatewayMCP } from './ai-gateway.app' 3 | 4 | export interface Env { 5 | OAUTH_KV: KVNamespace 6 | ENVIRONMENT: 'development' | 'staging' | 'production' 7 | MCP_SERVER_NAME: string 8 | MCP_SERVER_VERSION: string 9 | CLOUDFLARE_CLIENT_ID: string 10 | CLOUDFLARE_CLIENT_SECRET: string 11 | MCP_OBJECT: DurableObjectNamespace 12 | USER_DETAILS: DurableObjectNamespace 13 | MCP_METRICS: AnalyticsEngineDataset 14 | DEV_DISABLE_OAUTH: string 15 | DEV_CLOUDFLARE_API_TOKEN: string 16 | DEV_CLOUDFLARE_EMAIL: string 17 | } 18 | -------------------------------------------------------------------------------- /apps/ai-gateway/src/types.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const GatewayIdParam = z.string().describe('The gateway ID.') 4 | export const LogIdParam = z.string() 5 | export const pageParam = z.number().int().min(1).optional().default(1) 6 | export const perPageParam = z.number().int().min(1).max(50).optional().default(20) 7 | 8 | export const ListLogsParams = { 9 | gateway_id: GatewayIdParam, 10 | page: pageParam, 11 | per_page: perPageParam, 12 | order_by: z 13 | .enum([ 14 | 'created_at', 15 | 'provider', 16 | 'model', 17 | 'model_type', 18 | 'success', 19 | 'cached', 20 | 'cost', 21 | 'tokens_in', 22 | 'tokens_out', 23 | 'duration', 24 | 'feedback', 25 | ]) 26 | .optional() 27 | .default('created_at'), 28 | order_by_direction: z.enum(['asc', 'desc']).optional().default('desc'), 29 | start_date: z.string().datetime().optional(), 30 | end_date: z.string().datetime().optional(), 31 | feedback: z.union([z.literal(-1), z.literal(0), z.literal(1)]).optional(), 32 | success: z.boolean().optional(), 33 | cached: z.boolean().optional(), 34 | model: z.string().toLowerCase().optional(), 35 | provider: z.string().toLowerCase().optional(), 36 | } 37 | -------------------------------------------------------------------------------- /apps/ai-gateway/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/workers.json", 3 | "include": ["*/**.ts", "./vitest.config.ts", "./types.d.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/ai-gateway/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { TestEnv } from './vitest.config' 2 | 3 | declare module 'cloudflare:test' { 4 | interface ProvidedEnv extends TestEnv {} 5 | } 6 | -------------------------------------------------------------------------------- /apps/ai-gateway/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' 2 | 3 | import type { Env } from './src/ai-gateway.context' 4 | 5 | export interface TestEnv extends Env { 6 | CLOUDFLARE_MOCK_ACCOUNT_ID: string 7 | CLOUDFLARE_MOCK_API_TOKEN: string 8 | } 9 | 10 | export default defineWorkersConfig({ 11 | test: { 12 | poolOptions: { 13 | workers: { 14 | wrangler: { configPath: `${__dirname}/wrangler.jsonc` }, 15 | miniflare: { 16 | bindings: { 17 | CLOUDFLARE_MOCK_ACCOUNT_ID: 'mock-account-id', 18 | CLOUDFLARE_MOCK_API_TOKEN: 'mock-api-token', 19 | } satisfies Partial, 20 | }, 21 | }, 22 | }, 23 | }, 24 | }) 25 | -------------------------------------------------------------------------------- /apps/auditlogs/.dev.vars.example: -------------------------------------------------------------------------------- 1 | CLOUDFLARE_CLIENT_ID= 2 | CLOUDFLARE_CLIENT_SECRET= 3 | DEV_DISABLE_OAUTH= 4 | DEV_CLOUDFLARE_API_TOKEN= 5 | DEV_CLOUDFLARE_EMAIL= -------------------------------------------------------------------------------- /apps/auditlogs/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ['@repo/eslint-config/default.cjs'], 5 | } 6 | -------------------------------------------------------------------------------- /apps/auditlogs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # auditlogs 2 | 3 | ## 0.0.4 4 | 5 | ### Patch Changes 6 | 7 | - 3677a18: Remove extraneous log 8 | - Updated dependencies [3677a18] 9 | - @repo/mcp-common@0.16.3 10 | 11 | ## 0.0.3 12 | 13 | ### Patch Changes 14 | 15 | - 86c2e4f: Add API token passthrough auth 16 | - Updated dependencies [86c2e4f] 17 | - @repo/mcp-common@0.16.2 18 | 19 | ## 0.0.2 20 | 21 | ### Patch Changes 22 | 23 | - cf3771b: chore: add suffixes to common files in apps and packages 24 | 25 | It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. 26 | 27 | - Updated dependencies [cf3771b] 28 | - @repo/mcp-common@0.16.1 29 | - @repo/mcp-observability@0.31.1 30 | -------------------------------------------------------------------------------- /apps/auditlogs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auditlogs", 3 | "version": "0.0.4", 4 | "private": true, 5 | "scripts": { 6 | "check:lint": "run-eslint-workers", 7 | "check:types": "run-tsc", 8 | "deploy": "wrangler deploy", 9 | "dev": "wrangler dev", 10 | "start": "wrangler dev", 11 | "types": "wrangler types --include-env=false", 12 | "test": "vitest run" 13 | }, 14 | "dependencies": { 15 | "@cloudflare/workers-oauth-provider": "0.0.5", 16 | "@hono/zod-validator": "0.4.3", 17 | "@modelcontextprotocol/sdk": "1.10.2", 18 | "@repo/mcp-common": "workspace:*", 19 | "@repo/mcp-observability": "workspace:*", 20 | "agents": "0.0.67", 21 | "cloudflare": "4.2.0", 22 | "hono": "4.7.6", 23 | "zod": "3.24.2" 24 | }, 25 | "devDependencies": { 26 | "@cloudflare/vitest-pool-workers": "0.8.14", 27 | "@types/node": "22.14.1", 28 | "prettier": "3.5.3", 29 | "typescript": "5.5.4", 30 | "vitest": "3.0.9", 31 | "wrangler": "4.10.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /apps/auditlogs/src/auditlogs.context.ts: -------------------------------------------------------------------------------- 1 | import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' 2 | import type { AuditlogMCP } from './auditlogs.app' 3 | 4 | export interface Env { 5 | OAUTH_KV: KVNamespace 6 | ENVIRONMENT: 'development' | 'staging' | 'production' 7 | MCP_SERVER_NAME: string 8 | MCP_SERVER_VERSION: string 9 | CLOUDFLARE_ACCESS_TOKEN: string 10 | CLOUDFLARE_CLIENT_ID: string 11 | CLOUDFLARE_CLIENT_SECRET: string 12 | MCP_OBJECT: DurableObjectNamespace 13 | USER_DETAILS: DurableObjectNamespace 14 | MCP_METRICS: AnalyticsEngineDataset 15 | DEV_DISABLE_OAUTH: string 16 | DEV_CLOUDFLARE_API_TOKEN: string 17 | DEV_CLOUDFLARE_EMAIL: string 18 | } 19 | -------------------------------------------------------------------------------- /apps/auditlogs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/workers.json", 3 | "include": ["*/**.ts", "worker-configuration.d.ts", "./vitest.config.ts", "./types.d.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/auditlogs/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { TestEnv } from './vitest.config' 2 | 3 | declare module 'cloudflare:test' { 4 | interface ProvidedEnv extends TestEnv {} 5 | } 6 | -------------------------------------------------------------------------------- /apps/auditlogs/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' 2 | 3 | import type { Env } from './src/auditlogs.context' 4 | 5 | export interface TestEnv extends Env { 6 | CLOUDFLARE_MOCK_ACCOUNT_ID: string 7 | CLOUDFLARE_MOCK_API_TOKEN: string 8 | } 9 | 10 | export default defineWorkersConfig({ 11 | test: { 12 | poolOptions: { 13 | workers: { 14 | wrangler: { configPath: `${__dirname}/wrangler.jsonc` }, 15 | miniflare: { 16 | bindings: { 17 | CLOUDFLARE_MOCK_ACCOUNT_ID: 'mock-account-id', 18 | CLOUDFLARE_MOCK_API_TOKEN: 'mock-api-token', 19 | } satisfies Partial, 20 | }, 21 | }, 22 | }, 23 | }, 24 | }) 25 | -------------------------------------------------------------------------------- /apps/autorag/.dev.vars.example: -------------------------------------------------------------------------------- 1 | CLOUDFLARE_CLIENT_ID= 2 | CLOUDFLARE_CLIENT_SECRET= 3 | DEV_DISABLE_OAUTH= 4 | DEV_CLOUDFLARE_API_TOKEN= 5 | DEV_CLOUDFLARE_EMAIL= 6 | -------------------------------------------------------------------------------- /apps/autorag/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ['@repo/eslint-config/default.cjs'], 5 | } 6 | -------------------------------------------------------------------------------- /apps/autorag/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # cloudflare-autorag-mcp-server 2 | 3 | ## 0.0.4 4 | 5 | ### Patch Changes 6 | 7 | - 3677a18: Remove extraneous log 8 | - Updated dependencies [3677a18] 9 | - @repo/mcp-common@0.16.3 10 | 11 | ## 0.0.3 12 | 13 | ### Patch Changes 14 | 15 | - 86c2e4f: Add API token passthrough auth 16 | - Updated dependencies [86c2e4f] 17 | - @repo/mcp-common@0.16.2 18 | 19 | ## 0.0.2 20 | 21 | ### Patch Changes 22 | 23 | - cf3771b: chore: add suffixes to common files in apps and packages 24 | 25 | It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. 26 | 27 | - Updated dependencies [cf3771b] 28 | - @repo/mcp-common@0.16.1 29 | - @repo/mcp-observability@0.31.1 30 | -------------------------------------------------------------------------------- /apps/autorag/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | If you'd like to iterate and test your MCP server, you can do so in local development. 4 | 5 | ## Local Development 6 | 7 | 1. Create a `.dev.vars` file in your project root: 8 | 9 | If you're a Cloudflare employee: 10 | 11 | ``` 12 | CLOUDFLARE_CLIENT_ID=your_development_cloudflare_client_id 13 | CLOUDFLARE_CLIENT_SECRET=your_development_cloudflare_client_secret 14 | ``` 15 | 16 | If you're an external contributor, you can provide a development API token: 17 | 18 | ``` 19 | DEV_DISABLE_OAUTH=true 20 | # This is your global api token 21 | DEV_CLOUDFLARE_API_TOKEN=your_development_api_token 22 | ``` 23 | 24 | 2. Start the local development server: 25 | 26 | ```bash 27 | npx wrangler dev 28 | ``` 29 | 30 | 3. To test locally, open Inspector, and connect to `http://localhost:8976/sse`. 31 | Once you follow the prompts, you'll be able to "List Tools". You can also connect with any MCP client. 32 | 33 | ## Deploying the Worker ( Cloudflare employees only ) 34 | 35 | Set secrets via Wrangler: 36 | 37 | ```bash 38 | npx wrangler secret put CLOUDFLARE_CLIENT_ID -e 39 | npx wrangler secret put CLOUDFLARE_CLIENT_SECRET -e 40 | npx wrangler secret put URL_SCANNER_API_TOKEN -e 41 | ``` 42 | 43 | ## Set up a KV namespace 44 | 45 | Create the KV namespace: 46 | 47 | ```bash 48 | npx wrangler kv namespace create "OAUTH_KV" 49 | ``` 50 | 51 | Then, update the Wrangler file with the generated KV namespace ID. 52 | 53 | ## Deploy & Test 54 | 55 | Deploy the MCP server to make it available on your workers.dev domain: 56 | 57 | ```bash 58 | npx wrangler deploy -e 59 | ``` 60 | 61 | Test the remote server using [Inspector](https://modelcontextprotocol.io/docs/tools/inspector): 62 | 63 | ```bash 64 | npx @modelcontextprotocol/inspector@latest 65 | ``` 66 | -------------------------------------------------------------------------------- /apps/autorag/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloudflare-autorag-mcp-server", 3 | "version": "0.0.4", 4 | "private": true, 5 | "scripts": { 6 | "check:lint": "run-eslint-workers", 7 | "check:types": "run-tsc", 8 | "deploy": "run-wrangler-deploy", 9 | "dev": "wrangler dev", 10 | "start": "wrangler dev", 11 | "types": "wrangler types --include-env=false", 12 | "test": "vitest run" 13 | }, 14 | "dependencies": { 15 | "@cloudflare/workers-oauth-provider": "0.0.5", 16 | "@hono/zod-validator": "0.4.3", 17 | "@modelcontextprotocol/sdk": "1.10.2", 18 | "@repo/mcp-common": "workspace:*", 19 | "@repo/mcp-observability": "workspace:*", 20 | "agents": "0.0.67", 21 | "cloudflare": "4.2.0", 22 | "hono": "4.7.6", 23 | "zod": "3.24.2" 24 | }, 25 | "devDependencies": { 26 | "@cloudflare/vitest-pool-workers": "0.8.14", 27 | "@types/node": "22.14.1", 28 | "prettier": "3.5.3", 29 | "typescript": "5.5.4", 30 | "vitest": "3.0.9", 31 | "wrangler": "4.10.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /apps/autorag/src/autorag.context.ts: -------------------------------------------------------------------------------- 1 | import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' 2 | import type { AutoRAGMCP } from './autorag.app' 3 | 4 | export interface Env { 5 | OAUTH_KV: KVNamespace 6 | ENVIRONMENT: 'development' | 'staging' | 'production' 7 | MCP_SERVER_NAME: string 8 | MCP_SERVER_VERSION: string 9 | CLOUDFLARE_CLIENT_ID: string 10 | CLOUDFLARE_CLIENT_SECRET: string 11 | MCP_OBJECT: DurableObjectNamespace 12 | USER_DETAILS: DurableObjectNamespace 13 | MCP_METRICS: AnalyticsEngineDataset 14 | DEV_DISABLE_OAUTH: string 15 | DEV_CLOUDFLARE_API_TOKEN: string 16 | DEV_CLOUDFLARE_EMAIL: string 17 | } 18 | -------------------------------------------------------------------------------- /apps/autorag/src/types.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const pageParam = z.number().int().min(1).optional().default(1) 4 | export const perPageParam = z.number().int().min(1).max(50).optional().default(20) 5 | -------------------------------------------------------------------------------- /apps/autorag/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/workers.json", 3 | "include": ["*/**.ts", "./vitest.config.ts", "./types.d.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/autorag/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { TestEnv } from './vitest.config' 2 | 3 | declare module 'cloudflare:test' { 4 | interface ProvidedEnv extends TestEnv {} 5 | } 6 | -------------------------------------------------------------------------------- /apps/autorag/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' 2 | 3 | import type { Env } from './src/autorag.context' 4 | 5 | export interface TestEnv extends Env { 6 | CLOUDFLARE_MOCK_ACCOUNT_ID: string 7 | CLOUDFLARE_MOCK_API_TOKEN: string 8 | } 9 | 10 | export default defineWorkersConfig({ 11 | test: { 12 | poolOptions: { 13 | workers: { 14 | wrangler: { configPath: `${__dirname}/wrangler.jsonc` }, 15 | miniflare: { 16 | bindings: { 17 | CLOUDFLARE_MOCK_ACCOUNT_ID: 'mock-account-id', 18 | CLOUDFLARE_MOCK_API_TOKEN: 'mock-api-token', 19 | } satisfies Partial, 20 | }, 21 | }, 22 | }, 23 | }, 24 | }) 25 | -------------------------------------------------------------------------------- /apps/browser-rendering/.dev.vars.example: -------------------------------------------------------------------------------- 1 | CLOUDFLARE_CLIENT_ID= 2 | CLOUDFLARE_CLIENT_SECRET= 3 | DEV_DISABLE_OAUTH= 4 | DEV_CLOUDFLARE_API_TOKEN= 5 | DEV_CLOUDFLARE_EMAIL= 6 | -------------------------------------------------------------------------------- /apps/browser-rendering/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ['@repo/eslint-config/default.cjs'], 5 | } 6 | -------------------------------------------------------------------------------- /apps/browser-rendering/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # cloudflare-browser-mcp-server 2 | 3 | ## 0.0.4 4 | 5 | ### Patch Changes 6 | 7 | - 3677a18: Remove extraneous log 8 | - Updated dependencies [3677a18] 9 | - @repo/mcp-common@0.16.3 10 | 11 | ## 0.0.3 12 | 13 | ### Patch Changes 14 | 15 | - 86c2e4f: Add API token passthrough auth 16 | - Updated dependencies [86c2e4f] 17 | - @repo/mcp-common@0.16.2 18 | 19 | ## 0.0.2 20 | 21 | ### Patch Changes 22 | 23 | - cf3771b: chore: add suffixes to common files in apps and packages 24 | 25 | It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. 26 | 27 | - Updated dependencies [cf3771b] 28 | - @repo/mcp-common@0.16.1 29 | - @repo/mcp-observability@0.31.1 30 | -------------------------------------------------------------------------------- /apps/browser-rendering/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | If you'd like to iterate and test your MCP server, you can do so in local development. 4 | 5 | ## Local Development 6 | 7 | 1. Create a `.dev.vars` file in your project root: 8 | 9 | If you're a Cloudflare employee: 10 | 11 | ``` 12 | CLOUDFLARE_CLIENT_ID=your_development_cloudflare_client_id 13 | CLOUDFLARE_CLIENT_SECRET=your_development_cloudflare_client_secret 14 | ``` 15 | 16 | If you're an external contributor, you can provide a development API token: 17 | 18 | ``` 19 | DEV_DISABLE_OAUTH=true 20 | # This is your global api token 21 | DEV_CLOUDFLARE_API_TOKEN=your_development_api_token 22 | ``` 23 | 24 | 2. Start the local development server: 25 | 26 | ```bash 27 | npx wrangler dev 28 | ``` 29 | 30 | 3. To test locally, open Inspector, and connect to `http://localhost:8976/sse`. 31 | Once you follow the prompts, you'll be able to "List Tools". You can also connect with any MCP client. 32 | 33 | ## Deploying the Worker ( Cloudflare employees only ) 34 | 35 | Set secrets via Wrangler: 36 | 37 | ```bash 38 | npx wrangler secret put CLOUDFLARE_CLIENT_ID -e 39 | npx wrangler secret put CLOUDFLARE_CLIENT_SECRET -e 40 | ``` 41 | 42 | ## Set up a KV namespace 43 | 44 | Create the KV namespace: 45 | 46 | ```bash 47 | npx wrangler kv namespace create "OAUTH_KV" 48 | ``` 49 | 50 | Then, update the Wrangler file with the generated KV namespace ID. 51 | 52 | ## Deploy & Test 53 | 54 | Deploy the MCP server to make it available on your workers.dev domain: 55 | 56 | ```bash 57 | npx wrangler deploy -e 58 | ``` 59 | 60 | Test the remote server using [Inspector](https://modelcontextprotocol.io/docs/tools/inspector): 61 | 62 | ```bash 63 | npx @modelcontextprotocol/inspector@latest 64 | ``` 65 | -------------------------------------------------------------------------------- /apps/browser-rendering/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloudflare-browser-mcp-server", 3 | "version": "0.0.4", 4 | "private": true, 5 | "scripts": { 6 | "check:lint": "run-eslint-workers", 7 | "check:types": "run-tsc", 8 | "deploy": "run-wrangler-deploy", 9 | "dev": "wrangler dev", 10 | "start": "wrangler dev", 11 | "types": "wrangler types --include-env=false", 12 | "test": "vitest run" 13 | }, 14 | "dependencies": { 15 | "@cloudflare/workers-oauth-provider": "0.0.5", 16 | "@hono/zod-validator": "0.4.3", 17 | "@modelcontextprotocol/sdk": "1.10.2", 18 | "@repo/mcp-common": "workspace:*", 19 | "@repo/mcp-observability": "workspace:*", 20 | "agents": "0.0.67", 21 | "cloudflare": "4.2.0", 22 | "hono": "4.7.6", 23 | "zod": "3.24.2" 24 | }, 25 | "devDependencies": { 26 | "@cloudflare/vitest-pool-workers": "0.8.14", 27 | "@types/node": "22.14.1", 28 | "prettier": "3.5.3", 29 | "typescript": "5.5.4", 30 | "vitest": "3.0.9", 31 | "wrangler": "4.10.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /apps/browser-rendering/src/browser.context.ts: -------------------------------------------------------------------------------- 1 | import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' 2 | import type { BrowserMCP } from './browser.app' 3 | 4 | export interface Env { 5 | OAUTH_KV: KVNamespace 6 | ENVIRONMENT: 'development' | 'staging' | 'production' 7 | MCP_SERVER_NAME: string 8 | MCP_SERVER_VERSION: string 9 | CLOUDFLARE_CLIENT_ID: string 10 | CLOUDFLARE_CLIENT_SECRET: string 11 | MCP_OBJECT: DurableObjectNamespace 12 | USER_DETAILS: DurableObjectNamespace 13 | MCP_METRICS: AnalyticsEngineDataset 14 | DEV_DISABLE_OAUTH: string 15 | DEV_CLOUDFLARE_API_TOKEN: string 16 | DEV_CLOUDFLARE_EMAIL: string 17 | } 18 | -------------------------------------------------------------------------------- /apps/browser-rendering/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/workers.json", 3 | "include": ["*/**.ts", "./vitest.config.ts", "./types.d.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/browser-rendering/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { TestEnv } from './vitest.config' 2 | 3 | declare module 'cloudflare:test' { 4 | interface ProvidedEnv extends TestEnv {} 5 | } 6 | -------------------------------------------------------------------------------- /apps/browser-rendering/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' 2 | 3 | import type { Env } from './src/browser.context' 4 | 5 | export interface TestEnv extends Env { 6 | CLOUDFLARE_MOCK_ACCOUNT_ID: string 7 | CLOUDFLARE_MOCK_API_TOKEN: string 8 | } 9 | 10 | export default defineWorkersConfig({ 11 | test: { 12 | poolOptions: { 13 | workers: { 14 | wrangler: { configPath: `${__dirname}/wrangler.jsonc` }, 15 | miniflare: { 16 | bindings: { 17 | CLOUDFLARE_MOCK_ACCOUNT_ID: 'mock-account-id', 18 | CLOUDFLARE_MOCK_API_TOKEN: 'mock-api-token', 19 | } satisfies Partial, 20 | }, 21 | }, 22 | }, 23 | }, 24 | }) 25 | -------------------------------------------------------------------------------- /apps/cloudflare-one-casb/.dev.vars.example: -------------------------------------------------------------------------------- 1 | CLOUDFLARE_CLIENT_ID= 2 | CLOUDFLARE_CLIENT_SECRET= 3 | DEV_DISABLE_OAUTH= 4 | DEV_CLOUDFLARE_API_TOKEN= 5 | DEV_CLOUDFLARE_EMAIL= -------------------------------------------------------------------------------- /apps/cloudflare-one-casb/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ['@repo/eslint-config/default.cjs'], 5 | } 6 | -------------------------------------------------------------------------------- /apps/cloudflare-one-casb/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # cloudflare-casb-mcp-server 2 | 3 | ## 0.0.4 4 | 5 | ### Patch Changes 6 | 7 | - 3677a18: Remove extraneous log 8 | - Updated dependencies [3677a18] 9 | - @repo/mcp-common@0.16.3 10 | 11 | ## 0.0.3 12 | 13 | ### Patch Changes 14 | 15 | - 86c2e4f: Add API token passthrough auth 16 | - Updated dependencies [86c2e4f] 17 | - @repo/mcp-common@0.16.2 18 | 19 | ## 0.0.2 20 | 21 | ### Patch Changes 22 | 23 | - cf3771b: chore: add suffixes to common files in apps and packages 24 | 25 | It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. 26 | 27 | - Updated dependencies [cf3771b] 28 | - @repo/mcp-common@0.16.1 29 | -------------------------------------------------------------------------------- /apps/cloudflare-one-casb/README.md: -------------------------------------------------------------------------------- 1 | # Model Context Protocol (MCP) Server + Cloudflare OAuth 2 | 3 | This is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) server that supports remote MCP connections, with Cloudflare OAuth built-in. 4 | 5 | You should use this as a template to build an MCP server for Cloudflare, provided by Cloudflare at `server-name.mcp.cloudflare.com`. It has a basic set of tools `apps/template-start-here/src/tools/logpush.tools.ts` — you can modify these to do what you need 6 | 7 | ## Getting Started 8 | 9 | - Set secrets via Wrangler 10 | 11 | ```bash 12 | wrangler secret put CLOUDFLARE_CLIENT_ID 13 | wrangler secret put CLOUDFLARE_CLIENT_SECRET 14 | ``` 15 | 16 | #### Set up a KV namespace 17 | 18 | - Create the KV namespace: 19 | `wrangler kv:namespace create "OAUTH_KV"` 20 | - Update the Wrangler file with the KV ID 21 | 22 | #### Deploy & Test 23 | 24 | Deploy the MCP server to make it available on your workers.dev domain 25 | ` wrangler deploy` 26 | 27 | Test the remote server using [Inspector](https://modelcontextprotocol.io/docs/tools/inspector): 28 | 29 | ``` 30 | npx wrangler deploy 31 | ``` 32 | -------------------------------------------------------------------------------- /apps/cloudflare-one-casb/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloudflare-casb-mcp-server", 3 | "version": "0.0.4", 4 | "private": true, 5 | "scripts": { 6 | "check:lint": "run-eslint-workers", 7 | "check:types": "run-tsc", 8 | "deploy": "run-wrangler-deploy", 9 | "dev": "wrangler dev", 10 | "start": "wrangler dev", 11 | "types": "wrangler types --include-env=false", 12 | "test": "vitest run" 13 | }, 14 | "dependencies": { 15 | "@cloudflare/workers-oauth-provider": "0.0.5", 16 | "@hono/zod-validator": "0.4.3", 17 | "@modelcontextprotocol/sdk": "1.10.2", 18 | "@repo/mcp-common": "workspace:*", 19 | "agents": "0.0.67", 20 | "cloudflare": "4.2.0", 21 | "hono": "4.7.6", 22 | "zod": "3.24.2" 23 | }, 24 | "devDependencies": { 25 | "@cloudflare/vitest-pool-workers": "0.8.14", 26 | "@types/jsonwebtoken": "9.0.9", 27 | "@types/node": "22.14.1", 28 | "prettier": "3.5.3", 29 | "typescript": "5.5.4", 30 | "vitest": "3.0.9", 31 | "wrangler": "4.10.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /apps/cloudflare-one-casb/src/cf1-casb.context.ts: -------------------------------------------------------------------------------- 1 | import type { CASBMCP, UserDetails } from './cf1-casb.app' 2 | 3 | export interface Env { 4 | ENVIRONMENT: 'development' | 'staging' | 'production' 5 | MCP_SERVER_NAME: string 6 | MCP_SERVER_VERSION: string 7 | MCP_OBJECT: DurableObjectNamespace 8 | MCP_METRICS: AnalyticsEngineDataset 9 | AI: Ai 10 | CLOUDFLARE_CLIENT_ID: string 11 | CLOUDFLARE_CLIENT_SECRET: string 12 | USER_DETAILS: DurableObjectNamespace 13 | DEV_DISABLE_OAUTH: string 14 | DEV_CLOUDFLARE_API_TOKEN: string 15 | DEV_CLOUDFLARE_EMAIL: string 16 | } 17 | -------------------------------------------------------------------------------- /apps/cloudflare-one-casb/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/workers.json", 3 | "include": ["*/**.ts", "./types.d.ts", "./vitest.config.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/cloudflare-one-casb/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { TestEnv } from './vitest.config' 2 | 3 | declare module 'cloudflare:test' { 4 | interface ProvidedEnv extends TestEnv {} 5 | } 6 | -------------------------------------------------------------------------------- /apps/cloudflare-one-casb/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' 2 | 3 | import type { Env } from './src/cf1-casb.context' 4 | 5 | export interface TestEnv extends Env { 6 | CLOUDFLARE_MOCK_ACCOUNT_ID: string 7 | CLOUDFLARE_MOCK_API_TOKEN: string 8 | } 9 | 10 | export default defineWorkersConfig({ 11 | test: { 12 | poolOptions: { 13 | workers: { 14 | wrangler: { configPath: `${__dirname}/wrangler.jsonc` }, 15 | miniflare: { 16 | bindings: { 17 | CLOUDFLARE_MOCK_ACCOUNT_ID: 'mock-account-id', 18 | CLOUDFLARE_MOCK_API_TOKEN: 'mock-api-token', 19 | } satisfies Partial, 20 | }, 21 | }, 22 | }, 23 | }, 24 | }) 25 | -------------------------------------------------------------------------------- /apps/demo-day/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ['@repo/eslint-config/default.cjs'], 5 | } 6 | -------------------------------------------------------------------------------- /apps/demo-day/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # demo-day 2 | 3 | ## 0.0.4 4 | 5 | ### Patch Changes 6 | 7 | - 3677a18: Remove extraneous log 8 | - Updated dependencies [3677a18] 9 | - @repo/mcp-common@0.16.3 10 | 11 | ## 0.0.3 12 | 13 | ### Patch Changes 14 | 15 | - Updated dependencies [86c2e4f] 16 | - @repo/mcp-common@0.16.2 17 | 18 | ## 0.0.2 19 | 20 | ### Patch Changes 21 | 22 | - cf3771b: chore: add suffixes to common files in apps and packages 23 | 24 | It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. 25 | 26 | - Updated dependencies [cf3771b] 27 | - @repo/mcp-common@0.16.1 28 | - @repo/mcp-observability@0.31.1 29 | -------------------------------------------------------------------------------- /apps/demo-day/frontend/public/anthropic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /apps/demo-day/frontend/public/asana.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /apps/demo-day/frontend/public/atlassian.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /apps/demo-day/frontend/public/canva.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /apps/demo-day/frontend/public/dina.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/mcp-server-cloudflare/main/apps/demo-day/frontend/public/dina.jpg -------------------------------------------------------------------------------- /apps/demo-day/frontend/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/mcp-server-cloudflare/main/apps/demo-day/frontend/public/favicon-16x16.png -------------------------------------------------------------------------------- /apps/demo-day/frontend/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/mcp-server-cloudflare/main/apps/demo-day/frontend/public/favicon-32x32.png -------------------------------------------------------------------------------- /apps/demo-day/frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/mcp-server-cloudflare/main/apps/demo-day/frontend/public/favicon.ico -------------------------------------------------------------------------------- /apps/demo-day/frontend/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/mcp-server-cloudflare/main/apps/demo-day/frontend/public/favicon.png -------------------------------------------------------------------------------- /apps/demo-day/frontend/public/linear.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /apps/demo-day/frontend/public/matt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/mcp-server-cloudflare/main/apps/demo-day/frontend/public/matt.jpg -------------------------------------------------------------------------------- /apps/demo-day/frontend/public/mcpog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/mcp-server-cloudflare/main/apps/demo-day/frontend/public/mcpog.png -------------------------------------------------------------------------------- /apps/demo-day/frontend/public/more.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /apps/demo-day/frontend/public/paypal.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /apps/demo-day/frontend/public/pete.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/mcp-server-cloudflare/main/apps/demo-day/frontend/public/pete.jpeg -------------------------------------------------------------------------------- /apps/demo-day/frontend/public/sentry.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /apps/demo-day/frontend/public/special_guest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/mcp-server-cloudflare/main/apps/demo-day/frontend/public/special_guest.png -------------------------------------------------------------------------------- /apps/demo-day/frontend/public/square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /apps/demo-day/frontend/public/sunil.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/mcp-server-cloudflare/main/apps/demo-day/frontend/public/sunil.jpg -------------------------------------------------------------------------------- /apps/demo-day/frontend/public/webflow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /apps/demo-day/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo-day", 3 | "version": "0.0.4", 4 | "private": true, 5 | "scripts": { 6 | "check:types": "run-tsc", 7 | "deploy": "run-wrangler-deploy", 8 | "dev": "wrangler dev", 9 | "start": "wrangler dev", 10 | "types": "wrangler types --include-env=false", 11 | "test": "vitest run" 12 | }, 13 | "dependencies": { 14 | "@modelcontextprotocol/sdk": "1.10.2", 15 | "@repo/mcp-common": "workspace:*", 16 | "@repo/mcp-observability": "workspace:*", 17 | "@types/node": "22.14.1", 18 | "agents": "0.0.67", 19 | "zod": "3.24.2" 20 | }, 21 | "devDependencies": { 22 | "@cloudflare/vitest-pool-workers": "0.8.14", 23 | "@types/node": "22.14.1", 24 | "prettier": "3.5.3", 25 | "typescript": "5.5.4", 26 | "vitest": "3.0.9", 27 | "wrangler": "4.10.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /apps/demo-day/src/demo-day.app.ts: -------------------------------------------------------------------------------- 1 | import { McpAgent } from 'agents/mcp' 2 | 3 | import { getEnv } from '@repo/mcp-common/src/env' 4 | import { CloudflareMCPServer } from '@repo/mcp-common/src/server' 5 | 6 | // The demo day MCP server isn't stateful, so we don't have state/props 7 | export type Props = never 8 | 9 | export type State = never 10 | 11 | export type Env = { 12 | ENVIRONMENT: 'development' | 'staging' | 'production' 13 | AUTORAG_NAME: 'cloudflare-docs-autorag' 14 | MCP_SERVER_NAME: 'PLACEHOLDER' 15 | MCP_SERVER_VERSION: 'PLACEHOLDER' 16 | MCP_OBJECT: DurableObjectNamespace 17 | MCP_METRICS: AnalyticsEngineDataset 18 | ASSETS: Fetcher 19 | } 20 | 21 | const env = getEnv() 22 | 23 | export class CloudflareDemoDayMCP extends McpAgent { 24 | server = new CloudflareMCPServer({ 25 | wae: env.MCP_METRICS, 26 | serverInfo: { 27 | name: env.MCP_SERVER_NAME, 28 | version: env.MCP_SERVER_VERSION, 29 | }, 30 | }) 31 | 32 | constructor( 33 | public ctx: DurableObjectState, 34 | public env: Env 35 | ) { 36 | super(ctx, env) 37 | } 38 | 39 | async init() { 40 | this.server.tool( 41 | 'mcp_demo_day_info', 42 | "Get information about Cloudflare's MCP Demo Day. Use this tool if the user asks about Cloudflare's MCP demo day", 43 | async () => { 44 | const res = await this.env.ASSETS.fetch('https://assets.local/index.html') 45 | return { 46 | content: [ 47 | { 48 | type: 'resource', 49 | resource: { 50 | uri: 'https://demo-day.mcp.cloudflare.com', 51 | mimeType: 'text/html', 52 | text: await res.text(), 53 | }, 54 | }, 55 | { 56 | type: 'text', 57 | text: "Above is the contents of the demo day webpage, hosted at https://demo-day.mcp.cloudflare.com. Use it to answer the user's questions.", 58 | }, 59 | ], 60 | } 61 | } 62 | ) 63 | } 64 | } 65 | 66 | export default CloudflareDemoDayMCP.mount('/sse') 67 | -------------------------------------------------------------------------------- /apps/demo-day/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/workers.json", 3 | "include": ["src/demo-day.app.ts"], 4 | "compilerOptions": { 5 | "types": ["@types/node"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /apps/demo-day/wrangler.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/wrangler/config-schema.json", 3 | "main": "src/demo-day.app.ts", 4 | "compatibility_date": "2025-03-10", 5 | "compatibility_flags": ["nodejs_compat"], 6 | "account_id": "6702657b6aa048cf3081ff3ff3c9c52f", 7 | "routes": [{ "pattern": "demo-day.mcp.cloudflare.com", "custom_domain": true }], 8 | "name": "mcp-cloudflare-demo-day", 9 | "migrations": [ 10 | { 11 | "new_sqlite_classes": ["CloudflareDemoDayMCP"], 12 | "tag": "v1" 13 | } 14 | ], 15 | "observability": { 16 | "enabled": true 17 | }, 18 | "durable_objects": { 19 | "bindings": [ 20 | { 21 | "class_name": "CloudflareDemoDayMCP", 22 | "name": "MCP_OBJECT" 23 | } 24 | ] 25 | }, 26 | "assets": { 27 | "directory": "./frontend", 28 | "binding": "ASSETS" 29 | }, 30 | "vars": { 31 | "ENVIRONMENT": "development", 32 | "MCP_SERVER_NAME": "PLACEHOLDER", 33 | "MCP_SERVER_VERSION": "PLACEHOLDER" 34 | }, 35 | "dev": { 36 | "port": 8976 37 | }, 38 | "workers_dev": false, 39 | "preview_urls": false, 40 | "analytics_engine_datasets": [ 41 | { 42 | "binding": "MCP_METRICS", 43 | "dataset": "mcp-metrics-production" 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /apps/dex-analysis/.dev.vars.example: -------------------------------------------------------------------------------- 1 | CLOUDFLARE_CLIENT_ID= 2 | CLOUDFLARE_CLIENT_SECRET= 3 | DEV_DISABLE_OAUTH= 4 | DEV_CLOUDFLARE_API_TOKEN= 5 | DEV_CLOUDFLARE_EMAIL= -------------------------------------------------------------------------------- /apps/dex-analysis/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ['@repo/eslint-config/default.cjs'], 5 | } 6 | -------------------------------------------------------------------------------- /apps/dex-analysis/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # dex-analysis 2 | 3 | ## 0.0.4 4 | 5 | ### Patch Changes 6 | 7 | - 3677a18: Remove extraneous log 8 | - Updated dependencies [3677a18] 9 | - @repo/mcp-common@0.16.3 10 | 11 | ## 0.0.3 12 | 13 | ### Patch Changes 14 | 15 | - 86c2e4f: Add API token passthrough auth 16 | - Updated dependencies [86c2e4f] 17 | - @repo/mcp-common@0.16.2 18 | 19 | ## 0.0.2 20 | 21 | ### Patch Changes 22 | 23 | - cf3771b: chore: add suffixes to common files in apps and packages 24 | 25 | It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. 26 | 27 | - Updated dependencies [cf3771b] 28 | - @repo/mcp-common@0.16.1 29 | - @repo/mcp-observability@0.31.1 30 | -------------------------------------------------------------------------------- /apps/dex-analysis/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dex-analysis", 3 | "version": "0.0.4", 4 | "private": true, 5 | "scripts": { 6 | "check:lint": "run-eslint-workers", 7 | "check:types": "run-tsc", 8 | "deploy": "run-wrangler-deploy", 9 | "dev": "wrangler dev", 10 | "start": "wrangler dev", 11 | "types": "wrangler types --include-env=false", 12 | "cf-typegen": "wrangler types", 13 | "test": "vitest run" 14 | }, 15 | "dependencies": { 16 | "@cloudflare/workers-oauth-provider": "0.0.5", 17 | "@hono/zod-validator": "0.4.3", 18 | "@modelcontextprotocol/sdk": "1.10.2", 19 | "@repo/mcp-common": "workspace:*", 20 | "@repo/mcp-observability": "workspace:*", 21 | "agents": "0.0.67", 22 | "cloudflare": "4.2.0", 23 | "hono": "4.7.6", 24 | "zod": "3.24.2" 25 | }, 26 | "devDependencies": { 27 | "@cloudflare/vitest-pool-workers": "0.8.14", 28 | "@types/jsonwebtoken": "9.0.9", 29 | "@types/node": "22.14.1", 30 | "prettier": "3.5.3", 31 | "typescript": "5.5.4", 32 | "vitest": "3.0.9", 33 | "wrangler": "4.10.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /apps/dex-analysis/src/api/dex.ts: -------------------------------------------------------------------------------- 1 | import { fetchCloudflareApi } from '@repo/mcp-common/src/cloudflare-api' 2 | 3 | export const fetchDexTestAnalyzation = async ({ 4 | dexTestId, 5 | accountId, 6 | accessToken, 7 | timeStart, 8 | timeEnd, 9 | }: { 10 | dexTestId: string 11 | accountId: string 12 | accessToken: string 13 | timeStart: string 14 | timeEnd: string 15 | }) => { 16 | return await fetchCloudflareApi({ 17 | endpoint: `/dex/test-results/by-quartile?from=${timeStart}&to=${timeEnd}&limit=4&testId=${dexTestId}`, 18 | accountId, 19 | apiToken: accessToken, 20 | options: { 21 | method: 'GET', 22 | headers: { 23 | 'Content-Type': 'application/json', 24 | }, 25 | }, 26 | }) 27 | } 28 | 29 | export const fetchDexTests = async ({ 30 | accountId, 31 | accessToken, 32 | }: { 33 | accountId: string 34 | accessToken: string 35 | }) => { 36 | return await fetchCloudflareApi({ 37 | endpoint: '/dex/tests/overview?per_page=50', 38 | accountId, 39 | apiToken: accessToken, 40 | options: { 41 | method: 'GET', 42 | headers: { 43 | 'Content-Type': 'application/json', 44 | }, 45 | }, 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /apps/dex-analysis/src/dex-analysis.context.ts: -------------------------------------------------------------------------------- 1 | import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' 2 | import type { CloudflareDEXMCP } from './dex-analysis.app' 3 | 4 | export interface Env { 5 | OAUTH_KV: KVNamespace 6 | ENVIRONMENT: 'development' | 'staging' | 'production' 7 | MCP_SERVER_NAME: string 8 | MCP_SERVER_VERSION: string 9 | CLOUDFLARE_CLIENT_ID: string 10 | CLOUDFLARE_CLIENT_SECRET: string 11 | MCP_OBJECT: DurableObjectNamespace 12 | USER_DETAILS: DurableObjectNamespace 13 | MCP_METRICS: AnalyticsEngineDataset 14 | DEV_DISABLE_OAUTH: string 15 | DEV_CLOUDFLARE_API_TOKEN: string 16 | DEV_CLOUDFLARE_EMAIL: string 17 | } 18 | -------------------------------------------------------------------------------- /apps/dex-analysis/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/workers.json", 3 | "include": ["*/**.ts", "./vitest.config.ts", "./types.d.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/dex-analysis/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { TestEnv } from './vitest.config' 2 | 3 | declare module 'cloudflare:test' { 4 | interface ProvidedEnv extends TestEnv {} 5 | } 6 | -------------------------------------------------------------------------------- /apps/dex-analysis/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' 2 | 3 | import type { Env } from './src/dex-analysis.context' 4 | 5 | export interface TestEnv extends Env { 6 | CLOUDFLARE_MOCK_ACCOUNT_ID: string 7 | CLOUDFLARE_MOCK_API_TOKEN: string 8 | } 9 | 10 | export default defineWorkersConfig({ 11 | test: { 12 | poolOptions: { 13 | workers: { 14 | wrangler: { configPath: `${__dirname}/wrangler.jsonc` }, 15 | miniflare: { 16 | bindings: { 17 | CLOUDFLARE_MOCK_ACCOUNT_ID: 'mock-account-id', 18 | CLOUDFLARE_MOCK_API_TOKEN: 'mock-api-token', 19 | } satisfies Partial, 20 | }, 21 | }, 22 | }, 23 | }, 24 | }) 25 | -------------------------------------------------------------------------------- /apps/dns-analytics/.dev.vars.example: -------------------------------------------------------------------------------- 1 | CLOUDFLARE_CLIENT_ID= 2 | CLOUDFLARE_CLIENT_SECRET= 3 | DEV_CLOUDFLARE_API_TOKEN= -------------------------------------------------------------------------------- /apps/dns-analytics/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ['@repo/eslint-config/default.cjs'], 5 | } 6 | -------------------------------------------------------------------------------- /apps/dns-analytics/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # dns-analytics 2 | 3 | ## 0.0.4 4 | 5 | ### Patch Changes 6 | 7 | - 3677a18: Remove extraneous log 8 | - Updated dependencies [3677a18] 9 | - @repo/mcp-common@0.16.3 10 | 11 | ## 0.0.3 12 | 13 | ### Patch Changes 14 | 15 | - 86c2e4f: Add API token passthrough auth 16 | - Updated dependencies [86c2e4f] 17 | - @repo/mcp-common@0.16.2 18 | 19 | ## 0.0.2 20 | 21 | ### Patch Changes 22 | 23 | - cf3771b: chore: add suffixes to common files in apps and packages 24 | 25 | It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. 26 | 27 | - Updated dependencies [cf3771b] 28 | - @repo/mcp-common@0.16.1 29 | - @repo/mcp-observability@0.31.1 30 | -------------------------------------------------------------------------------- /apps/dns-analytics/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | If you'd like to iterate and test your MCP server, you can do so in local development. 4 | 5 | ## Local Development 6 | 7 | 1. Create a `.dev.vars` file in your project root: 8 | 9 | If you're a Cloudflare employee: 10 | 11 | ``` 12 | CLOUDFLARE_CLIENT_ID=your_development_cloudflare_client_id 13 | CLOUDFLARE_CLIENT_SECRET=your_development_cloudflare_client_secret 14 | DEV_CLOUDFLARE_API_TOKEN=your_development_api_token 15 | ``` 16 | 17 | If you're an external contributor, you can provide a development API token (See [Cloudflare API](https://developers.cloudflare.com/api/) for information on creating an API Token): 18 | 19 | ``` 20 | DEV_DISABLE_OAUTH=true 21 | DEV_CLOUDFLARE_EMAIL=your_cloudflare_email 22 | # This is your api token with endpoint access. 23 | DEV_CLOUDFLARE_API_TOKEN=your_development_api_token 24 | ``` 25 | 26 | 2. Start the local development server: 27 | 28 | ```bash 29 | npx wrangler dev 30 | ``` 31 | 32 | 3. To test locally, open Inspector, and connect to `http://localhost:8976/sse`. 33 | Once you follow the prompts, you'll be able to "List Tools". You can also connect with any MCP client. 34 | 35 | ## Deploying the Worker ( Cloudflare employees only ) 36 | 37 | Set secrets via Wrangler: 38 | 39 | ```bash 40 | npx wrangler secret put CLOUDFLARE_CLIENT_ID -e 41 | npx wrangler secret put CLOUDFLARE_CLIENT_SECRET -e 42 | ``` 43 | 44 | ## Set up a KV namespace 45 | 46 | Create the KV namespace: 47 | 48 | ```bash 49 | npx wrangler kv namespace create "OAUTH_KV" 50 | ``` 51 | 52 | Then, update the Wrangler file with the generated KV namespace ID. 53 | 54 | ## Deploy & Test 55 | 56 | Deploy the MCP server to make it available on your workers.dev domain: 57 | 58 | ```bash 59 | npx wrangler deploy -e 60 | ``` 61 | 62 | Test the remote server using [Inspector](https://modelcontextprotocol.io/docs/tools/inspector): 63 | 64 | ```bash 65 | npx @modelcontextprotocol/inspector@latest 66 | ``` 67 | -------------------------------------------------------------------------------- /apps/dns-analytics/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dns-analytics", 3 | "version": "0.0.4", 4 | "private": true, 5 | "scripts": { 6 | "check:lint": "run-eslint-workers", 7 | "check:types": "run-tsc", 8 | "deploy": "wrangler deploy", 9 | "dev": "wrangler dev", 10 | "start": "wrangler dev", 11 | "types": "wrangler types --include-env=false", 12 | "test": "vitest run" 13 | }, 14 | "dependencies": { 15 | "@cloudflare/workers-oauth-provider": "0.0.5", 16 | "@hono/zod-validator": "0.4.3", 17 | "@modelcontextprotocol/sdk": "1.10.2", 18 | "@repo/mcp-common": "workspace:*", 19 | "@repo/mcp-observability": "workspace:*", 20 | "agents": "0.0.67", 21 | "cloudflare": "4.2.0", 22 | "hono": "4.7.6", 23 | "zod": "3.24.2" 24 | }, 25 | "devDependencies": { 26 | "@cloudflare/vitest-pool-workers": "0.8.14", 27 | "@types/node": "22.14.1", 28 | "prettier": "3.5.3", 29 | "typescript": "5.5.4", 30 | "vitest": "3.0.9", 31 | "wrangler": "4.10.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /apps/dns-analytics/src/dns-analytics.context.ts: -------------------------------------------------------------------------------- 1 | import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' 2 | import type { DNSAnalyticsMCP } from './dns-analytics.app' 3 | 4 | export interface Env { 5 | OAUTH_KV: KVNamespace 6 | ENVIRONMENT: 'development' | 'staging' | 'production' 7 | MCP_SERVER_NAME: string 8 | MCP_SERVER_VERSION: string 9 | CLOUDFLARE_CLIENT_ID: string 10 | CLOUDFLARE_CLIENT_SECRET: string 11 | MCP_OBJECT: DurableObjectNamespace 12 | USER_DETAILS: DurableObjectNamespace 13 | MCP_METRICS: AnalyticsEngineDataset 14 | SENTRY_ACCESS_CLIENT_ID: string 15 | SENTRY_ACCESS_CLIENT_SECRET: string 16 | GIT_HASH: string 17 | SENTRY_DSN: string 18 | DEV_DISABLE_OAUTH: string 19 | DEV_CLOUDFLARE_API_TOKEN: string 20 | DEV_CLOUDFLARE_EMAIL: string 21 | } 22 | -------------------------------------------------------------------------------- /apps/dns-analytics/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/workers.json", 3 | "include": ["*/**.ts", "./worker-configuration.d.ts", "./types.d.ts", "./vitest.config.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/dns-analytics/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { TestEnv } from './vitest.config' 2 | 3 | declare module 'cloudflare:test' { 4 | interface ProvidedEnv extends TestEnv {} 5 | } 6 | -------------------------------------------------------------------------------- /apps/dns-analytics/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' 2 | 3 | export interface TestEnv extends Env { 4 | CLOUDFLARE_MOCK_ACCOUNT_ID: string 5 | CLOUDFLARE_MOCK_API_TOKEN: string 6 | } 7 | 8 | export default defineWorkersConfig({ 9 | test: { 10 | poolOptions: { 11 | workers: { 12 | wrangler: { configPath: `${__dirname}/wrangler.jsonc` }, 13 | miniflare: { 14 | bindings: { 15 | CLOUDFLARE_MOCK_ACCOUNT_ID: 'mock-account-id', 16 | CLOUDFLARE_MOCK_API_TOKEN: 'mock-api-token', 17 | } satisfies Partial, 18 | }, 19 | }, 20 | }, 21 | }, 22 | }) 23 | -------------------------------------------------------------------------------- /apps/docs-autorag/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ['@repo/eslint-config/default.cjs'], 5 | } 6 | -------------------------------------------------------------------------------- /apps/docs-autorag/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # docs-autorag 2 | 3 | ## 0.0.4 4 | 5 | ### Patch Changes 6 | 7 | - 3677a18: Remove extraneous log 8 | - Updated dependencies [3677a18] 9 | - @repo/mcp-common@0.16.3 10 | 11 | ## 0.0.3 12 | 13 | ### Patch Changes 14 | 15 | - Updated dependencies [86c2e4f] 16 | - @repo/mcp-common@0.16.2 17 | 18 | ## 0.0.2 19 | 20 | ### Patch Changes 21 | 22 | - cf3771b: chore: add suffixes to common files in apps and packages 23 | 24 | It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. 25 | 26 | - Updated dependencies [cf3771b] 27 | - @repo/mcp-common@0.16.1 28 | - @repo/mcp-observability@0.31.1 29 | -------------------------------------------------------------------------------- /apps/docs-autorag/README.md: -------------------------------------------------------------------------------- 1 | # Model Context Protocol (MCP) Server + Cloudflare Documentation (via Autorag) 2 | 3 | This is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) server that supports remote MCP connections. It connects to an autorag instance (in this case, Cloudflare docs) 4 | 5 | To run this server, you'll need access to an autorag instance which has indexed the contents of cloudflare-docs: https://github.com/cloudflare/cloudflare-docs/ 6 | 7 | The Cloudflare account this worker is deployed on already has this Autorag instance setup and indexed. 8 | 9 | ## Running locally 10 | 11 | ``` 12 | pnpm run start 13 | ``` 14 | 15 | Then connect to the server via remote MCP at `http://localhost:8976/sse` 16 | 17 | ## Deploying 18 | 19 | ``` 20 | pnpm run deploy --env [ENVIRONMENT] 21 | ``` 22 | -------------------------------------------------------------------------------- /apps/docs-autorag/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs-autorag", 3 | "version": "0.0.4", 4 | "private": true, 5 | "scripts": { 6 | "check:lint": "run-eslint-workers", 7 | "check:types": "run-tsc", 8 | "deploy": "run-wrangler-deploy", 9 | "dev": "wrangler dev", 10 | "start": "wrangler dev", 11 | "types": "wrangler types --include-env=false", 12 | "test": "vitest run" 13 | }, 14 | "dependencies": { 15 | "@cloudflare/workers-oauth-provider": "0.0.5", 16 | "@hono/zod-validator": "0.4.3", 17 | "@modelcontextprotocol/sdk": "1.10.2", 18 | "@repo/mcp-common": "workspace:*", 19 | "@repo/mcp-observability": "workspace:*", 20 | "agents": "0.0.67", 21 | "cloudflare": "4.2.0", 22 | "hono": "4.7.6", 23 | "mime": "4.0.6", 24 | "zod": "3.24.2" 25 | }, 26 | "devDependencies": { 27 | "@cloudflare/vitest-pool-workers": "0.8.14", 28 | "@types/node": "22.14.1", 29 | "prettier": "3.5.3", 30 | "typescript": "5.5.4", 31 | "vitest": "3.0.9", 32 | "wrangler": "4.10.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /apps/docs-autorag/src/docs-autorag.app.ts: -------------------------------------------------------------------------------- 1 | import { McpAgent } from 'agents/mcp' 2 | 3 | import { createApiHandler } from '@repo/mcp-common/src/api-handler' 4 | import { getEnv } from '@repo/mcp-common/src/env' 5 | import { CloudflareMCPServer } from '@repo/mcp-common/src/server' 6 | 7 | import { registerDocsTools } from './tools/docs-autorag.tools' 8 | 9 | import type { Env } from './docs-autorag.context' 10 | 11 | const env = getEnv() 12 | 13 | // The docs MCP server isn't stateful, so we don't have state/props 14 | export type Props = never 15 | 16 | export type State = never 17 | 18 | export class CloudflareDocumentationMCP extends McpAgent { 19 | server = new CloudflareMCPServer({ 20 | wae: env.MCP_METRICS, 21 | serverInfo: { 22 | name: env.MCP_SERVER_NAME, 23 | version: env.MCP_SERVER_VERSION, 24 | }, 25 | }) 26 | 27 | constructor( 28 | public ctx: DurableObjectState, 29 | public env: Env 30 | ) { 31 | super(ctx, env) 32 | } 33 | 34 | async init() { 35 | registerDocsTools(this) 36 | } 37 | } 38 | 39 | export default createApiHandler(CloudflareDocumentationMCP) 40 | -------------------------------------------------------------------------------- /apps/docs-autorag/src/docs-autorag.context.ts: -------------------------------------------------------------------------------- 1 | import type { CloudflareDocumentationMCP } from './docs-autorag.app' 2 | 3 | export interface Env { 4 | ENVIRONMENT: 'development' | 'staging' | 'production' 5 | AUTORAG_NAME: 'cloudflare-docs-autorag' 6 | MCP_SERVER_NAME: string 7 | MCP_SERVER_VERSION: string 8 | MCP_OBJECT: DurableObjectNamespace 9 | MCP_METRICS: AnalyticsEngineDataset 10 | AI: Ai 11 | } 12 | -------------------------------------------------------------------------------- /apps/docs-autorag/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/workers.json", 3 | "include": ["*/**.ts", "./vitest.config.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/docs-autorag/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' 2 | 3 | import type { Env } from './src/docs-autorag.context' 4 | 5 | export interface TestEnv extends Env { 6 | CLOUDFLARE_MOCK_ACCOUNT_ID: string 7 | CLOUDFLARE_MOCK_API_TOKEN: string 8 | } 9 | 10 | export default defineWorkersConfig({ 11 | test: { 12 | poolOptions: { 13 | workers: { 14 | wrangler: { configPath: `${__dirname}/wrangler.jsonc` }, 15 | miniflare: { 16 | bindings: { 17 | CLOUDFLARE_MOCK_ACCOUNT_ID: 'mock-account-id', 18 | CLOUDFLARE_MOCK_API_TOKEN: 'mock-api-token', 19 | } satisfies Partial, 20 | }, 21 | }, 22 | }, 23 | }, 24 | }) 25 | -------------------------------------------------------------------------------- /apps/docs-vectorize/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ['@repo/eslint-config/default.cjs'], 5 | } 6 | -------------------------------------------------------------------------------- /apps/docs-vectorize/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # docs-vectorize 2 | 3 | ## 0.0.4 4 | 5 | ### Patch Changes 6 | 7 | - 3677a18: Remove extraneous log 8 | - Updated dependencies [3677a18] 9 | - @repo/mcp-common@0.16.3 10 | 11 | ## 0.0.3 12 | 13 | ### Patch Changes 14 | 15 | - Updated dependencies [86c2e4f] 16 | - @repo/mcp-common@0.16.2 17 | 18 | ## 0.0.2 19 | 20 | ### Patch Changes 21 | 22 | - cf3771b: chore: add suffixes to common files in apps and packages 23 | 24 | It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. 25 | 26 | - Updated dependencies [cf3771b] 27 | - @repo/mcp-common@0.16.1 28 | - @repo/mcp-observability@0.31.1 29 | -------------------------------------------------------------------------------- /apps/docs-vectorize/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs-vectorize", 3 | "version": "0.0.4", 4 | "private": true, 5 | "scripts": { 6 | "check:lint": "run-eslint-workers", 7 | "check:types": "run-tsc", 8 | "deploy": "run-wrangler-deploy", 9 | "dev": "wrangler dev --experimental-vectorize-bind-to-prod", 10 | "start": "npm run dev", 11 | "types": "wrangler types --include-env=false", 12 | "test": "vitest run" 13 | }, 14 | "dependencies": { 15 | "@cloudflare/workers-oauth-provider": "0.0.5", 16 | "@hono/zod-validator": "0.4.3", 17 | "@modelcontextprotocol/sdk": "1.10.2", 18 | "@repo/mcp-common": "workspace:*", 19 | "@repo/mcp-observability": "workspace:*", 20 | "agents": "0.0.67", 21 | "cloudflare": "4.2.0", 22 | "hono": "4.7.6", 23 | "mime": "4.0.6", 24 | "zod": "3.24.2" 25 | }, 26 | "devDependencies": { 27 | "@cloudflare/vitest-pool-workers": "0.8.14", 28 | "@types/node": "22.14.1", 29 | "prettier": "3.5.3", 30 | "typescript": "5.5.4", 31 | "vitest": "3.0.9", 32 | "wrangler": "4.10.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /apps/docs-vectorize/src/docs-vectorize.app.ts: -------------------------------------------------------------------------------- 1 | import { McpAgent } from 'agents/mcp' 2 | 3 | import { createApiHandler } from '@repo/mcp-common/src/api-handler' 4 | import { getEnv } from '@repo/mcp-common/src/env' 5 | import { CloudflareMCPServer } from '@repo/mcp-common/src/server' 6 | 7 | import { registerPrompts } from './prompts/docs-vectorize.prompts' 8 | import { registerDocsTools } from './tools/docs-vectorize.tools' 9 | 10 | import type { Env } from './docs-vectorize.context' 11 | 12 | const env = getEnv() 13 | 14 | // The docs MCP server isn't stateful, so we don't have state/props 15 | export type Props = never 16 | 17 | export type State = never 18 | 19 | export class CloudflareDocumentationMCP extends McpAgent { 20 | server = new CloudflareMCPServer({ 21 | wae: env.MCP_METRICS, 22 | serverInfo: { 23 | name: env.MCP_SERVER_NAME, 24 | version: env.MCP_SERVER_VERSION, 25 | }, 26 | }) 27 | 28 | constructor( 29 | public ctx: DurableObjectState, 30 | public env: Env 31 | ) { 32 | super(ctx, env) 33 | } 34 | 35 | async init() { 36 | registerDocsTools(this) 37 | registerPrompts(this) 38 | } 39 | } 40 | 41 | export default createApiHandler(CloudflareDocumentationMCP) 42 | -------------------------------------------------------------------------------- /apps/docs-vectorize/src/docs-vectorize.context.ts: -------------------------------------------------------------------------------- 1 | import type { CloudflareDocumentationMCP } from './docs-vectorize.app' 2 | 3 | export interface Env { 4 | ENVIRONMENT: 'development' | 'staging' | 'production' 5 | MCP_SERVER_NAME: string 6 | MCP_SERVER_VERSION: string 7 | MCP_OBJECT: DurableObjectNamespace 8 | MCP_METRICS: AnalyticsEngineDataset 9 | AI: Ai 10 | VECTORIZE: VectorizeIndex 11 | } 12 | -------------------------------------------------------------------------------- /apps/docs-vectorize/src/prompts/docs-vectorize.prompts.ts: -------------------------------------------------------------------------------- 1 | import type { CloudflareDocumentationMCP } from '../docs-vectorize.app' 2 | 3 | /** 4 | * Registers developer-platform-related prompts with the MCP server 5 | * @param agent The MCP server instance 6 | */ 7 | export function registerPrompts(agent: CloudflareDocumentationMCP) { 8 | agent.server.prompt( 9 | 'workers-prompt-full', 10 | 'Detailed prompt for generating Cloudflare Workers code (and other developer platform products) from https://developers.cloudflare.com/workers/prompt.txt', 11 | async () => ({ 12 | messages: [ 13 | { 14 | role: 'user', 15 | content: { 16 | type: 'text', 17 | text: await ( 18 | await fetch('https://developers.cloudflare.com/workers/prompt.txt', { 19 | cf: { cacheEverything: true, cacheTtl: 3600 }, 20 | }) 21 | ).text(), 22 | }, 23 | }, 24 | ], 25 | }) 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /apps/docs-vectorize/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/workers.json", 3 | "include": ["*/**.ts", "./vitest.config.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/docs-vectorize/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' 2 | 3 | import type { Env } from './src/docs-vectorize.context' 4 | 5 | export interface TestEnv extends Env { 6 | CLOUDFLARE_MOCK_ACCOUNT_ID: string 7 | CLOUDFLARE_MOCK_API_TOKEN: string 8 | } 9 | 10 | export default defineWorkersConfig({ 11 | test: { 12 | poolOptions: { 13 | workers: { 14 | wrangler: { configPath: `${__dirname}/wrangler.jsonc` }, 15 | miniflare: { 16 | bindings: { 17 | CLOUDFLARE_MOCK_ACCOUNT_ID: 'mock-account-id', 18 | CLOUDFLARE_MOCK_API_TOKEN: 'mock-api-token', 19 | } satisfies Partial, 20 | }, 21 | }, 22 | }, 23 | }, 24 | }) 25 | -------------------------------------------------------------------------------- /apps/graphql/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ['@repo/eslint-config/default.cjs'], 5 | } 6 | -------------------------------------------------------------------------------- /apps/graphql/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # graphql-mcp-server 2 | 3 | ## 0.0.4 4 | 5 | ### Patch Changes 6 | 7 | - 3677a18: Remove extraneous log 8 | - Updated dependencies [3677a18] 9 | - @repo/mcp-common@0.16.3 10 | 11 | ## 0.0.3 12 | 13 | ### Patch Changes 14 | 15 | - 86c2e4f: Add API token passthrough auth 16 | - Updated dependencies [86c2e4f] 17 | - @repo/mcp-common@0.16.2 18 | 19 | ## 0.0.2 20 | 21 | ### Patch Changes 22 | 23 | - Updated dependencies [cf3771b] 24 | - @repo/mcp-common@0.16.1 25 | - @repo/mcp-observability@0.31.1 26 | -------------------------------------------------------------------------------- /apps/graphql/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphql-mcp-server", 3 | "version": "0.0.4", 4 | "private": true, 5 | "scripts": { 6 | "check:lint": "run-eslint-workers", 7 | "check:types": "run-tsc", 8 | "deploy": "run-wrangler-deploy", 9 | "dev": "wrangler dev", 10 | "start": "wrangler dev", 11 | "types": "wrangler types --include-env=false", 12 | "test": "vitest run" 13 | }, 14 | "dependencies": { 15 | "@cloudflare/workers-oauth-provider": "0.0.5", 16 | "@hono/zod-validator": "0.4.3", 17 | "@modelcontextprotocol/sdk": "1.10.2", 18 | "@repo/mcp-common": "workspace:*", 19 | "@repo/mcp-observability": "workspace:*", 20 | "agents": "0.0.67", 21 | "cloudflare": "4.2.0", 22 | "hono": "4.7.6", 23 | "zod": "3.24.2", 24 | "lz-string": "1.5.0" 25 | }, 26 | "devDependencies": { 27 | "@cloudflare/vitest-pool-workers": "0.8.14", 28 | "@types/node": "22.14.1", 29 | "prettier": "3.5.3", 30 | "typescript": "5.5.4", 31 | "vitest": "3.0.9", 32 | "wrangler": "4.10.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /apps/graphql/src/graphql.context.ts: -------------------------------------------------------------------------------- 1 | import type { GraphQLMCP, UserDetails } from './graphql.app' 2 | 3 | export interface Env { 4 | OAUTH_KV: KVNamespace 5 | ENVIRONMENT: 'development' | 'staging' | 'production' 6 | MCP_SERVER_NAME: string 7 | MCP_SERVER_VERSION: string 8 | CLOUDFLARE_CLIENT_ID: string 9 | CLOUDFLARE_CLIENT_SECRET: string 10 | MCP_OBJECT: DurableObjectNamespace 11 | USER_DETAILS: DurableObjectNamespace 12 | MCP_METRICS: AnalyticsEngineDataset 13 | SENTRY_ACCESS_CLIENT_ID: string 14 | SENTRY_ACCESS_CLIENT_SECRET: string 15 | GIT_HASH: string 16 | SENTRY_DSN: string 17 | DEV_DISABLE_OAUTH: string 18 | DEV_CLOUDFLARE_API_TOKEN: string 19 | DEV_CLOUDFLARE_EMAIL: string 20 | } 21 | -------------------------------------------------------------------------------- /apps/graphql/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/workers.json", 3 | "include": ["*/**.ts", "./vitest.config.ts", "./types.d.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/graphql/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { TestEnv } from './vitest.config' 2 | 3 | declare module 'cloudflare:test' { 4 | interface ProvidedEnv extends TestEnv {} 5 | } 6 | -------------------------------------------------------------------------------- /apps/logpush/.dev.vars.example: -------------------------------------------------------------------------------- 1 | CLOUDFLARE_CLIENT_ID= 2 | CLOUDFLARE_CLIENT_SECRET= 3 | DEV_DISABLE_OAUTH= 4 | DEV_CLOUDFLARE_API_TOKEN= 5 | DEV_CLOUDFLARE_EMAIL= -------------------------------------------------------------------------------- /apps/logpush/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ['@repo/eslint-config/default.cjs'], 5 | } 6 | -------------------------------------------------------------------------------- /apps/logpush/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # logpush 2 | 3 | ## 0.0.4 4 | 5 | ### Patch Changes 6 | 7 | - 3677a18: Remove extraneous log 8 | - Updated dependencies [3677a18] 9 | - @repo/mcp-common@0.16.3 10 | 11 | ## 0.0.3 12 | 13 | ### Patch Changes 14 | 15 | - 86c2e4f: Add API token passthrough auth 16 | - Updated dependencies [86c2e4f] 17 | - @repo/mcp-common@0.16.2 18 | 19 | ## 0.0.2 20 | 21 | ### Patch Changes 22 | 23 | - cf3771b: chore: add suffixes to common files in apps and packages 24 | 25 | It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. 26 | 27 | - Updated dependencies [cf3771b] 28 | - @repo/mcp-common@0.16.1 29 | - @repo/mcp-observability@0.31.1 30 | -------------------------------------------------------------------------------- /apps/logpush/README.md: -------------------------------------------------------------------------------- 1 | # Cloudflare Logpush MCP Server 📜 2 | 3 | This is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) server that supports remote MCP 4 | connections, with Cloudflare OAuth built-in. 5 | 6 | It integrates tools powered by the [Cloudflare Logpush API](https://developers.cloudflare.com/logs/) to provide insights into Logpush jobs. 7 | 8 | ## 🔨 Available Tools 9 | 10 | Currently available tools: 11 | 12 | | **Category** | **Tool** | **Description** | 13 | | --------------------------- | ---------------------------- | ----------------------------------------------------------------------- | 14 | | **Logpush Jobs By Account** | `logpush_jobs_by_account_id` | Fetches Logpush jobs by specific account or all accounts under the user | 15 | 16 | This MCP server is still a work in progress, and we plan to add more tools in the future. 17 | 18 | ### Prompt Examples 19 | 20 | - `Which Logpush jobs failed recently?` 21 | - `Do any of my Logpush jobs in my account have errors?` 22 | - `Can you list all the enabled job failures from today?` 23 | 24 | ## Access the remote MCP server from any MCP Client 25 | 26 | If your MCP client has first class support for remote MCP servers, the client will provide a way to accept the server URL (`https://logs.mcp.cloudflare.com`) directly within its interface (for example in [Cloudflare AI Playground](https://playground.ai.cloudflare.com/)). 27 | 28 | If your client does not yet support remote MCP servers, you will need to set up its resepective configuration file using [mcp-remote](https://www.npmjs.com/package/mcp-remote) to specify which servers your client can access. 29 | 30 | Replace the content with the following configuration: 31 | 32 | ```json 33 | { 34 | "mcpServers": { 35 | "cloudflare": { 36 | "command": "npx", 37 | "args": ["mcp-remote", "https://logs.mcp.cloudflare.com/sse"] 38 | } 39 | } 40 | } 41 | ``` 42 | 43 | Once you've set up your configuration file, restart MCP client and a browser window will open showing your OAuth login page. Proceed through the authentication flow to grant the client access to your MCP server. After you grant access, the tools will become available for you to use. 44 | -------------------------------------------------------------------------------- /apps/logpush/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "logpush", 3 | "version": "0.0.4", 4 | "private": true, 5 | "scripts": { 6 | "check:lint": "run-eslint-workers", 7 | "check:types": "run-tsc", 8 | "deploy": "wrangler deploy", 9 | "dev": "wrangler dev", 10 | "start": "wrangler dev", 11 | "types": "wrangler types --include-env=false", 12 | "test": "vitest run" 13 | }, 14 | "dependencies": { 15 | "@cloudflare/workers-oauth-provider": "0.0.5", 16 | "@hono/zod-validator": "0.4.3", 17 | "@modelcontextprotocol/sdk": "1.10.2", 18 | "@repo/mcp-common": "workspace:*", 19 | "@repo/mcp-observability": "workspace:*", 20 | "agents": "0.0.67", 21 | "cloudflare": "4.2.0", 22 | "hono": "4.7.6", 23 | "zod": "3.24.2" 24 | }, 25 | "devDependencies": { 26 | "@cloudflare/vitest-pool-workers": "0.8.14", 27 | "@types/node": "22.14.1", 28 | "prettier": "3.5.3", 29 | "typescript": "5.5.4", 30 | "vitest": "3.0.9", 31 | "wrangler": "4.10.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /apps/logpush/src/logpush.context.ts: -------------------------------------------------------------------------------- 1 | import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' 2 | import type { LogsMCP } from './logpush.app' 3 | 4 | export interface Env { 5 | OAUTH_KV: KVNamespace 6 | ENVIRONMENT: 'development' | 'staging' | 'production' 7 | MCP_SERVER_NAME: string 8 | MCP_SERVER_VERSION: string 9 | CLOUDFLARE_CLIENT_ID: string 10 | CLOUDFLARE_CLIENT_SECRET: string 11 | MCP_OBJECT: DurableObjectNamespace 12 | USER_DETAILS: DurableObjectNamespace 13 | MCP_METRICS: AnalyticsEngineDataset 14 | DEV_DISABLE_OAUTH: string 15 | DEV_CLOUDFLARE_API_TOKEN: string 16 | DEV_CLOUDFLARE_EMAIL: string 17 | } 18 | -------------------------------------------------------------------------------- /apps/logpush/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/workers.json", 3 | "include": ["*/**.ts", "./vitest.config.ts", "./types.d.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/logpush/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { TestEnv } from './vitest.config' 2 | 3 | declare module 'cloudflare:test' { 4 | interface ProvidedEnv extends TestEnv {} 5 | } 6 | -------------------------------------------------------------------------------- /apps/logpush/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' 2 | 3 | import type { Env } from './src/logpush.context' 4 | 5 | export interface TestEnv extends Env { 6 | CLOUDFLARE_MOCK_ACCOUNT_ID: string 7 | CLOUDFLARE_MOCK_API_TOKEN: string 8 | } 9 | 10 | export default defineWorkersConfig({ 11 | test: { 12 | poolOptions: { 13 | workers: { 14 | wrangler: { configPath: `${__dirname}/wrangler.jsonc` }, 15 | miniflare: { 16 | bindings: { 17 | CLOUDFLARE_MOCK_ACCOUNT_ID: 'mock-account-id', 18 | CLOUDFLARE_MOCK_API_TOKEN: 'mock-api-token', 19 | } satisfies Partial, 20 | }, 21 | }, 22 | }, 23 | }, 24 | }) 25 | -------------------------------------------------------------------------------- /apps/radar/.dev.vars.example: -------------------------------------------------------------------------------- 1 | CLOUDFLARE_CLIENT_ID= 2 | CLOUDFLARE_CLIENT_SECRET= 3 | DEV_DISABLE_OAUTH= 4 | DEV_CLOUDFLARE_API_TOKEN= 5 | DEV_CLOUDFLARE_EMAIL= 6 | -------------------------------------------------------------------------------- /apps/radar/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ['@repo/eslint-config/default.cjs'], 5 | } 6 | -------------------------------------------------------------------------------- /apps/radar/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # cloudflare-radar-mcp-server 2 | 3 | ## 0.0.4 4 | 5 | ### Patch Changes 6 | 7 | - 3677a18: Remove extraneous log 8 | - Updated dependencies [3677a18] 9 | - @repo/mcp-common@0.16.3 10 | 11 | ## 0.0.3 12 | 13 | ### Patch Changes 14 | 15 | - 86c2e4f: Add API token passthrough auth 16 | - Updated dependencies [86c2e4f] 17 | - @repo/mcp-common@0.16.2 18 | 19 | ## 0.0.2 20 | 21 | ### Patch Changes 22 | 23 | - cf3771b: chore: add suffixes to common files in apps and packages 24 | 25 | It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. 26 | 27 | - Updated dependencies [cf3771b] 28 | - @repo/mcp-common@0.16.1 29 | - @repo/mcp-observability@0.31.1 30 | -------------------------------------------------------------------------------- /apps/radar/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | If you'd like to iterate and test your MCP server, you can do so in local development. 4 | 5 | ## Local Development 6 | 7 | 1. Create a `.dev.vars` file in your project root: 8 | 9 | If you're a Cloudflare employee: 10 | 11 | ``` 12 | CLOUDFLARE_CLIENT_ID=your_development_cloudflare_client_id 13 | CLOUDFLARE_CLIENT_SECRET=your_development_cloudflare_client_secret 14 | ``` 15 | 16 | If you're an external contributor, you can provide a development API token: 17 | 18 | ``` 19 | DEV_DISABLE_OAUTH=true 20 | DEV_CLOUDFLARE_EMAIL=your_cloudflare_email 21 | # This is your global api token 22 | DEV_CLOUDFLARE_API_TOKEN=your_development_api_token 23 | ``` 24 | 25 | 2. Start the local development server: 26 | 27 | ```bash 28 | npx wrangler dev 29 | ``` 30 | 31 | 3. To test locally, open Inspector, and connect to `http://localhost:8976/sse`. 32 | Once you follow the prompts, you'll be able to "List Tools". You can also connect with any MCP client. 33 | 34 | ## Deploying the Worker ( Cloudflare employees only ) 35 | 36 | Set secrets via Wrangler: 37 | 38 | ```bash 39 | npx wrangler secret put CLOUDFLARE_CLIENT_ID -e 40 | npx wrangler secret put CLOUDFLARE_CLIENT_SECRET -e 41 | ``` 42 | 43 | ## Set up a KV namespace 44 | 45 | Create the KV namespace: 46 | 47 | ```bash 48 | npx wrangler kv namespace create "OAUTH_KV" 49 | ``` 50 | 51 | Then, update the Wrangler file with the generated KV namespace ID. 52 | 53 | ## Deploy & Test 54 | 55 | Deploy the MCP server to make it available on your workers.dev domain: 56 | 57 | ```bash 58 | npx wrangler deploy -e 59 | ``` 60 | 61 | Test the remote server using [Inspector](https://modelcontextprotocol.io/docs/tools/inspector): 62 | 63 | ```bash 64 | npx @modelcontextprotocol/inspector@latest 65 | ``` 66 | -------------------------------------------------------------------------------- /apps/radar/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloudflare-radar-mcp-server", 3 | "version": "0.0.4", 4 | "private": true, 5 | "scripts": { 6 | "check:lint": "run-eslint-workers", 7 | "check:types": "run-tsc", 8 | "deploy": "run-wrangler-deploy", 9 | "dev": "wrangler dev", 10 | "start": "wrangler dev", 11 | "types": "wrangler types --include-env=false", 12 | "test": "vitest run" 13 | }, 14 | "dependencies": { 15 | "@cloudflare/workers-oauth-provider": "0.0.5", 16 | "@hono/zod-validator": "0.4.3", 17 | "@modelcontextprotocol/sdk": "1.10.2", 18 | "@repo/mcp-common": "workspace:*", 19 | "@repo/mcp-observability": "workspace:*", 20 | "agents": "0.0.67", 21 | "cloudflare": "4.2.0", 22 | "hono": "4.7.6", 23 | "zod": "3.24.2" 24 | }, 25 | "devDependencies": { 26 | "@cloudflare/vitest-pool-workers": "0.8.14", 27 | "@types/node": "22.14.1", 28 | "prettier": "3.5.3", 29 | "typescript": "5.5.4", 30 | "vitest": "3.0.9", 31 | "wrangler": "4.10.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /apps/radar/src/radar.context.ts: -------------------------------------------------------------------------------- 1 | import type { RadarMCP, UserDetails } from './radar.app' 2 | 3 | export interface Env { 4 | OAUTH_KV: KVNamespace 5 | ENVIRONMENT: 'development' | 'staging' | 'production' 6 | MCP_SERVER_NAME: string 7 | MCP_SERVER_VERSION: string 8 | CLOUDFLARE_CLIENT_ID: string 9 | CLOUDFLARE_CLIENT_SECRET: string 10 | MCP_OBJECT: DurableObjectNamespace 11 | USER_DETAILS: DurableObjectNamespace 12 | MCP_METRICS: AnalyticsEngineDataset 13 | DEV_DISABLE_OAUTH: string 14 | DEV_CLOUDFLARE_API_TOKEN: string 15 | DEV_CLOUDFLARE_EMAIL: string 16 | } 17 | 18 | export const BASE_INSTRUCTIONS = /* markdown */ ` 19 | # Cloudflare Radar MCP Server 20 | 21 | This server integrates tools powered by the Cloudflare Radar API to provide insights into global Internet traffic, 22 | trends, and other related utilities. 23 | 24 | An active account is **only required** for URL Scanner-related tools (e.g., \`scan_url\`). 25 | 26 | For tools related to Internet trends and insights, analyze the results and, when appropriate, generate visualizations 27 | such as line charts, pie charts, bar charts, stacked area charts, choropleth maps, treemaps, or other relevant chart types. 28 | 29 | ### Making comparisons 30 | 31 | Many tools support **array-based filters** to enable comparisons across multiple criteria. 32 | In such cases, the array index corresponds to a distinct data series. 33 | For each data series, provide a corresponding \`dateRange\`, or alternatively a \`dateStart\` and \`dateEnd\` pair. 34 | Example: To compare HTTP traffic between Portugal and Spain over the last 7 days: 35 | - \`dateRange: ["7d", "7d"]\` 36 | - \`location: ["PT", "ES"]\` 37 | 38 | This applies to date filters and other filters that support comparison across multiple values. 39 | If a tool does **not** support array-based filters, you can achieve the same comparison by making multiple separate 40 | calls to the tool. 41 | ` 42 | -------------------------------------------------------------------------------- /apps/radar/src/types/url-scanner.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file contains the validators for the URL scanner tools. 3 | */ 4 | import { z } from 'zod' 5 | 6 | export const UrlParam = z 7 | .string() 8 | .url() 9 | .describe('A valid URL including protocol (e.g., "https://example.com").') 10 | 11 | export const CreateScanResult = z 12 | .object({ 13 | uuid: z.string(), 14 | }) 15 | .passthrough() 16 | -------------------------------------------------------------------------------- /apps/radar/src/utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Resolves and invokes a method dynamically based on the provided slugs. 3 | * 4 | * This function traverses the object based on the `slugs` array, binds the method 5 | * to its correct context, and invokes it with the provided parameters. 6 | * 7 | * @param {Object} client - The root object (e.g., `client.radar.http`) to resolve methods from. 8 | * @param {string[]} path - The path to the desired method. 9 | * @param {Object} params - The parameters to pass when invoking the resolved method. 10 | * @returns {Promise} The result of the method invocation. 11 | */ 12 | export async function resolveAndInvoke(client: any, path: string, params: any): Promise { 13 | const slugs = path.split('/') 14 | const method = slugs.reduce((acc, key) => acc?.[key], client) 15 | const parentContext = slugs.slice(0, -1).reduce((acc, key) => acc?.[key], client) 16 | return await method.bind(parentContext)(params) 17 | } 18 | -------------------------------------------------------------------------------- /apps/radar/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/workers.json", 3 | "include": ["*/**.ts", "./vitest.config.ts", "./types.d.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/radar/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { TestEnv } from './vitest.config' 2 | 3 | declare module 'cloudflare:test' { 4 | interface ProvidedEnv extends TestEnv {} 5 | } 6 | -------------------------------------------------------------------------------- /apps/radar/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' 2 | 3 | import type { Env } from './src/radar.context' 4 | 5 | export interface TestEnv extends Env { 6 | CLOUDFLARE_MOCK_ACCOUNT_ID: string 7 | CLOUDFLARE_MOCK_API_TOKEN: string 8 | } 9 | 10 | export default defineWorkersConfig({ 11 | test: { 12 | poolOptions: { 13 | workers: { 14 | wrangler: { configPath: `${__dirname}/wrangler.jsonc` }, 15 | miniflare: { 16 | bindings: { 17 | CLOUDFLARE_MOCK_ACCOUNT_ID: 'mock-account-id', 18 | CLOUDFLARE_MOCK_API_TOKEN: 'mock-api-token', 19 | } satisfies Partial, 20 | }, 21 | }, 22 | }, 23 | }, 24 | }) 25 | -------------------------------------------------------------------------------- /apps/sandbox-container/.dev.vars.example: -------------------------------------------------------------------------------- 1 | CLOUDFLARE_CLIENT_ID= 2 | CLOUDFLARE_CLIENT_SECRET= 3 | DEV_DISABLE_OAUTH= 4 | DEV_CLOUDFLARE_API_TOKEN= 5 | DEV_CLOUDFLARE_EMAIL= -------------------------------------------------------------------------------- /apps/sandbox-container/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ['@repo/eslint-config/default.cjs'], 5 | } 6 | -------------------------------------------------------------------------------- /apps/sandbox-container/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # containers-mcp 2 | 3 | ## 0.0.4 4 | 5 | ### Patch Changes 6 | 7 | - 3677a18: Remove extraneous log 8 | - Updated dependencies [3677a18] 9 | - @repo/mcp-common@0.16.3 10 | 11 | ## 0.0.3 12 | 13 | ### Patch Changes 14 | 15 | - 86c2e4f: Add API token passthrough auth 16 | - Updated dependencies [86c2e4f] 17 | - @repo/mcp-common@0.16.2 18 | 19 | ## 0.0.2 20 | 21 | ### Patch Changes 22 | 23 | - cf3771b: chore: add suffixes to common files in apps and packages 24 | 25 | It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. 26 | 27 | - Updated dependencies [cf3771b] 28 | - @repo/mcp-common@0.16.1 29 | - @repo/eval-tools@0.31.1 30 | - @repo/mcp-observability@0.31.1 31 | -------------------------------------------------------------------------------- /apps/sandbox-container/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Container MCP Server 2 | 3 | This is a simple MCP-based interface for a sandboxed development environment. 4 | 5 | ## Local dev 6 | 7 | Cloudchamber local dev isn't implemented yet, so we are doing a bit of a hack to just run the server in your local environment. Because of this, testing the container(s) and container manager locally is not possible at this time. 8 | 9 | Do the following from within the sandbox-container app: 10 | 11 | 1. Copy the `.dev.vars.example` file to a new `.dev.vars` file. 12 | 2. Get the Cloudflare client id and secret from a team member and add them to the `.dev.vars` file. 13 | 3. Run `pnpm i` then `pnpm dev` to start the MCP server. 14 | 4. Run `pnpx @modelcontextprotocol/inspector` to start the MCP inspector client. 15 | 5. Open the inspector client in your browser and connect to the server via `http://localhost:8976/sse`. 16 | 17 | Note: Temporary files created through files tool calls are stored in the workdir folder of this app. 18 | 19 | ## Deploying 20 | 21 | 1. Make sure the docker daemon is running 22 | 23 | 2. Disable WARP and run 24 | 25 | ``` 26 | npx https://prerelease-registry.devprod.cloudflare.dev/workers-sdk/runs/14387504770/npm-package-wrangler-8740 deploy 27 | ``` 28 | 29 | 3. Add to your Claude config. If using with Claude, you'll need to disable WARP: 30 | 31 | ``` 32 | { 33 | "mcpServers": { 34 | "container": { 35 | "command": "npx", 36 | "args": [ 37 | "mcp-remote", 38 | // this is my deployed instance 39 | "https://container-starter-2.cmsparks.workers.dev/sse" 40 | ] 41 | } 42 | } 43 | } 44 | ``` 45 | -------------------------------------------------------------------------------- /apps/sandbox-container/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use Alpine as base for minimal size 2 | FROM alpine:3.19 as base 3 | 4 | # Install necessary packages while minimizing layers 5 | # We combine commands with && and clean cache in the same layer 6 | # to reduce the image size 7 | RUN apk update && \ 8 | apk add --no-cache \ 9 | # Core utilities 10 | git \ 11 | curl \ 12 | wget \ 13 | # Build essentials 14 | build-base \ 15 | # Python and pip 16 | python3 \ 17 | py3-pip \ 18 | # Node and npm 19 | nodejs \ 20 | npm && \ 21 | # Clean up the cache to reduce image size 22 | rm -rf /var/cache/apk/* && \ 23 | # Create symlink for python 24 | ln -sf /usr/bin/python3 /usr/bin/python 25 | 26 | # Install pnpm in a separate layer for better caching 27 | RUN npm install -g pnpm && \ 28 | rm -rf /root/.npm 29 | 30 | # Set up pnpm environment 31 | ENV PNPM_HOME=/usr/local/bin 32 | ENV PATH=$PNPM_HOME:$PATH 33 | 34 | # Set working directory 35 | WORKDIR /app 36 | 37 | # Set environment variables 38 | ENV PATH="/app/node_modules/.bin:${PATH}" 39 | 40 | ### 41 | # STAGE: PRUNE - Generate a partial monorepo for the sandbox-container app. The output will be placed into a directory named "out" 42 | ### 43 | FROM base AS prune 44 | WORKDIR /app 45 | RUN pnpm install turbo --global 46 | COPY . . 47 | RUN turbo prune containers-starter 48 | 49 | ### 50 | # STAGE: INSTALL & RUN 51 | ### 52 | FROM base AS installer 53 | WORKDIR /app 54 | 55 | COPY --from=prune /app/out/ . 56 | RUN pnpm install --frozen-lockfile --only=production 57 | 58 | WORKDIR /app/apps/sandbox-container 59 | 60 | # Expose the port your Node.js server will run on 61 | EXPOSE 8080 62 | 63 | CMD ["pnpm", "run", "start:container"] 64 | -------------------------------------------------------------------------------- /apps/sandbox-container/container/fileUtils.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'node:fs/promises' 2 | import path from 'node:path' 3 | import mime from 'mime' 4 | 5 | // this is because there isn't a "real" directory mime type, so we're reusing the "text/directory" mime type 6 | // so claude doesn't give an error 7 | export const DIRECTORY_CONTENT_TYPE = 'text/directory' 8 | 9 | export async function get_file_name_from_path(path: string): Promise { 10 | path = path.replace('/files/contents', '') 11 | path = path.endsWith('/') ? path.substring(0, path.length - 1) : path 12 | 13 | return path 14 | } 15 | 16 | export async function list_files_in_directory(dirPath: string): Promise { 17 | const files: string[] = [] 18 | try { 19 | const dir = await fs.readdir(path.join(process.cwd(), dirPath), { 20 | withFileTypes: true, 21 | }) 22 | for (const dirent of dir) { 23 | const relPath = path.relative(process.cwd(), `${dirPath}/${dirent.name}`) 24 | files.push(`file:///${relPath}`) 25 | } 26 | } catch (error) { 27 | throw new Error('Failed to read directory') 28 | } 29 | 30 | return files 31 | } 32 | 33 | export async function get_mime_type(path: string): Promise { 34 | let mimeType = mime.getType(path) 35 | if (mimeType && mimeType === 'inode/directory') { 36 | mimeType = DIRECTORY_CONTENT_TYPE 37 | } 38 | return mimeType 39 | } 40 | -------------------------------------------------------------------------------- /apps/sandbox-container/container/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["es2023"], 4 | "module": "ES2022", 5 | "target": "es2022", 6 | "types": ["@types/node"], 7 | "strict": true, 8 | "esModuleInterop": true, 9 | "skipLibCheck": true, 10 | "moduleResolution": "bundler", 11 | "noEmit": true, 12 | "allowImportingTsExtensions": true 13 | }, 14 | "include": ["**/*.ts"], 15 | "exclude": [] 16 | } 17 | -------------------------------------------------------------------------------- /apps/sandbox-container/evals/exec.eval.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'vitest' 2 | import { describeEval } from 'vitest-evals' 3 | 4 | import { runTask } from '@repo/eval-tools/src/runTask' 5 | import { checkFactuality } from '@repo/eval-tools/src/scorers' 6 | import { eachModel } from '@repo/eval-tools/src/test-models' 7 | 8 | import { initializeClient } from './utils' 9 | 10 | eachModel('$modelName', ({ model }) => { 11 | describeEval('Runs a python file in a container', { 12 | data: async () => [ 13 | { 14 | input: 'Create a hello world python script and run it', 15 | expected: `The container_file_write tool was called, containing a file ending in .py.\ 16 | Then the container_file_exec tool was called with python or python3 as one of the arguments`, 17 | }, 18 | ], 19 | task: async (input) => { 20 | const client = await initializeClient() 21 | const { promptOutput, toolCalls } = await runTask(client, model, input) 22 | 23 | expect(toolCalls).toEqual( 24 | expect.arrayContaining([ 25 | expect.objectContaining({ 26 | type: 'tool-call', 27 | toolName: 'container_exec', 28 | args: { 29 | args: expect.objectContaining({ 30 | args: expect.stringContaining('python'), 31 | }), 32 | }, 33 | }), 34 | ]) 35 | ) 36 | 37 | expect(toolCalls).toEqual( 38 | expect.arrayContaining([ 39 | expect.objectContaining({ 40 | type: 'tool-call', 41 | toolName: 'container_file_write', 42 | args: { 43 | args: expect.objectContaining({ 44 | path: expect.stringContaining('.py'), 45 | }), 46 | }, 47 | }), 48 | ]) 49 | ) 50 | 51 | return promptOutput 52 | }, 53 | scorers: [checkFactuality], 54 | threshold: 1, 55 | timeout: 60000, 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /apps/sandbox-container/evals/initialize.eval.ts: -------------------------------------------------------------------------------- 1 | import { describeEval } from 'vitest-evals' 2 | 3 | import { runTask } from '@repo/eval-tools/src/runTask' 4 | import { checkFactuality } from '@repo/eval-tools/src/scorers' 5 | import { eachModel } from '@repo/eval-tools/src/test-models' 6 | 7 | import { initializeClient } from './utils' 8 | 9 | eachModel('$modelName', ({ model }) => { 10 | describeEval('Runs container initialize', { 11 | data: async () => [ 12 | { 13 | input: 'create and ping a container', 14 | expected: 15 | 'The container_initialize tool was called and then the container_ping tool was called', 16 | }, 17 | ], 18 | task: async (input) => { 19 | const client = await initializeClient() 20 | const { promptOutput } = await runTask(client, model, input) 21 | return promptOutput 22 | }, 23 | scorers: [checkFactuality], 24 | threshold: 1, 25 | timeout: 60000, 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /apps/sandbox-container/evals/utils.ts: -------------------------------------------------------------------------------- 1 | import { MCPClientManager } from 'agents/mcp/client' 2 | 3 | export async function initializeClient(): Promise { 4 | const clientManager = new MCPClientManager('test-client', '0.0.0') 5 | await clientManager.connect('http://localhost:8976/sse') 6 | return clientManager 7 | } 8 | -------------------------------------------------------------------------------- /apps/sandbox-container/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "containers-mcp", 3 | "version": "0.0.4", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "check:types": "run-tsc", 8 | "check:lint": "run-eslint-workers", 9 | "deploy": "wrangler deploy", 10 | "dev": "concurrently \"tsx container/sandbox.container.app.ts\" \"wrangler dev --var \"ENVIRONMENT:dev\"\"", 11 | "build:container": "docker build --platform linux/amd64 --tag sandbox-container:$(git rev-parse --short HEAD) -f Dockerfile ../../ && wrangler containers push sandbox-container:$(git rev-parse --short HEAD)", 12 | "start": "wrangler dev", 13 | "start:container": "tsx container/sandbox.container.app.ts", 14 | "postinstall": "mkdir -p workdir", 15 | "test": "vitest", 16 | "types": "wrangler types --include-env=false", 17 | "eval:dev": "start-server-and-test --expect 404 eval:server http://localhost:8976 'vitest --testTimeout=60000 --config vitest.config.evals.ts'", 18 | "eval:server": "concurrently \"tsx container/sandbox.container.app.ts\" \"wrangler dev --var ENVIRONMENT:test --var DEV_DISABLE_OAUTH:true --var DEV_CLOUDFLARE_EMAIL:mcp-server-eval-account@workers-for-platforms-dev.cfdata.org\"", 19 | "eval:ci": "start-server-and-test --expect 404 eval:server http://localhost:8976 'vitest run --testTimeout=60000 --config vitest.config.evals.ts'" 20 | }, 21 | "dependencies": { 22 | "@cloudflare/workers-oauth-provider": "0.0.5", 23 | "@hono/node-server": "1.13.8", 24 | "@hono/zod-validator": "0.4.3", 25 | "@modelcontextprotocol/sdk": "1.10.2", 26 | "@n8n/json-schema-to-zod": "1.1.0", 27 | "@repo/eval-tools": "workspace:*", 28 | "@repo/mcp-common": "workspace:*", 29 | "@repo/mcp-observability": "workspace:*", 30 | "@types/node": "22.14.1", 31 | "agents": "0.0.67", 32 | "cron-schedule": "5.0.4", 33 | "esbuild": "0.25.1", 34 | "hono": "4.7.6", 35 | "mime": "4.0.6", 36 | "simple-git-hooks": "2.12.1", 37 | "tsx": "4.19.3", 38 | "vitest-evals": "0.1.4", 39 | "zod": "3.24.2" 40 | }, 41 | "devDependencies": { 42 | "@cloudflare/vitest-pool-workers": "0.8.14", 43 | "@types/mock-fs": "4.13.4", 44 | "@types/node": "22.14.1", 45 | "ai": "4.3.10", 46 | "concurrently": "9.1.2", 47 | "mock-fs": "5.5.0", 48 | "start-server-and-test": "2.0.11", 49 | "wrangler": "4.10.0" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /apps/sandbox-container/server/containerManager.ts: -------------------------------------------------------------------------------- 1 | import { DurableObject } from 'cloudflare:workers' 2 | 3 | import { getEnv } from '@repo/mcp-common/src/env' 4 | import { MetricsTracker } from '@repo/mcp-observability' 5 | 6 | import { ContainerEvent } from './metrics' 7 | 8 | import type { Env } from './sandbox.server.context' 9 | 10 | const env = getEnv() 11 | export class ContainerManager extends DurableObject { 12 | metrics = new MetricsTracker(env.MCP_METRICS, { 13 | name: env.MCP_SERVER_NAME, 14 | version: env.MCP_SERVER_VERSION, 15 | }) 16 | 17 | constructor( 18 | public ctx: DurableObjectState, 19 | public env: Env 20 | ) { 21 | super(ctx, env) 22 | } 23 | 24 | async trackContainer(id: string) { 25 | await this.ctx.storage.put(id, new Date()) 26 | } 27 | 28 | async killContainer(id: string) { 29 | await this.ctx.storage.delete(id) 30 | } 31 | 32 | async tryKillOldContainers() { 33 | const activeContainers = await this.ctx.storage.list() 34 | for (const c of activeContainers) { 35 | const id = c[0] 36 | const now = new Date() 37 | const time = c[1] 38 | 39 | console.log(id, time, now, now.valueOf() - time.valueOf()) 40 | 41 | // 15m timeout for container lifetime 42 | if (now.valueOf() - time.valueOf() > 15 * 60 * 1000) { 43 | const doId = this.env.USER_CONTAINER.idFromString(id) 44 | const stub = this.env.USER_CONTAINER.get(doId) 45 | await stub.destroyContainer() 46 | await this.killContainer(id) 47 | } 48 | } 49 | } 50 | 51 | async listActive(): Promise { 52 | const activeContainers = await this.ctx.storage.list() 53 | const activeIds: string[] = [] 54 | for (const c of activeContainers.keys()) { 55 | activeIds.push(c) 56 | } 57 | 58 | this.metrics.logEvent( 59 | new ContainerEvent({ 60 | active: activeIds.length, 61 | }) 62 | ) 63 | 64 | return activeIds 65 | } 66 | } 67 | 68 | export function getContainerManager(env: Env): DurableObjectStub { 69 | const id = env.CONTAINER_MANAGER.idFromName('manager') 70 | return env.CONTAINER_MANAGER.get(id) 71 | } 72 | -------------------------------------------------------------------------------- /apps/sandbox-container/server/metrics.ts: -------------------------------------------------------------------------------- 1 | import { MetricsEvent, MetricsEventIndexIds } from '@repo/mcp-observability' 2 | 3 | export class ContainerEvent extends MetricsEvent { 4 | constructor( 5 | private containers: { 6 | active?: number 7 | } 8 | ) { 9 | super() 10 | } 11 | 12 | toDataPoint(): AnalyticsEngineDataPoint { 13 | return { 14 | indexes: [MetricsEventIndexIds.CONTAINER_MANAGER], 15 | doubles: this.mapDoubles({ 16 | double1: this.containers.active, 17 | }), 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /apps/sandbox-container/server/sandbox.server.context.ts: -------------------------------------------------------------------------------- 1 | import type { ContainerManager, ContainerMcpAgent, UserContainer } from './sandbox.server.app' 2 | 3 | export interface Env { 4 | OAUTH_KV: KVNamespace 5 | CLOUDFLARE_CLIENT_ID: string 6 | CLOUDFLARE_CLIENT_SECRET: string 7 | ENVIRONMENT: 'dev' 8 | MCP_SERVER_NAME: string 9 | MCP_SERVER_VERSION: string 10 | OPENAI_API_KEY: string 11 | AI_GATEWAY_TOKEN: string 12 | CLOUDFLARE_ACCOUNT_ID: string 13 | AI_GATEWAY_ID: string 14 | MCP_OBJECT: DurableObjectNamespace 15 | CONTAINER_MANAGER: DurableObjectNamespace 16 | USER_CONTAINER: DurableObjectNamespace 17 | USER_BLOCKLIST: KVNamespace 18 | MCP_METRICS: AnalyticsEngineDataset 19 | AI: Ai 20 | DEV_DISABLE_OAUTH: string 21 | DEV_CLOUDFLARE_API_TOKEN: string 22 | DEV_CLOUDFLARE_EMAIL: string 23 | } 24 | -------------------------------------------------------------------------------- /apps/sandbox-container/server/utils.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | 3 | import { stripProtocolFromFilePath } from './utils' 4 | 5 | describe('get_file_name_from_path', () => { 6 | it('strips file:// protocol from path', async () => { 7 | const path = await stripProtocolFromFilePath('file:///files/contents/cats') 8 | expect(path).toBe('/files/contents/cats') 9 | }), 10 | it('leaves protocol-less paths untouched', async () => { 11 | const path = await stripProtocolFromFilePath('/files/contents/cats') 12 | expect(path).toBe('/files/contents/cats') 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /apps/sandbox-container/server/utils.ts: -------------------------------------------------------------------------------- 1 | export async function fileToBase64(blob: Blob): Promise { 2 | // Use ArrayBuffer instead of text() for binary data 3 | const arrayBuffer = await blob.arrayBuffer() 4 | const byteArray = new Uint8Array(arrayBuffer) 5 | 6 | // Convert byte array to base64 string 7 | let binary = '' 8 | byteArray.forEach((byte) => { 9 | binary += String.fromCharCode(byte) 10 | }) 11 | 12 | // Apply base64 encoding 13 | return btoa(binary) 14 | } 15 | 16 | // Used for file related tool calls in case the llm sends a full resource URI 17 | export async function stripProtocolFromFilePath(path: string): Promise { 18 | return path.startsWith('file://') ? path.replace('file://', '') : path 19 | } 20 | -------------------------------------------------------------------------------- /apps/sandbox-container/shared/consts.ts: -------------------------------------------------------------------------------- 1 | // Set this to the open port on your container 2 | export const OPEN_CONTAINER_PORT = 8080 3 | -------------------------------------------------------------------------------- /apps/sandbox-container/shared/schema.ts: -------------------------------------------------------------------------------- 1 | import z from 'zod' 2 | 3 | export type ExecParams = z.infer 4 | export const ExecParams = z.object({ 5 | args: z.string(), 6 | timeout: z.number().optional().describe('Timeout in milliseconds'), 7 | streamStderr: z.boolean().default(true), 8 | }) 9 | 10 | export type FileWrite = z.infer 11 | export const FileWrite = z.object({ 12 | path: z.string(), 13 | text: z.string().describe('Full text content of the file you want to write.'), 14 | }) 15 | 16 | export type FilePathParam = z.infer 17 | export const FilePathParam = z.object({ 18 | path: z.string(), 19 | }) 20 | 21 | export type FileList = z.infer 22 | export const FileList = z.object({ 23 | resources: z 24 | .object({ 25 | uri: z.string(), 26 | name: z.string(), 27 | description: z.string().optional(), 28 | mimeType: z.string().optional(), 29 | }) 30 | .array(), 31 | }) 32 | 33 | export type FilesContextSchema = z.infer 34 | export const FilesContextSchema = z.object({ 35 | files: z 36 | .object({ 37 | uri: z.string(), 38 | }) 39 | .array(), 40 | }) 41 | -------------------------------------------------------------------------------- /apps/sandbox-container/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/workers.json", 3 | "include": ["*/**.ts", "./vitest.config.evals.ts", "./types.d.ts"], 4 | "exclude": ["container/**.ts"] 5 | } 6 | -------------------------------------------------------------------------------- /apps/sandbox-container/types.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'cloudflare:test' { 2 | interface ProvidedEnv { 3 | OPENAI_API_KEY: 'TODO' 4 | AI_GATEWAY_TOKEN: string 5 | CLOUDFLARE_ACCOUNT_ID: string 6 | AI_GATEWAY_ID: string 7 | AI: Ai 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /apps/sandbox-container/vitest.config.evals.ts: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' 2 | 3 | export default defineWorkersConfig({ 4 | test: { 5 | include: ['**/*.eval.?(c|m)[jt]s?(x)'], 6 | poolOptions: { 7 | workers: { 8 | isolatedStorage: true, 9 | wrangler: { configPath: './wrangler.jsonc' }, 10 | miniflare: { 11 | bindings: { 12 | ENVIRONMENT: 'test', 13 | }, 14 | }, 15 | }, 16 | }, 17 | }, 18 | }) 19 | -------------------------------------------------------------------------------- /apps/workers-bindings/.dev.vars.example: -------------------------------------------------------------------------------- 1 | CLOUDFLARE_CLIENT_ID= 2 | CLOUDFLARE_CLIENT_SECRET= 3 | DEV_DISABLE_OAUTH= 4 | DEV_CLOUDFLARE_API_TOKEN= 5 | DEV_CLOUDFLARE_EMAIL= -------------------------------------------------------------------------------- /apps/workers-bindings/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ['@repo/eslint-config/default.cjs'], 5 | } 6 | -------------------------------------------------------------------------------- /apps/workers-bindings/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # workers-bindings 2 | 3 | ## 0.0.3 4 | 5 | ### Patch Changes 6 | 7 | - 3677a18: Remove extraneous log 8 | - Updated dependencies [3677a18] 9 | - @repo/mcp-common@0.16.3 10 | 11 | ## 0.0.2 12 | 13 | ### Patch Changes 14 | 15 | - 86c2e4f: Add API token passthrough auth 16 | - Updated dependencies [86c2e4f] 17 | - @repo/mcp-common@0.16.2 18 | 19 | ## 0.0.1 20 | 21 | ### Patch Changes 22 | 23 | - cf3771b: chore: add suffixes to common files in apps and packages 24 | 25 | It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. 26 | 27 | - Updated dependencies [cf3771b] 28 | - @repo/mcp-common@0.16.1 29 | - @repo/eval-tools@0.31.1 30 | - @repo/mcp-observability@0.31.1 31 | -------------------------------------------------------------------------------- /apps/workers-bindings/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | If you'd like to iterate and test your MCP server, you can do so in local development. 4 | 5 | ## Local Development 6 | 7 | 1. Create a `.dev.vars` file in your project root: 8 | 9 | If you're a Cloudflare employee: 10 | 11 | ``` 12 | CLOUDFLARE_CLIENT_ID=your_development_cloudflare_client_id 13 | CLOUDFLARE_CLIENT_SECRET=your_development_cloudflare_client_secret 14 | ``` 15 | 16 | If you're an external contributor, you can provide a development API token: 17 | 18 | ``` 19 | DEV_DISABLE_OAUTH=true 20 | DEV_CLOUDFLARE_EMAIL=your_cloudflare_email 21 | # This is your global api token 22 | DEV_CLOUDFLARE_API_TOKEN=your_development_api_token 23 | ``` 24 | 25 | 2. Start the local development server: 26 | 27 | ```bash 28 | npx wrangler dev 29 | ``` 30 | 31 | 3. To test locally, open Inspector, and connect to `http://localhost:8976/sse`. 32 | Once you follow the prompts, you'll be able to "List Tools". You can also connect with any MCP client. 33 | 34 | ## Deploying the Worker ( Cloudflare employees only ) 35 | 36 | Set secrets via Wrangler: 37 | 38 | ```bash 39 | npx wrangler secret put CLOUDFLARE_CLIENT_ID -e 40 | npx wrangler secret put CLOUDFLARE_CLIENT_SECRET -e 41 | ``` 42 | 43 | ## Set up a KV namespace 44 | 45 | Create the KV namespace: 46 | 47 | ```bash 48 | npx wrangler kv namespace create "OAUTH_KV" 49 | ``` 50 | 51 | Then, update the Wrangler file with the generated KV namespace ID. 52 | 53 | ## Deploy & Test 54 | 55 | Deploy the MCP server to make it available on your workers.dev domain: 56 | 57 | ```bash 58 | npx wrangler deploy -e 59 | ``` 60 | 61 | Test the remote server using [Inspector](https://modelcontextprotocol.io/docs/tools/inspector): 62 | 63 | ```bash 64 | npx @modelcontextprotocol/inspector@latest 65 | ``` 66 | -------------------------------------------------------------------------------- /apps/workers-bindings/evals/accounts.eval.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'vitest' 2 | import { describeEval } from 'vitest-evals' 3 | 4 | import { runTask } from '@repo/eval-tools/src/runTask' 5 | import { checkFactuality } from '@repo/eval-tools/src/scorers' 6 | import { eachModel } from '@repo/eval-tools/src/test-models' 7 | 8 | import { initializeClient } from './utils' // Assuming utils.ts will exist here 9 | 10 | // Define a mock account ID for testing 11 | const MOCK_ACCOUNT_ID = 'mock-account-12345' 12 | 13 | eachModel('$modelName', ({ model }) => { 14 | describeEval('List Cloudflare Accounts', { 15 | data: async () => [ 16 | { 17 | input: 'List all my Cloudflare accounts.', 18 | expected: 'The accounts_list tool should be called to retrieve the list of accounts.', 19 | }, 20 | ], 21 | task: async (input: string) => { 22 | const client = await initializeClient() 23 | const { promptOutput, toolCalls } = await runTask(client, model, input) 24 | 25 | const toolCall = toolCalls.find((call) => call.toolName === 'accounts_list') 26 | expect(toolCall, 'Tool accounts_list was not called').toBeDefined() 27 | return promptOutput 28 | }, 29 | scorers: [checkFactuality], 30 | threshold: 1, 31 | timeout: 60000, // 60 seconds 32 | }) 33 | describeEval('Set Active Cloudflare Account', { 34 | data: async () => [ 35 | { 36 | input: `Set my active Cloudflare account to ${MOCK_ACCOUNT_ID}.`, 37 | expected: `The set_active_account tool should be called with the account ID ${MOCK_ACCOUNT_ID}.`, 38 | }, 39 | ], 40 | task: async (input: string) => { 41 | const client = await initializeClient() 42 | const { promptOutput, toolCalls } = await runTask(client, model, input) 43 | const toolCall = toolCalls.find((call) => call.toolName === 'set_active_account') 44 | expect(toolCall, 'Tool set_active_account was not called').toBeDefined() 45 | 46 | expect(toolCall?.args, 'Arguments for set_active_account did not match').toEqual( 47 | expect.objectContaining({ activeAccountIdParam: MOCK_ACCOUNT_ID }) 48 | ) 49 | return promptOutput 50 | }, 51 | scorers: [checkFactuality], 52 | threshold: 1, 53 | timeout: 60000, // 60 seconds 54 | }) 55 | }) 56 | -------------------------------------------------------------------------------- /apps/workers-bindings/evals/hyperdrive.eval.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'vitest' 2 | import { describeEval } from 'vitest-evals' 3 | 4 | import { runTask } from '@repo/eval-tools/src/runTask' 5 | import { checkFactuality } from '@repo/eval-tools/src/scorers' 6 | import { eachModel } from '@repo/eval-tools/src/test-models' 7 | import { HYPERDRIVE_TOOLS } from '@repo/mcp-common/src/tools/hyperdrive.tools' 8 | 9 | import { initializeClient } from './utils' // Assuming utils.ts will exist here 10 | 11 | // TODO: Add test for creating hyperdrive config with the following params once we can securely pass parameters to the tool. See: https://github.com/modelcontextprotocol/modelcontextprotocol/pull/382 12 | // const HYPERDRIVE_NAME = 'neon-test-hyperdrive' 13 | // const HYPERDRIVE_DATABASE = 'neondb' 14 | // const HYPERDRIVE_HOST = 'ep-late-cell-a4fm3g5p-pooler.us-east-1.aws.neon.tech' 15 | // const HYPERDRIVE_PORT = 5432 16 | // const HYPERDRIVE_USER = 'neondb_owner' 17 | // const HYPERDRIVE_PASSWORD = 'my-test-password' 18 | 19 | eachModel('$modelName', ({ model }) => { 20 | describeEval('Hyperdrive Tool Evaluations', { 21 | data: async () => [ 22 | { 23 | input: `List my hyperdrive configurations.`, 24 | expected: `The ${HYPERDRIVE_TOOLS.hyperdrive_configs_list} tool should be called to list my hyperdrive configurations.`, 25 | }, 26 | ], 27 | task: async (input: string) => { 28 | const client = await initializeClient(/* Pass necessary mocks/config */) 29 | const { promptOutput, toolCalls } = await runTask(client, model, input) 30 | 31 | const toolCall = toolCalls.find( 32 | (call) => call.toolName === HYPERDRIVE_TOOLS.hyperdrive_configs_list 33 | ) 34 | expect( 35 | toolCall, 36 | `Tool ${HYPERDRIVE_TOOLS.hyperdrive_configs_list} was not called` 37 | ).toBeDefined() 38 | 39 | return promptOutput 40 | }, 41 | scorers: [checkFactuality], 42 | threshold: 1, 43 | timeout: 60000, 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /apps/workers-bindings/evals/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { TestEnv } from '../vitest.config' 2 | 3 | declare module 'cloudflare:test' { 4 | interface ProvidedEnv extends TestEnv {} 5 | } 6 | -------------------------------------------------------------------------------- /apps/workers-bindings/evals/utils.ts: -------------------------------------------------------------------------------- 1 | import { MCPClientManager } from 'agents/mcp/client' 2 | 3 | export async function initializeClient(): Promise { 4 | const clientManager = new MCPClientManager('test-client', '0.0.0') 5 | await clientManager.connect('http://localhost:8977/sse') 6 | return clientManager 7 | } 8 | -------------------------------------------------------------------------------- /apps/workers-bindings/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workers-bindings", 3 | "version": "0.0.3", 4 | "private": true, 5 | "scripts": { 6 | "check:lint": "run-eslint-workers", 7 | "check:types": "run-tsc", 8 | "deploy": "wrangler deploy", 9 | "deploy:staging": "wrangler deploy --env staging", 10 | "deploy:production": "wrangler deploy --env production", 11 | "eval:dev": "start-server-and-test --expect 404 eval:server http://localhost:8977 'vitest --testTimeout=60000 --config vitest.config.evals.ts'", 12 | "eval:server": "wrangler dev --var ENVIRONMENT:test --var DEV_DISABLE_OAUTH:true --var DEV_CLOUDFLARE_EMAIL:mcp-server-eval-account@workers-for-platforms-dev.cfdata.org --inspector-port 9230 --port 8977", 13 | "eval:ci": "start-server-and-test --expect 404 eval:server http://localhost:8977 'vitest run --testTimeout=60000 --config vitest.config.evals.ts'", 14 | "dev": "wrangler dev", 15 | "start": "wrangler dev", 16 | "types": "wrangler types --include-env=false", 17 | "test": "vitest" 18 | }, 19 | "devDependencies": { 20 | "@cloudflare/vitest-pool-workers": "0.8.14", 21 | "@types/node": "22.14.1", 22 | "marked": "15.0.7", 23 | "typescript": "5.5.4", 24 | "vitest": "3.0.9", 25 | "wrangler": "4.10.0" 26 | }, 27 | "dependencies": { 28 | "@cloudflare/workers-oauth-provider": "0.0.5", 29 | "@modelcontextprotocol/sdk": "1.10.2", 30 | "@n8n/json-schema-to-zod": "1.1.0", 31 | "@repo/eval-tools": "workspace:*", 32 | "@repo/mcp-common": "workspace:*", 33 | "@repo/mcp-observability": "workspace:*", 34 | "agents": "0.0.67", 35 | "ai": "4.3.10", 36 | "concurrently": "9.1.2", 37 | "hono": "4.7.6", 38 | "start-server-and-test": "2.0.11", 39 | "vitest-evals": "0.1.4", 40 | "zod": "3.24.2" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /apps/workers-bindings/src/bindings.context.ts: -------------------------------------------------------------------------------- 1 | import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' 2 | import type { WorkersBindingsMCP } from './bindings.app' 3 | 4 | export interface Env { 5 | OAUTH_KV: KVNamespace 6 | ENVIRONMENT: 'development' | 'staging' | 'production' | 'test' 7 | MCP_SERVER_NAME: string 8 | MCP_SERVER_VERSION: string 9 | CLOUDFLARE_CLIENT_ID: string 10 | CLOUDFLARE_CLIENT_SECRET: string 11 | MCP_OBJECT: DurableObjectNamespace 12 | USER_DETAILS: DurableObjectNamespace 13 | MCP_METRICS: AnalyticsEngineDataset 14 | DEV_DISABLE_OAUTH: string 15 | DEV_CLOUDFLARE_API_TOKEN: string 16 | DEV_CLOUDFLARE_EMAIL: string 17 | CLOUDFLARE_API_TOKEN: string 18 | OPENAI_API_KEY: string 19 | AI_GATEWAY_TOKEN: string 20 | CLOUDFLARE_ACCOUNT_ID: string 21 | AI_GATEWAY_ID: string 22 | AI: Ai 23 | } 24 | -------------------------------------------------------------------------------- /apps/workers-bindings/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/workers.json", 3 | "include": ["*/**.ts", "./vitest.config.evals.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/workers-bindings/vitest.config.evals.ts: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' 2 | 3 | export default defineWorkersConfig({ 4 | test: { 5 | include: ['**/*.eval.?(c|m)[jt]s?(x)'], 6 | poolOptions: { 7 | workers: { 8 | isolatedStorage: true, 9 | wrangler: { configPath: './wrangler.jsonc' }, 10 | miniflare: { 11 | bindings: { 12 | ENVIRONMENT: 'test', 13 | }, 14 | }, 15 | }, 16 | }, 17 | }, 18 | }) 19 | -------------------------------------------------------------------------------- /apps/workers-bindings/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' 2 | 3 | import type { Env } from './src/bindings.context' 4 | 5 | export interface TestEnv extends Env { 6 | CLOUDFLARE_MOCK_ACCOUNT_ID: string 7 | CLOUDFLARE_MOCK_API_TOKEN: string 8 | } 9 | 10 | export default defineWorkersConfig({ 11 | test: { 12 | poolOptions: { 13 | workers: { 14 | wrangler: { configPath: `${__dirname}/wrangler.jsonc` }, 15 | miniflare: { 16 | bindings: { 17 | CLOUDFLARE_MOCK_ACCOUNT_ID: 'mock-account-id', 18 | CLOUDFLARE_MOCK_API_TOKEN: 'mock-api-token', 19 | } satisfies Partial, 20 | }, 21 | }, 22 | }, 23 | }, 24 | }) 25 | -------------------------------------------------------------------------------- /apps/workers-builds/.dev.vars.example: -------------------------------------------------------------------------------- 1 | CLOUDFLARE_CLIENT_ID= 2 | CLOUDFLARE_CLIENT_SECRET= 3 | # OR 4 | DEV_DISABLE_OAUTH= 5 | DEV_CLOUDFLARE_API_TOKEN= 6 | DEV_CLOUDFLARE_EMAIL= 7 | -------------------------------------------------------------------------------- /apps/workers-builds/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ['@repo/eslint-config/default.cjs'], 5 | } 6 | -------------------------------------------------------------------------------- /apps/workers-builds/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # workers-builds 2 | 3 | ## 0.0.4 4 | 5 | ### Patch Changes 6 | 7 | - 3677a18: Remove extraneous log 8 | - Updated dependencies [3677a18] 9 | - @repo/mcp-common@0.16.3 10 | 11 | ## 0.0.3 12 | 13 | ### Patch Changes 14 | 15 | - 86c2e4f: Add API token passthrough auth 16 | - Updated dependencies [86c2e4f] 17 | - @repo/mcp-common@0.16.2 18 | 19 | ## 0.0.2 20 | 21 | ### Patch Changes 22 | 23 | - cf3771b: chore: add suffixes to common files in apps and packages 24 | 25 | It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. 26 | 27 | - Updated dependencies [cf3771b] 28 | - @repo/mcp-common@0.16.1 29 | - @repo/mcp-observability@0.31.1 30 | -------------------------------------------------------------------------------- /apps/workers-builds/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | If you'd like to iterate and test your MCP server, you can do so in local development. 4 | 5 | ## Local Development 6 | 7 | 1. Create a `.dev.vars` file in your project root: 8 | 9 | If you're a Cloudflare employee: 10 | 11 | ``` 12 | CLOUDFLARE_CLIENT_ID=your_development_cloudflare_client_id 13 | CLOUDFLARE_CLIENT_SECRET=your_development_cloudflare_client_secret 14 | ``` 15 | 16 | If you're an external contributor, you can provide a development API token: 17 | 18 | ``` 19 | DEV_DISABLE_OAUTH=true 20 | DEV_CLOUDFLARE_EMAIL=your_cloudflare_email 21 | # This is your global api token 22 | DEV_CLOUDFLARE_API_TOKEN=your_development_api_token 23 | ``` 24 | 25 | 2. Start the local development server: 26 | 27 | ```bash 28 | pnpm dev 29 | ``` 30 | 31 | 3. To test locally, open Inspector, and connect to `http://localhost:8976/sse`. 32 | Once you follow the prompts, you'll be able to "List Tools". You can also connect with any MCP client. 33 | 34 | ## Deploying the Worker ( Cloudflare employees only ) 35 | 36 | Set secrets via Wrangler: 37 | 38 | ```bash 39 | npx wrangler secret put CLOUDFLARE_CLIENT_ID -e 40 | npx wrangler secret put CLOUDFLARE_CLIENT_SECRET -e 41 | ``` 42 | 43 | ## Set up a KV namespace 44 | 45 | Create the KV namespace: 46 | 47 | ```bash 48 | npx wrangler kv namespace create "OAUTH_KV" 49 | ``` 50 | 51 | Then, update the Wrangler file with the generated KV namespace ID. 52 | 53 | ## Deploy & Test 54 | 55 | Deploy the MCP server to make it available on your workers.dev domain: 56 | 57 | ```bash 58 | npx wrangler deploy -e 59 | ``` 60 | 61 | Test the remote server using [Inspector](https://modelcontextprotocol.io/docs/tools/inspector): 62 | 63 | ```bash 64 | npx @modelcontextprotocol/inspector@latest 65 | ``` 66 | -------------------------------------------------------------------------------- /apps/workers-builds/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workers-builds", 3 | "version": "0.0.4", 4 | "private": true, 5 | "scripts": { 6 | "check:lint": "run-eslint-workers", 7 | "check:types": "run-tsc", 8 | "deploy": "wrangler deploy", 9 | "dev": "vite dev", 10 | "start": "vite dev", 11 | "types": "wrangler types --include-env=false", 12 | "test": "vitest run" 13 | }, 14 | "dependencies": { 15 | "@cloudflare/workers-oauth-provider": "0.0.5", 16 | "@hono/zod-validator": "0.4.3", 17 | "@modelcontextprotocol/sdk": "1.10.2", 18 | "@repo/mcp-common": "workspace:*", 19 | "@repo/mcp-observability": "workspace:*", 20 | "agents": "0.0.67", 21 | "cloudflare": "4.2.0", 22 | "hono": "4.7.6", 23 | "zod": "3.24.2" 24 | }, 25 | "devDependencies": { 26 | "@cloudflare/vite-plugin": "1.1.0", 27 | "@cloudflare/vitest-pool-workers": "0.8.14", 28 | "@types/node": "22.14.1", 29 | "prettier": "3.5.3", 30 | "typescript": "5.5.4", 31 | "vite": "6.3.4", 32 | "vitest": "3.0.9", 33 | "wrangler": "4.10.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /apps/workers-builds/src/workers-builds.context.ts: -------------------------------------------------------------------------------- 1 | import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' 2 | import type { BuildsMCP } from './workers-builds.app' 3 | 4 | export interface Env { 5 | OAUTH_KV: KVNamespace 6 | ENVIRONMENT: 'development' | 'staging' | 'production' 7 | MCP_SERVER_NAME: string 8 | MCP_SERVER_VERSION: string 9 | CLOUDFLARE_CLIENT_ID: string 10 | CLOUDFLARE_CLIENT_SECRET: string 11 | MCP_OBJECT: DurableObjectNamespace 12 | USER_DETAILS: DurableObjectNamespace 13 | MCP_METRICS: AnalyticsEngineDataset 14 | SENTRY_ACCESS_CLIENT_ID: string 15 | SENTRY_ACCESS_CLIENT_SECRET: string 16 | GIT_HASH: string 17 | SENTRY_DSN: string 18 | DEV_DISABLE_OAUTH: string 19 | DEV_CLOUDFLARE_API_TOKEN: string 20 | DEV_CLOUDFLARE_EMAIL: string 21 | } 22 | -------------------------------------------------------------------------------- /apps/workers-builds/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/workers.json", 3 | "include": ["*/**.ts", "./vitest.config.ts", "./types.d.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/workers-builds/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { TestEnv } from './vitest.config' 2 | 3 | declare module 'cloudflare:test' { 4 | interface ProvidedEnv extends TestEnv {} 5 | } 6 | -------------------------------------------------------------------------------- /apps/workers-builds/vite.config.mts: -------------------------------------------------------------------------------- 1 | import { cloudflare } from '@cloudflare/vite-plugin' 2 | import { defineConfig } from 'vite' 3 | 4 | export default defineConfig({ 5 | plugins: [cloudflare()], 6 | server: { 7 | port: 8976, 8 | }, 9 | }) 10 | -------------------------------------------------------------------------------- /apps/workers-builds/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' 2 | 3 | import type { Env } from './src/workers-builds.context' 4 | 5 | export interface TestEnv extends Env { 6 | CLOUDFLARE_MOCK_ACCOUNT_ID: string 7 | CLOUDFLARE_MOCK_API_TOKEN: string 8 | } 9 | 10 | export default defineWorkersConfig({ 11 | test: { 12 | poolOptions: { 13 | workers: { 14 | wrangler: { configPath: `${__dirname}/wrangler.jsonc` }, 15 | miniflare: { 16 | bindings: { 17 | CLOUDFLARE_MOCK_ACCOUNT_ID: 'mock-account-id', 18 | CLOUDFLARE_MOCK_API_TOKEN: 'mock-api-token', 19 | } satisfies Partial, 20 | }, 21 | }, 22 | }, 23 | }, 24 | }) 25 | -------------------------------------------------------------------------------- /apps/workers-observability/.dev.vars.example: -------------------------------------------------------------------------------- 1 | CLOUDFLARE_CLIENT_ID= 2 | CLOUDFLARE_CLIENT_SECRET= 3 | DEV_DISABLE_OAUTH= 4 | DEV_CLOUDFLARE_API_TOKEN= 5 | DEV_CLOUDFLARE_EMAIL= -------------------------------------------------------------------------------- /apps/workers-observability/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ['@repo/eslint-config/default.cjs'], 5 | } 6 | -------------------------------------------------------------------------------- /apps/workers-observability/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # workers-observability 2 | 3 | ## 0.0.4 4 | 5 | ### Patch Changes 6 | 7 | - 3677a18: Remove extraneous log 8 | - Updated dependencies [3677a18] 9 | - @repo/mcp-common@0.16.3 10 | 11 | ## 0.0.3 12 | 13 | ### Patch Changes 14 | 15 | - 86c2e4f: Add API token passthrough auth 16 | - Updated dependencies [86c2e4f] 17 | - @repo/mcp-common@0.16.2 18 | 19 | ## 0.0.2 20 | 21 | ### Patch Changes 22 | 23 | - b190e97: fix: set correct entrypoint in wrangler.jsonc 24 | - cf3771b: chore: add suffixes to common files in apps and packages 25 | 26 | It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. 27 | 28 | - Updated dependencies [cf3771b] 29 | - @repo/mcp-common@0.16.1 30 | - @repo/mcp-observability@0.31.1 31 | -------------------------------------------------------------------------------- /apps/workers-observability/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | If you'd like to iterate and test your MCP server, you can do so in local development. 4 | 5 | ## Local Development 6 | 7 | 1. Create a `.dev.vars` file in your project root: 8 | 9 | If you're a Cloudflare employee: 10 | 11 | ``` 12 | CLOUDFLARE_CLIENT_ID=your_development_cloudflare_client_id 13 | CLOUDFLARE_CLIENT_SECRET=your_development_cloudflare_client_secret 14 | ``` 15 | 16 | If you're an external contributor, you can provide a development API token: 17 | 18 | ``` 19 | DEV_DISABLE_OAUTH=true 20 | DEV_CLOUDFLARE_EMAIL=your_cloudflare_email 21 | # This is your global api token 22 | DEV_CLOUDFLARE_API_TOKEN=your_development_api_token 23 | ``` 24 | 25 | 2. Start the local development server: 26 | 27 | ```bash 28 | npx wrangler dev 29 | ``` 30 | 31 | 3. To test locally, open Inspector, and connect to `http://localhost:8976/sse`. 32 | Once you follow the prompts, you'll be able to "List Tools". You can also connect with any MCP client. 33 | 34 | ## Deploying the Worker ( Cloudflare employees only ) 35 | 36 | Set secrets via Wrangler: 37 | 38 | ```bash 39 | npx wrangler secret put CLOUDFLARE_CLIENT_ID -e 40 | npx wrangler secret put CLOUDFLARE_CLIENT_SECRET -e 41 | ``` 42 | 43 | ## Set up a KV namespace 44 | 45 | Create the KV namespace: 46 | 47 | ```bash 48 | npx wrangler kv namespace create "OAUTH_KV" 49 | ``` 50 | 51 | Then, update the Wrangler file with the generated KV namespace ID. 52 | 53 | ## Deploy & Test 54 | 55 | Deploy the MCP server to make it available on your workers.dev domain: 56 | 57 | ```bash 58 | npx wrangler deploy -e 59 | ``` 60 | 61 | Test the remote server using [Inspector](https://modelcontextprotocol.io/docs/tools/inspector): 62 | 63 | ```bash 64 | npx @modelcontextprotocol/inspector@latest 65 | ``` 66 | -------------------------------------------------------------------------------- /apps/workers-observability/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workers-observability", 3 | "version": "0.0.4", 4 | "private": true, 5 | "scripts": { 6 | "check:lint": "run-eslint-workers", 7 | "check:types": "run-tsc", 8 | "deploy": "wrangler deploy", 9 | "dev": "wrangler dev", 10 | "start": "wrangler dev", 11 | "types": "wrangler types --include-env=false", 12 | "test": "vitest run" 13 | }, 14 | "dependencies": { 15 | "@cloudflare/workers-oauth-provider": "0.0.5", 16 | "@fast-csv/format": "5.0.2", 17 | "@hono/zod-validator": "0.4.3", 18 | "@modelcontextprotocol/sdk": "1.10.2", 19 | "@repo/mcp-common": "workspace:*", 20 | "@repo/mcp-observability": "workspace:*", 21 | "agents": "0.0.67", 22 | "cloudflare": "4.2.0", 23 | "hono": "4.7.6", 24 | "zod": "3.24.2" 25 | }, 26 | "devDependencies": { 27 | "@cloudflare/vitest-pool-workers": "0.8.14", 28 | "@types/node": "22.14.1", 29 | "prettier": "3.5.3", 30 | "typescript": "5.5.4", 31 | "vitest": "3.0.9", 32 | "wrangler": "4.10.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /apps/workers-observability/src/workers-observability.context.ts: -------------------------------------------------------------------------------- 1 | import type { UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do' 2 | import type { ObservabilityMCP } from './workers-observability.app' 3 | 4 | export interface Env { 5 | OAUTH_KV: KVNamespace 6 | ENVIRONMENT: 'development' | 'staging' | 'production' 7 | MCP_SERVER_NAME: string 8 | MCP_SERVER_VERSION: string 9 | CLOUDFLARE_CLIENT_ID: string 10 | CLOUDFLARE_CLIENT_SECRET: string 11 | MCP_OBJECT: DurableObjectNamespace 12 | USER_DETAILS: DurableObjectNamespace 13 | MCP_METRICS: AnalyticsEngineDataset 14 | SENTRY_ACCESS_CLIENT_ID: string 15 | SENTRY_ACCESS_CLIENT_SECRET: string 16 | GIT_HASH: string 17 | SENTRY_DSN: string 18 | DEV_DISABLE_OAUTH: string 19 | DEV_CLOUDFLARE_API_TOKEN: string 20 | DEV_CLOUDFLARE_EMAIL: string 21 | } 22 | -------------------------------------------------------------------------------- /apps/workers-observability/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/workers.json", 3 | "include": ["*/**.ts", "./vitest.config.ts", "./types.d.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/workers-observability/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { TestEnv } from './vitest.config' 2 | 3 | declare module 'cloudflare:test' { 4 | interface ProvidedEnv extends TestEnv {} 5 | } 6 | -------------------------------------------------------------------------------- /apps/workers-observability/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config' 2 | 3 | import type { Env } from './src/workers-observability.context' 4 | 5 | export interface TestEnv extends Env { 6 | CLOUDFLARE_MOCK_ACCOUNT_ID: string 7 | CLOUDFLARE_MOCK_API_TOKEN: string 8 | } 9 | 10 | export default defineWorkersConfig({ 11 | test: { 12 | poolOptions: { 13 | workers: { 14 | wrangler: { configPath: `${__dirname}/wrangler.jsonc` }, 15 | miniflare: { 16 | bindings: { 17 | CLOUDFLARE_MOCK_ACCOUNT_ID: 'mock-account-id', 18 | CLOUDFLARE_MOCK_API_TOKEN: 'mock-api-token', 19 | } satisfies Partial, 20 | }, 21 | }, 22 | }, 23 | }, 24 | }) 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@cloudflare/mcp-server-cloudflare", 3 | "version": "1.0.0", 4 | "description": "Monorepo for Cloudflare MCP servers", 5 | "license": "Apache-2.0", 6 | "author": "Cloudflare, Inc. (https://cloudflare.com)", 7 | "homepage": "https://github.com/cloudflare/mcp-server-cloudflare", 8 | "bugs": "https://github.com/cloudflare/mcp-server-cloudflare/issues", 9 | "type": "module", 10 | "sideEffects": false, 11 | "scripts": { 12 | "changeset:new": "run-changeset-new", 13 | "check:deps": "syncpack lint", 14 | "check:format": "prettier . --check --cache --ignore-unknown", 15 | "check:turbo": "run-turbo check", 16 | "types": "run-turbo types", 17 | "test:ci": "run-vitest-ci", 18 | "test": "vitest run --passWithNoTests", 19 | "fix:format": "prettier . --write --cache --ignore-unknown", 20 | "fix:deps": "run-fix-deps", 21 | "test:watch": "vitest", 22 | "eval:ci": "run-turbo eval:ci", 23 | "eval:dev": "run-turbo eval:dev", 24 | "update-deps": "syncpack update" 25 | }, 26 | "devDependencies": { 27 | "@changesets/cli": "2.28.1", 28 | "@cloudflare/vitest-pool-workers": "0.8.14", 29 | "@ianvs/prettier-plugin-sort-imports": "4.4.1", 30 | "@repo/eslint-config": "workspace:*", 31 | "@repo/tools": "workspace:*", 32 | "@repo/typescript-config": "workspace:*", 33 | "@vitest/ui": "3.0.9", 34 | "prettier": "3.5.3", 35 | "syncpack": "13.0.3", 36 | "turbo": "2.5.0", 37 | "typescript": "5.5.4", 38 | "vitest": "3.0.9" 39 | }, 40 | "packageManager": "pnpm@10.8.0", 41 | "pnpm": { 42 | "onlyBuiltDependencies": [ 43 | "esbuild", 44 | "sharp", 45 | "workerd" 46 | ], 47 | "overrides": { 48 | "esbuild": "0.25.1" 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/eslint-config/README.md: -------------------------------------------------------------------------------- 1 | # `@repo/eslint-config` 2 | 3 | Collection of internal eslint configurations. 4 | -------------------------------------------------------------------------------- /packages/eslint-config/default.cjs: -------------------------------------------------------------------------------- 1 | const { resolve } = require('node:path') 2 | 3 | const project = resolve(process.cwd(), 'tsconfig.json') 4 | 5 | /** @type {import("eslint").Linter.Config} */ 6 | module.exports = { 7 | ignorePatterns: ['.*.{js,cjs}', '**/node_modules/**', '**/dist/**', '**/dist2/**'], 8 | plugins: ['@typescript-eslint', 'import', 'unused-imports'], 9 | extends: ['turbo'], 10 | settings: { 11 | 'import/resolver': { 12 | typescript: { 13 | project, 14 | }, 15 | }, 16 | }, 17 | overrides: [ 18 | // TypeScript 19 | { 20 | // enable the rule specifically for TypeScript files 21 | files: ['**/*.{ts,tsx,mjs}'], 22 | parser: '@typescript-eslint/parser', 23 | parserOptions: { 24 | ecmaVersion: 2024, 25 | sourceType: 'module', 26 | project: true, 27 | }, 28 | extends: [ 29 | 'eslint:recommended', 30 | 'plugin:@typescript-eslint/recommended', 31 | 'plugin:import/typescript', 32 | 'turbo', 33 | 'prettier', // disable rules that conflict with prettier 34 | ], 35 | rules: { 36 | '@typescript-eslint/consistent-type-imports': ['warn', { prefer: 'type-imports' }], 37 | '@typescript-eslint/explicit-function-return-type': 'off', 38 | 'react-hooks/rules-of-hooks': 'off', 39 | '@typescript-eslint/ban-ts-comment': 'off', 40 | '@typescript-eslint/no-floating-promises': 'warn', 41 | 'unused-imports/no-unused-imports': 'warn', 42 | '@typescript-eslint/array-type': ['warn', { default: 'array-simple' }], 43 | '@typescript-eslint/no-unused-vars': [ 44 | 'warn', 45 | { 46 | argsIgnorePattern: '^_', 47 | varsIgnorePattern: '^_', 48 | caughtErrorsIgnorePattern: '^_', 49 | }, 50 | ], 51 | '@typescript-eslint/no-explicit-any': 'off', 52 | 'prefer-const': 'warn', 53 | 'no-mixed-spaces-and-tabs': ['error', 'smart-tabs'], 54 | 'no-empty': 'warn', 55 | }, 56 | }, 57 | 58 | // Node 59 | { 60 | files: ['.eslintrc.cjs'], 61 | env: { 62 | node: true, 63 | }, 64 | }, 65 | ], 66 | } 67 | -------------------------------------------------------------------------------- /packages/eslint-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/eslint-config", 3 | "version": "0.1.35", 4 | "private": true, 5 | "sideEffects": false, 6 | "files": [ 7 | "remix.cjs", 8 | "default.cjs" 9 | ], 10 | "devDependencies": { 11 | "@types/eslint": "8.56.10", 12 | "@types/node": "22.14.1", 13 | "@typescript-eslint/eslint-plugin": "7.18.0", 14 | "@typescript-eslint/parser": "7.18.0", 15 | "eslint": "8.57.0", 16 | "eslint-config-prettier": "9.1.0", 17 | "eslint-config-turbo": "2.4.2", 18 | "eslint-import-resolver-typescript": "3.8.3", 19 | "eslint-plugin-import": "2.31.0", 20 | "eslint-plugin-only-warn": "1.1.0", 21 | "eslint-plugin-unused-imports": "3.2.0", 22 | "typescript": "5.5.4", 23 | "vitest": "3.0.9" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/eval-tools/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/eval-tools", 3 | "version": "0.31.1", 4 | "private": true, 5 | "sideEffects": false, 6 | "type": "module", 7 | "scripts": { 8 | "types": "wrangler types --include-env=false" 9 | }, 10 | "directories": { 11 | "bin": "bin" 12 | }, 13 | "dependencies": { 14 | "@ai-sdk/anthropic": "1.2.11", 15 | "@ai-sdk/google": "1.2.17", 16 | "@ai-sdk/openai": "1.3.20", 17 | "@cloudflare/vitest-pool-workers": "0.8.14", 18 | "agents": "0.0.67", 19 | "ai": "4.3.10", 20 | "ai-gateway-provider": "0.0.6", 21 | "workers-ai-provider": "0.3.0", 22 | "wrangler": "4.10.0", 23 | "zod": "3.24.2" 24 | }, 25 | "devDependencies": { 26 | "@repo/typescript-config": "workspace:*", 27 | "@types/node": "22.14.1", 28 | "vitest-evals": "0.1.4" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/eval-tools/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/workers.json", 3 | "include": ["**/*.ts", "./worker-configuration.d.ts"], 4 | "exclude": ["node_modules", "tsconfig.json"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/eval-tools/wrangler.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stub-worker", 3 | "compatibility_date": "2025-04-14", 4 | "vars": { 5 | "OPENAI_API_KEY": "TODO", 6 | "AI_GATEWAY_TOKEN": "TODO", 7 | "CLOUDFLARE_ACCOUNT_ID": "TODO", 8 | "AI_GATEWAY_ID": "TODO" 9 | }, 10 | "ai": { 11 | "binding": "AI" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/mcp-common/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ['@repo/eslint-config/default.cjs'], 5 | } 6 | -------------------------------------------------------------------------------- /packages/mcp-common/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @repo/mcp-common 2 | 3 | ## 0.16.3 4 | 5 | ### Patch Changes 6 | 7 | - 3677a18: Remove extraneous log 8 | 9 | ## 0.16.2 10 | 11 | ### Patch Changes 12 | 13 | - 86c2e4f: Add API token passthrough auth 14 | 15 | ## 0.16.1 16 | 17 | ### Patch Changes 18 | 19 | - cf3771b: chore: add suffixes to common files in apps and packages 20 | 21 | It can be confusing switching between 16 files named 'index.ts', or 3 files named workers.ts. This change renames common files to have suffixes such as .types.ts, .api.ts, etc. to make it easier to work across files in the monorepo. 22 | 23 | - @repo/mcp-observability@0.31.1 24 | -------------------------------------------------------------------------------- /packages/mcp-common/README.md: -------------------------------------------------------------------------------- 1 | # MCP Common 2 | 3 | A place for common things used across MCP packages/apps 4 | -------------------------------------------------------------------------------- /packages/mcp-common/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/mcp-common", 3 | "version": "0.16.3", 4 | "private": true, 5 | "sideEffects": false, 6 | "main": "./src/index.ts", 7 | "scripts": { 8 | "check:lint": "run-eslint-workers", 9 | "check:types": "run-tsc", 10 | "test": "vitest run", 11 | "test:coverage": "run-vitest-coverage" 12 | }, 13 | "dependencies": { 14 | "@cloudflare/workers-oauth-provider": "0.0.5", 15 | "@fast-csv/format": "5.0.2", 16 | "@hono/zod-validator": "0.4.3", 17 | "@modelcontextprotocol/sdk": "1.10.2", 18 | "@repo/mcp-observability": "workspace:*", 19 | "agents": "0.0.67", 20 | "cloudflare": "4.2.0", 21 | "hono": "4.7.6", 22 | "toucan-js": "4.1.1", 23 | "zod": "3.24.2" 24 | }, 25 | "devDependencies": { 26 | "@cloudflare/vitest-pool-workers": "0.8.14", 27 | "@repo/eslint-config": "workspace:*", 28 | "@repo/tools": "workspace:*", 29 | "@repo/typescript-config": "workspace:*", 30 | "@sentry/types": "8.9.2", 31 | "@types/node": "22.14.1", 32 | "@vitest/ui": "3.0.9", 33 | "vitest": "3.0.9", 34 | "wrangler": "4.10.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/mcp-common/src/api-handler.ts: -------------------------------------------------------------------------------- 1 | import type { McpAgent } from 'agents/mcp' 2 | 3 | // Support both SSE and Streamable HTTP 4 | export function createApiHandler< 5 | T extends typeof McpAgent>, 6 | >(agent: T, opts?: { binding?: string }) { 7 | return { 8 | fetch: (req: Request, env: unknown, ctx: ExecutionContext) => { 9 | const url = new URL(req.url) 10 | if (url.pathname === '/sse' || url.pathname === '/sse/message') { 11 | return agent.serveSSE('/sse', { binding: opts?.binding }).fetch(req, env, ctx) 12 | } 13 | if (url.pathname === '/mcp') { 14 | return agent.serve('/mcp', { binding: opts?.binding }).fetch(req, env, ctx) 15 | } 16 | return new Response('Not found', { status: 404 }) 17 | }, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/mcp-common/src/api-token-mode.ts: -------------------------------------------------------------------------------- 1 | import { getUserAndAccounts } from './cloudflare-oauth-handler' 2 | 3 | import type { McpAgent } from 'agents/mcp' 4 | import type { AuthProps } from './cloudflare-oauth-handler' 5 | 6 | interface RequiredEnv { 7 | DEV_CLOUDFLARE_API_TOKEN: string 8 | DEV_CLOUDFLARE_EMAIL: string 9 | DEV_DISABLE_OAUTH: string 10 | } 11 | 12 | export async function isApiTokenRequest(req: Request, env: RequiredEnv) { 13 | // shortcircuit for dev 14 | if (env.DEV_CLOUDFLARE_API_TOKEN && env.DEV_DISABLE_OAUTH === 'true') { 15 | return true 16 | } 17 | 18 | const authHeader = req.headers.get('Authorization') 19 | if (!authHeader) return false 20 | 21 | const [type, token] = authHeader.split(' ') 22 | if (type !== 'Bearer') return false 23 | 24 | // Return true only if the token was issued by the OAuthProvider. 25 | // A token provisioned by the OAuthProvider has 3 parts, split by colons. 26 | const codeParts = token.split(':') 27 | return codeParts.length !== 3 28 | } 29 | 30 | export async function handleApiTokenMode< 31 | T extends typeof McpAgent>, 32 | >(agent: T, req: Request, env: RequiredEnv, ctx: ExecutionContext) { 33 | // Handle global API token case 34 | let opts, token 35 | // dev mode 36 | if ( 37 | env.DEV_CLOUDFLARE_API_TOKEN && 38 | env.DEV_CLOUDFLARE_EMAIL && 39 | env.DEV_DISABLE_OAUTH === 'true' 40 | ) { 41 | opts = { 42 | 'X-Auth-Key': env.DEV_CLOUDFLARE_API_TOKEN, 43 | 'X-Auth-Email': env.DEV_CLOUDFLARE_EMAIL, 44 | } 45 | token = env.DEV_CLOUDFLARE_API_TOKEN 46 | // header mode 47 | } else { 48 | const authHeader = req.headers.get('Authorization') 49 | if (!authHeader) { 50 | throw new Error('Authorization header is required') 51 | } 52 | 53 | const [type, tokenStr] = authHeader.split(' ') 54 | if (type !== 'Bearer') { 55 | throw new Error('Invalid authorization type, must be Bearer') 56 | } 57 | token = tokenStr 58 | } 59 | 60 | const { user, accounts } = await getUserAndAccounts(token, opts) 61 | ctx.props = { 62 | accessToken: token, 63 | user, 64 | accounts, 65 | } as AuthProps 66 | return agent.mount('/sse').fetch(req, env, ctx) 67 | } 68 | -------------------------------------------------------------------------------- /packages/mcp-common/src/api/account.api.ts: -------------------------------------------------------------------------------- 1 | import type { Cloudflare } from 'cloudflare' 2 | import type { Account } from 'cloudflare/resources/accounts/accounts.mjs' 3 | import type { CloudflareMcpAgent } from '../types/cloudflare-mcp-agent.types' 4 | import type { ToolHandler } from '../types/tools.types' 5 | 6 | export async function handleAccountsList({ client }: { client: Cloudflare }): Promise { 7 | // Currently limited to 50 accounts 8 | const response = await client.accounts.list({ query: { per_page: 50 } }) 9 | return response.result 10 | } 11 | 12 | export const withAccountCheck = >( 13 | agent: CloudflareMcpAgent, 14 | handler: ToolHandler 15 | ) => { 16 | return async (params: T) => { 17 | const accountId = await agent.getActiveAccountId() 18 | if (!accountId) { 19 | return { 20 | content: [ 21 | { 22 | type: 'text' as const, 23 | text: 'No currently active accountId. Try listing your accounts (accounts_list) and then setting an active account (set_active_account)', 24 | }, 25 | ], 26 | } 27 | } 28 | 29 | try { 30 | const result = await handler({ 31 | ...params, 32 | accountId, 33 | apiToken: agent.props.accessToken || '', 34 | }) 35 | return { 36 | content: [{ type: 'text' as const, text: JSON.stringify(result) }], 37 | } 38 | } catch (error) { 39 | return { 40 | content: [ 41 | { 42 | type: 'text' as const, 43 | text: JSON.stringify({ 44 | error: `Error processing request: ${error instanceof Error ? error.message : 'Unknown error'}`, 45 | }), 46 | }, 47 | ], 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/mcp-common/src/api/workers-builds.api.ts: -------------------------------------------------------------------------------- 1 | import { fetchCloudflareApi } from '../cloudflare-api' 2 | import { 3 | GetBuildLogsResult, 4 | GetBuildResult, 5 | ListBuildsByScriptResult, 6 | ListBuildsByScriptResultInfo, 7 | } from '../types/workers-builds.types' 8 | import { V4Schema } from '../v4-api' 9 | 10 | import type { LogLine } from '../types/workers-builds.types' 11 | 12 | export async function listBuilds({ 13 | accountId, 14 | workerId, 15 | page = 1, 16 | perPage = 10, 17 | apiToken, 18 | }: { 19 | accountId: string 20 | workerId: string 21 | page?: number 22 | perPage?: number 23 | apiToken: string 24 | }) { 25 | return fetchCloudflareApi({ 26 | endpoint: `/builds/workers/${workerId}/builds?page=${page}&per_page=${perPage}`, 27 | accountId, 28 | apiToken, 29 | responseSchema: V4Schema(ListBuildsByScriptResult, ListBuildsByScriptResultInfo), 30 | }) 31 | } 32 | 33 | export async function getBuild({ 34 | accountId, 35 | buildUUID, 36 | apiToken, 37 | }: { 38 | accountId: string 39 | buildUUID: string 40 | apiToken: string 41 | }) { 42 | return fetchCloudflareApi({ 43 | endpoint: `/builds/builds/${buildUUID}`, 44 | accountId, 45 | apiToken, 46 | responseSchema: V4Schema(GetBuildResult), 47 | }) 48 | } 49 | 50 | export async function getBuildLogs({ 51 | accountId, 52 | buildUUID, 53 | apiToken, 54 | }: { 55 | accountId: string 56 | buildUUID: string 57 | apiToken: string 58 | }) { 59 | const allLogs: LogLine[] = [] 60 | let cursor: string | undefined = undefined 61 | let hasMore = true 62 | 63 | while (hasMore) { 64 | let endpoint = `/builds/builds/${buildUUID}/logs` 65 | if (cursor) { 66 | endpoint += `?cursor=${cursor}` 67 | } 68 | 69 | const res = await fetchCloudflareApi({ 70 | endpoint, 71 | accountId, 72 | apiToken, 73 | responseSchema: V4Schema(GetBuildLogsResult), 74 | }) 75 | 76 | if (res.result) { 77 | allLogs.push(...res.result.lines) 78 | 79 | if (res.result.cursor && res.result.truncated) { 80 | cursor = res.result.cursor 81 | } else { 82 | hasMore = false 83 | } 84 | } 85 | } 86 | 87 | return allLogs 88 | } 89 | -------------------------------------------------------------------------------- /packages/mcp-common/src/api/workers.api.ts: -------------------------------------------------------------------------------- 1 | import { fetchCloudflareApi } from '../cloudflare-api' 2 | import { WorkersService } from '../types/workers.types' 3 | import { V4Schema } from '../v4-api' 4 | 5 | import type { Cloudflare } from 'cloudflare' 6 | 7 | /** 8 | * Fetches list of workers from Cloudflare API 9 | * @param client Cloudflare API Client 10 | * @param accountId Cloudflare account ID 11 | * @returns List of workers 12 | */ 13 | export async function handleWorkersList({ 14 | client, 15 | accountId, 16 | }: { 17 | client: Cloudflare 18 | accountId: string 19 | }): Promise { 20 | return (await client.workers.scripts.list({ account_id: accountId })).result 21 | } 22 | 23 | /** 24 | * Get details of a worker script from Cloudflare API 25 | * @param client Cloudflare API Client 26 | * @param scriptName Name of the worker script to download 27 | * @param accountId Cloudflare account ID 28 | * @returns The script name and id 29 | */ 30 | export async function handleGetWorkersService({ 31 | apiToken, 32 | scriptName, 33 | accountId, 34 | }: { 35 | apiToken: string 36 | scriptName: string 37 | accountId: string 38 | }) { 39 | return await fetchCloudflareApi({ 40 | endpoint: `/workers/services/${scriptName}`, 41 | accountId, 42 | apiToken, 43 | responseSchema: V4Schema(WorkersService), 44 | }) 45 | } 46 | 47 | /** 48 | * Downloads a specific worker script from Cloudflare API 49 | * @param client Cloudflare API Client 50 | * @param scriptName Name of the worker script to download 51 | * @param accountId Cloudflare account ID 52 | * @returns The worker script content 53 | */ 54 | export async function handleWorkerScriptDownload({ 55 | client, 56 | scriptName, 57 | accountId, 58 | }: { 59 | client: Cloudflare 60 | scriptName: string 61 | accountId: string 62 | }): Promise { 63 | return await client.workers.scripts.get(scriptName, { account_id: accountId }) 64 | } 65 | -------------------------------------------------------------------------------- /packages/mcp-common/src/api/zone.api.ts: -------------------------------------------------------------------------------- 1 | import type { Cloudflare } from 'cloudflare' 2 | 3 | export interface ZoneListParams { 4 | client: Cloudflare 5 | accountId: string 6 | page?: number 7 | perPage?: number 8 | direction?: 'asc' | 'desc' 9 | match?: 'any' | 'all' 10 | name?: string 11 | status?: string 12 | order?: string 13 | } 14 | 15 | /** 16 | * Lists zones under a Cloudflare account 17 | * @see https://developers.cloudflare.com/api/resources/zones/methods/list/ 18 | */ 19 | export async function handleZonesList({ 20 | client, 21 | accountId, 22 | page = 1, 23 | perPage = 50, 24 | direction = 'desc', 25 | match = 'all', 26 | name, 27 | status, 28 | order = 'name', 29 | }: ZoneListParams) { 30 | // Build query parameters 31 | const query: Record = { 32 | page, 33 | per_page: perPage, 34 | direction, 35 | match, 36 | account_id: accountId, 37 | } 38 | 39 | // Only add these parameters if they're defined and not empty strings 40 | if (name) { 41 | query.name = name 42 | } 43 | 44 | if (status) { 45 | query.status = status 46 | } 47 | 48 | if (order) { 49 | query.order = order 50 | } 51 | 52 | try { 53 | // Use the zones.list method from the Cloudflare client 54 | const response = await client.zones.list({ query }) 55 | return response.result 56 | } catch (error) { 57 | throw new Error( 58 | `Failed to list zones: ${error instanceof Error ? error.message : String(error)}` 59 | ) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/mcp-common/src/config.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export type MCPEnvironment = z.infer 4 | export const MCPEnvironment = z.enum(['VITEST', 'development', 'staging', 'production']) 5 | 6 | export function getEnvironment(environment: string) { 7 | return MCPEnvironment.parse(environment) 8 | } 9 | -------------------------------------------------------------------------------- /packages/mcp-common/src/constants.ts: -------------------------------------------------------------------------------- 1 | import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js' 2 | 3 | export const MISSING_ACCOUNT_ID_RESPONSE = { 4 | content: [ 5 | { 6 | type: 'text', 7 | text: 'No currently active accountId. Try listing your accounts (accounts_list) and then setting an active account (set_active_account)', 8 | }, 9 | ], 10 | } satisfies CallToolResult 11 | -------------------------------------------------------------------------------- /packages/mcp-common/src/durable-objects/user_details.do.ts: -------------------------------------------------------------------------------- 1 | import { DurableObject } from 'cloudflare:workers' 2 | import { z } from 'zod' 3 | 4 | import { DurableKVStore } from '../durable-kv-store' 5 | 6 | import type { DurableKVStorageKeys } from '../durable-kv-store' 7 | 8 | // Durable Object for persisting UserDetails in DO storage across sessions based off the userId 9 | export class UserDetails extends DurableObject { 10 | private readonly kv: DurableKVStore 11 | constructor(state: DurableObjectState, env: unknown) { 12 | super(state, env) 13 | this.env = env 14 | this.kv = new DurableKVStore({ 15 | state, 16 | prefix: 'meta', 17 | keys: UserDetailsKeys, 18 | }) 19 | } 20 | 21 | public async getActiveAccountId() { 22 | return await this.kv.get('active_account_id') 23 | } 24 | 25 | public async setActiveAccountId(activeAccountId: string) { 26 | this.kv.put('active_account_id', activeAccountId) 27 | } 28 | } 29 | 30 | /** 31 | * Storage keys used by UserDetails 32 | */ 33 | type UserDetailsKeys = typeof UserDetailsKeys 34 | const UserDetailsKeys = { 35 | active_account_id: z.string(), 36 | } as const satisfies DurableKVStorageKeys 37 | 38 | /** Get the UserDetails instance */ 39 | export function getUserDetails( 40 | env: { USER_DETAILS: DurableObjectNamespace }, 41 | user_id: string 42 | ): DurableObjectStub { 43 | const id = env.USER_DETAILS.idFromName(user_id) 44 | return env.USER_DETAILS.get(id) 45 | } 46 | -------------------------------------------------------------------------------- /packages/mcp-common/src/env.ts: -------------------------------------------------------------------------------- 1 | import { env } from 'cloudflare:workers' 2 | 3 | // Helper to cast env as any generic Env type 4 | export function getEnv() { 5 | return env as Env 6 | } 7 | -------------------------------------------------------------------------------- /packages/mcp-common/src/format.ts: -------------------------------------------------------------------------------- 1 | import { writeToString } from '@fast-csv/format' 2 | 3 | /** 4 | * A collection of formatting functions (think of it like Golang's `fmt` package) 5 | */ 6 | export const fmt = { 7 | /** 8 | * Trims all lines of a string. 9 | * Useful for formatting tool instructions. 10 | */ 11 | trim: (str: string): string => 12 | str 13 | .trim() 14 | .split('\n') 15 | .map((line) => line.trim()) 16 | .join('\n'), 17 | 18 | /** 19 | * Converts a multi-line string into a single line. 20 | * Useful for formatting tool instructions. 21 | */ 22 | oneLine: (str: string): string => 23 | str 24 | .trim() 25 | .split('\n') 26 | .map((line) => line.trim()) 27 | .filter((line) => line.length > 0) 28 | .join(' '), 29 | 30 | /** 31 | * Convert an array of objects to a string of tab-separated values (TSV). 32 | * This is better than JSON for returning data to the model because it uses fewer tokens 33 | */ 34 | asTSV: (data: any[]): Promise => writeToString(data, { headers: true, delimiter: '\t' }), 35 | } as const 36 | -------------------------------------------------------------------------------- /packages/mcp-common/src/mcp-error.ts: -------------------------------------------------------------------------------- 1 | import type { ContentfulStatusCode } from 'hono/utils/http-status' 2 | 3 | export class McpError extends Error { 4 | public code: ContentfulStatusCode 5 | public reportToSentry: boolean 6 | // error message for internal use 7 | public internalMessage?: string 8 | public cause?: Error 9 | constructor( 10 | message: string, 11 | code: ContentfulStatusCode, 12 | opts: { 13 | reportToSentry: boolean 14 | internalMessage?: string 15 | cause?: Error 16 | } = { reportToSentry: false } 17 | ) { 18 | super(message) 19 | this.code = code 20 | this.name = 'MCPError' 21 | this.reportToSentry = opts.reportToSentry 22 | this.internalMessage = opts.internalMessage 23 | this.cause = opts.cause 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/mcp-common/src/poll.ts: -------------------------------------------------------------------------------- 1 | type PollOptions = { 2 | taskFn: () => Promise 3 | checkFn?: (result: T) => boolean 4 | intervalSeconds?: number 5 | maxWaitSeconds?: number 6 | onError?: (error: unknown) => void 7 | } 8 | 9 | const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) 10 | 11 | export async function pollUntilReady({ 12 | taskFn, 13 | checkFn = (result: T) => Boolean(result), 14 | intervalSeconds = 5, 15 | maxWaitSeconds = 60, 16 | onError = () => {}, 17 | }: PollOptions): Promise { 18 | let elapsed = 0 19 | let result: T | null = null 20 | 21 | while (elapsed < maxWaitSeconds) { 22 | try { 23 | result = await taskFn() 24 | if (checkFn(result)) break 25 | } catch (error) { 26 | onError(error) 27 | } 28 | 29 | await sleep(intervalSeconds * 1000) 30 | elapsed += intervalSeconds 31 | } 32 | 33 | if (result === null || !checkFn(result)) { 34 | throw new Error('Polling timed out or condition not met') 35 | } 36 | 37 | return result 38 | } 39 | -------------------------------------------------------------------------------- /packages/mcp-common/src/scopes.ts: -------------------------------------------------------------------------------- 1 | // These scopes are required for Cloudflare auth 2 | export const RequiredScopes = { 3 | 'user:read': 'See your user info such as name, email address, and account memberships.', 4 | offline_access: 'Grants refresh tokens for long-lived access.', 5 | } as const 6 | -------------------------------------------------------------------------------- /packages/mcp-common/src/types/cloudflare-mcp-agent.types.ts: -------------------------------------------------------------------------------- 1 | import { type McpAgent } from 'agents/mcp' 2 | 3 | import { type AccountSchema, type UserSchema } from '../cloudflare-oauth-handler' 4 | 5 | import type { CloudflareMCPServer } from '../server' 6 | 7 | export type CloudflareMCPAgentState = { activeAccountId: string | null } 8 | 9 | export type CloudflareMCPAgentProps = { 10 | accessToken: string 11 | user: UserSchema['result'] 12 | accounts: AccountSchema['result'] 13 | } 14 | 15 | // We omit server in this type, so that we can later use our own CloudflareMCPServer type ( which extends McpServer ) 16 | type McpAgentWithoutServer = Omit< 17 | McpAgent, 18 | 'server' 19 | > 20 | 21 | export interface CloudflareMcpAgent extends McpAgentWithoutServer { 22 | server: CloudflareMCPServer 23 | setActiveAccountId(accountId: string): Promise 24 | getActiveAccountId(): Promise 25 | } 26 | -------------------------------------------------------------------------------- /packages/mcp-common/src/types/d1.types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file contains the validators for the d1 tools. 3 | */ 4 | import { z } from 'zod' 5 | 6 | import type { DatabaseCreateParams } from 'cloudflare/resources/d1.mjs' 7 | 8 | export const D1DatabaseNameParam: z.ZodType = z.string() 9 | export const D1DatabasePrimaryLocationHintParam: z.ZodType< 10 | DatabaseCreateParams['primary_location_hint'] 11 | > = z.enum(['wnam', 'enam', 'weur', 'eeur', 'apac', 'oc']).optional() 12 | 13 | export const D1DatabaseQuerySqlParam = z.string() 14 | export const D1DatabaseQueryParamsParam = z.array(z.string()).optional() 15 | -------------------------------------------------------------------------------- /packages/mcp-common/src/types/shared.types.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const PaginationPerPageParam = z.number().nullable().optional() 4 | export const PaginationPageParam = z.number().nullable().optional() 5 | 6 | export const PaginationLimitParam = z.number().optional() 7 | export const PaginationOffsetParam = z.number().optional() 8 | -------------------------------------------------------------------------------- /packages/mcp-common/src/types/tools.types.ts: -------------------------------------------------------------------------------- 1 | import type { z } from 'zod' 2 | 3 | export type ToolHandler> = ( 4 | params: T & { accountId: string | null; apiToken: string } 5 | ) => Promise 6 | 7 | export interface ToolDefinition> { 8 | name: string 9 | description: string 10 | params: Record 11 | handler: ToolHandler 12 | } 13 | -------------------------------------------------------------------------------- /packages/mcp-common/src/types/workers.types.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export type WorkersService = z.infer 4 | export const WorkersService = z.object({ 5 | id: z.string(), 6 | default_environment: z.object({ 7 | environment: z.string(), 8 | script_tag: z.string(), 9 | created_on: z.string(), 10 | modified_on: z.string(), 11 | script: z.object({ 12 | created_on: z.string(), 13 | modified_on: z.string(), 14 | id: z.string(), 15 | }), 16 | }), 17 | created_on: z.string(), 18 | modified_on: z.string(), 19 | }) 20 | -------------------------------------------------------------------------------- /packages/mcp-common/src/v4-api.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | type V4ErrorSchema = typeof V4ErrorSchema 4 | const V4ErrorSchema = z.array( 5 | z.object({ 6 | code: z.number().optional(), 7 | message: z.string(), 8 | }) 9 | ) 10 | 11 | export type V4Schema = z.ZodObject<{ 12 | result: z.ZodNullable 13 | success: z.ZodBoolean 14 | errors: V4ErrorSchema 15 | messages: z.ZodArray 16 | }> 17 | 18 | export type V4SchemaWithResultInfo< 19 | TResultType extends z.ZodType, 20 | TResultInfoType extends z.ZodType, 21 | > = z.ZodObject<{ 22 | result: z.ZodNullable 23 | success: z.ZodBoolean 24 | errors: V4ErrorSchema 25 | messages: z.ZodArray 26 | result_info: z.ZodOptional> 27 | }> 28 | 29 | export function V4Schema( 30 | resultType: TResultType 31 | ): V4Schema 32 | export function V4Schema( 33 | resultType: TResultType, 34 | resultInfoType: TResultInfoType 35 | ): V4SchemaWithResultInfo 36 | export function V4Schema( 37 | resultType: TResultType, 38 | resultInfoType?: TResultInfoType 39 | ): V4Schema | V4SchemaWithResultInfo { 40 | if (resultInfoType) { 41 | return z.object({ 42 | result: resultType.nullable(), 43 | result_info: resultInfoType.nullable().optional(), 44 | success: z.boolean(), 45 | errors: V4ErrorSchema, 46 | messages: z.array(z.any()), 47 | }) 48 | } else { 49 | return z.object({ 50 | result: resultType.nullable(), 51 | success: z.boolean(), 52 | errors: V4ErrorSchema, 53 | messages: z.array(z.any()), 54 | }) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/mcp-common/tests/utils/cloudflare-mock.ts: -------------------------------------------------------------------------------- 1 | import { vi } from 'vitest' 2 | 3 | import type { Account } from 'cloudflare/resources/accounts/accounts.mjs' 4 | 5 | /** 6 | * Creates a mocked implementation of the Cloudflare client 7 | */ 8 | export const cloudflareClientMockImplementation = () => { 9 | return { 10 | accounts: { 11 | list: vi.fn(async () => { 12 | return { 13 | success: true, 14 | result: [ 15 | { 16 | id: 'mock-account-id', 17 | name: 'mock-account-name', 18 | }, 19 | ] satisfies Account[], 20 | } 21 | }), 22 | }, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/mcp-common/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/workers-lib.json", 3 | "include": ["**/*.ts"], 4 | "exclude": ["node_modules", "tsconfig.json"], 5 | "compilerOptions": { 6 | "types": ["@cloudflare/vitest-pool-workers"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/mcp-common/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { TestEnv } from './vitest.config' 2 | 3 | declare module 'cloudflare:test' { 4 | interface ProvidedEnv extends TestEnv {} 5 | } 6 | -------------------------------------------------------------------------------- /packages/mcp-common/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineWorkersProject } from '@cloudflare/vitest-pool-workers/config' 2 | 3 | export interface TestEnv { 4 | CLOUDFLARE_MOCK_ACCOUNT_ID: string 5 | CLOUDFLARE_MOCK_API_TOKEN: string 6 | } 7 | 8 | export default defineWorkersProject({ 9 | test: { 10 | poolOptions: { 11 | workers: { 12 | singleWorker: true, 13 | miniflare: { 14 | compatibilityDate: '2025-03-10', 15 | compatibilityFlags: ['nodejs_compat'], 16 | bindings: { 17 | CLOUDFLARE_MOCK_ACCOUNT_ID: 'mock-account-id', 18 | CLOUDFLARE_MOCK_API_TOKEN: 'mock-api-token', 19 | } satisfies Partial, 20 | }, 21 | }, 22 | }, 23 | }, 24 | }) 25 | -------------------------------------------------------------------------------- /packages/mcp-observability/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/mcp-observability", 3 | "version": "0.31.1", 4 | "private": true, 5 | "sideEffects": false, 6 | "type": "module", 7 | "main": "./src/index.ts", 8 | "scripts": { 9 | "types": "wrangler types --include-env=false" 10 | }, 11 | "directories": { 12 | "bin": "bin" 13 | }, 14 | "dependencies": { 15 | "@modelcontextprotocol/sdk": "1.10.2", 16 | "wrangler": "4.10.0", 17 | "zod": "3.24.2" 18 | }, 19 | "devDependencies": { 20 | "@repo/typescript-config": "workspace:*", 21 | "@types/node": "22.14.1" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/mcp-observability/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './analytics-engine' 2 | export * from './metrics' 3 | -------------------------------------------------------------------------------- /packages/mcp-observability/src/metrics.ts: -------------------------------------------------------------------------------- 1 | import { type ClientCapabilities } from '@modelcontextprotocol/sdk/types.js' 2 | 3 | import { MetricsEvent, MetricsEventIndexIds } from './analytics-engine' 4 | 5 | /** 6 | * TODO: once there are better hooks into MCP servers, we should track the session ID 7 | */ 8 | export class ToolCall extends MetricsEvent { 9 | constructor( 10 | private toolCall: { 11 | userId?: string 12 | toolName: string 13 | errorCode?: number 14 | } 15 | ) { 16 | super() 17 | } 18 | 19 | toDataPoint(): AnalyticsEngineDataPoint { 20 | return { 21 | indexes: [MetricsEventIndexIds.TOOL_CALL], 22 | blobs: this.mapBlobs({ 23 | blob3: this.toolCall.userId, 24 | blob4: this.toolCall.toolName, 25 | }), 26 | doubles: this.mapDoubles({ 27 | double1: this.toolCall.errorCode, 28 | }), 29 | } 30 | } 31 | } 32 | 33 | export class SessionStart extends MetricsEvent { 34 | constructor( 35 | private session: { 36 | userId?: string 37 | clientInfo?: { 38 | name: string 39 | version: string 40 | } 41 | clientCapabilities?: ClientCapabilities 42 | } 43 | ) { 44 | super() 45 | } 46 | 47 | toDataPoint(): AnalyticsEngineDataPoint { 48 | return { 49 | indexes: [MetricsEventIndexIds.SESSION_START], 50 | blobs: this.mapBlobs({ 51 | blob3: this.session.userId, 52 | blob4: this.session.clientInfo?.name, 53 | blob5: this.session.clientInfo?.version, 54 | }), 55 | doubles: this.mapDoubles({ 56 | double1: this.session.clientCapabilities?.roots ? 1 : 0, 57 | double2: this.session.clientCapabilities?.sampling ? 1 : 0, 58 | }), 59 | } 60 | } 61 | } 62 | 63 | export class AuthUser extends MetricsEvent { 64 | constructor( 65 | private authUser: { 66 | userId?: string 67 | errorMessage?: string 68 | } 69 | ) { 70 | super() 71 | } 72 | 73 | toDataPoint(): AnalyticsEngineDataPoint { 74 | return { 75 | indexes: [MetricsEventIndexIds.AUTH_USER], 76 | blobs: this.mapBlobs({ 77 | blob3: this.authUser.userId, 78 | blob4: this.authUser.errorMessage, 79 | }), 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /packages/mcp-observability/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/workers.json", 3 | "include": ["**/*.ts"], 4 | "exclude": ["node_modules", "tsconfig.json"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/tools/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ['@repo/eslint-config/default.cjs'], 5 | } 6 | -------------------------------------------------------------------------------- /packages/tools/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @repo/tools 2 | 3 | ## 0.32.1 4 | 5 | ### Patch Changes 6 | 7 | - 86c2e4f: Add API token passthrough auth 8 | 9 | ## 0.32.0 10 | 11 | ### Minor Changes 12 | 13 | - 33e0198: BANDA-899 feat: add runx deploy-published-workers script 14 | 15 | ### Patch Changes 16 | 17 | - bdb5b89: chore: remove pnpx from wrangler deploy script 18 | 19 | This is redundant because turbo and pnpm already add the bundled wrangler command to $PATH 20 | -------------------------------------------------------------------------------- /packages/tools/README.md: -------------------------------------------------------------------------------- 1 | # @repo/tools 2 | 3 | A collection of shared scripts for automating the monorepo while ensuring consistency across packages. 4 | 5 | ## Scripts 6 | 7 | ### Bin Scripts 8 | 9 | Simple shell scripts for common development tasks: 10 | 11 | - `run-tsc`: Run TypeScript type checking 12 | - `run-eslint-workers`: Run ESLint checks 13 | - `run-vitest`: Run tests 14 | - `run-vitest-ci`: Run tests in CI mode 15 | - `run-turbo`: Run Turbo commands with tracking disabled 16 | - `run-wrangler-deploy`: Deploy using Wrangler 17 | - `run-wrangler-types`: Generate Wrangler types 18 | - `run-fix-deps`: Fix dependencies 19 | 20 | ### Runx CLI 21 | 22 | A TypeScript-based CLI for more complex automation tasks. While the bin scripts work well for simple tasks, the runx CLI provides better type safety and more sophisticated programmatic control. 23 | 24 | Usage: 25 | 26 | ```bash 27 | pnpm runx [options] 28 | ``` 29 | 30 | Available commands: 31 | 32 | - `deploy-published-workers`: Deploy Cloudflare Workers (based on which packages changesets marked as published in the release). 33 | 34 | Note: 35 | 36 | The CLI will automatically use Bun if available, but falls back to tsx if not installed. 37 | -------------------------------------------------------------------------------- /packages/tools/bin/run-changeset-new: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu 3 | 4 | repo_root=$(git rev-parse --show-toplevel) 5 | cd "$repo_root" 6 | 7 | # Stage changes so that changeset can see them 8 | git add . 9 | 10 | changeset 11 | 12 | # Make sure a changeset was created 13 | git status --porcelain | grep '.changeset/.*\.md' >/dev/null && true || 14 | (echo "🚨 No changeset created" && exit 1) 15 | 16 | new_changeset=$(ls -t .changeset/*.md | head -n 1) 17 | echo "📝 New changeset: $new_changeset" 18 | git add "$new_changeset" -------------------------------------------------------------------------------- /packages/tools/bin/run-eslint-workers: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | args=( 5 | --cache 6 | --cache-strategy content 7 | --cache-location ./node_modules/.cache/eslint 8 | --max-warnings 1000 9 | . 10 | ) 11 | 12 | if [[ ! -z "${FIX_ESLINT:-}" ]]; then 13 | args+=("--fix") 14 | fi 15 | 16 | if [[ ! -z "${GITHUB_ACTIONS:-}" ]]; then 17 | args+=("--max-warnings=0") 18 | fi 19 | 20 | # get additional args 21 | while [[ $# -gt 0 ]]; do 22 | args+=("$1") 23 | shift 24 | done 25 | 26 | eslint "${args[@]}" 27 | -------------------------------------------------------------------------------- /packages/tools/bin/run-fix-deps: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | repo_root=$(git rev-parse --show-toplevel) 5 | cd "$repo_root" 6 | 7 | syncpack fix-mismatches 8 | -------------------------------------------------------------------------------- /packages/tools/bin/run-tsc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | tsc --noEmit -p tsconfig.json 5 | -------------------------------------------------------------------------------- /packages/tools/bin/run-turbo: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Helper script for running turbo with tracking disabled 4 | # so that we don't have to export this env var everywhere. 5 | 6 | export DO_NOT_TRACK=1 7 | 8 | turbo "$@" 9 | -------------------------------------------------------------------------------- /packages/tools/bin/run-vitest: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | if test -f ./vitest.config.ts; then 5 | vitest run --testTimeout=15000 6 | elif test -f ./vitest.config.node.ts; then 7 | vitest run --testTimeout=15000 -c ./vitest.config.node.ts 8 | fi 9 | -------------------------------------------------------------------------------- /packages/tools/bin/run-vitest-ci: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | vitest run --silent --testTimeout=15000 5 | 6 | -------------------------------------------------------------------------------- /packages/tools/bin/run-wrangler-deploy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Extract name and version from package.json using jq 4 | NAME=$(jq -r '.name' package.json) 5 | VERSION=$(jq -r '.version' package.json) 6 | 7 | # Deploy with wrangler using the extracted values as binding variables 8 | echo "Deploying MCP server $NAME version $VERSION" 9 | wrangler deploy \ 10 | --var MCP_SERVER_NAME:"$NAME" \ 11 | --var MCP_SERVER_VERSION:"$VERSION" \ 12 | "$@" 13 | -------------------------------------------------------------------------------- /packages/tools/bin/run-wrangler-types: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | wrangler types --include-env=false 5 | -------------------------------------------------------------------------------- /packages/tools/bin/runx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | set -eu 3 | 4 | script_path="$(realpath "$(dirname "$0")/../src/bin/runx.ts")" 5 | 6 | bin_dir="$(realpath "$(dirname "$0")")" 7 | tsx_path="$(realpath "$bin_dir/../node_modules/.bin/tsx")" 8 | 9 | if command -v bun >/dev/null 2>&1; then 10 | bun "$script_path" "$@" 11 | else 12 | "$tsx_path" "$script_path" "$@" 13 | fi 14 | -------------------------------------------------------------------------------- /packages/tools/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/tools", 3 | "version": "0.32.1", 4 | "private": true, 5 | "sideEffects": false, 6 | "type": "module", 7 | "directories": { 8 | "bin": "bin" 9 | }, 10 | "devDependencies": { 11 | "@types/fs-extra": "11.0.4", 12 | "@types/node": "22.14.1", 13 | "vitest": "3.0.9" 14 | }, 15 | "dependencies": { 16 | "@commander-js/extra-typings": "13.1.0", 17 | "@jahands/cli-tools": "0.10.2", 18 | "commander": "13.1.0", 19 | "empathic": "1.1.0", 20 | "tsx": "4.19.3", 21 | "zod": "4.0.0-beta.20250505T195954", 22 | "zx": "8.5.4" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/tools/src/bin/runx.ts: -------------------------------------------------------------------------------- 1 | import 'zx/globals' 2 | 3 | import { program } from '@commander-js/extra-typings' 4 | import { catchProcessError } from '@jahands/cli-tools' 5 | 6 | import { deployPublishedWorkersCmd } from '../cmd/deploy-published-packages' 7 | 8 | program 9 | .name('runx') 10 | .description('A CLI for scripts that automate this repo') 11 | 12 | // While `packages/tools/bin` scripts work well for simple tasks, 13 | // a typescript CLI is nicer for more complex things. 14 | 15 | .addCommand(deployPublishedWorkersCmd) 16 | 17 | // Don't hang for unresolved promises 18 | .hook('postAction', () => process.exit(0)) 19 | .parseAsync() 20 | .catch(catchProcessError()) 21 | -------------------------------------------------------------------------------- /packages/tools/src/changesets.ts: -------------------------------------------------------------------------------- 1 | import { cliError, isNotFoundError } from '@jahands/cli-tools' 2 | import { z } from 'zod' 3 | 4 | export type PublishedPackage = z.infer 5 | export const PublishedPackage = z.object({ 6 | name: z.string(), 7 | version: z.string(), 8 | }) 9 | export const PublishedPackages = z.array(PublishedPackage) 10 | 11 | /** 12 | * Reads and parses the list of published packages from the runner's temporary directory. 13 | * This file is generated by the changesets action in the release workflow and contains 14 | * information about packages that were just published. 15 | * @returns Array of published packages 16 | * @throws Error if RUNNER_TEMP is not set, file is not found, or JSON parsing fails 17 | */ 18 | export async function getPublishedPackages(): Promise { 19 | const runnerTemp = await z 20 | .string({ error: '$RUNNER_TEMP is not set' }) 21 | .min(1, { error: '$RUNNER_TEMP is empty' }) 22 | .parseAsync(process.env.RUNNER_TEMP) 23 | .catch((e) => { 24 | throw cliError(z.prettifyError(e)) 25 | }) 26 | 27 | const publishedPackagesPath = path.join(runnerTemp, 'published-packages.json') 28 | 29 | echo(chalk.dim(`Reading published packages from ${publishedPackagesPath}`)) 30 | 31 | return fs 32 | .readFile(publishedPackagesPath, 'utf8') 33 | .then((s) => PublishedPackages.parse(JSON.parse(s))) 34 | .catch((e) => { 35 | if (isNotFoundError(e)) { 36 | throw cliError(`No published packages file found at: ${publishedPackagesPath}`) 37 | } else if (e instanceof z.ZodError) { 38 | throw new Error(`Failed to parse published packages: ${z.prettifyError(e)}`) 39 | } 40 | throw new Error(`Failed to parse published packages: ${e}`) 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /packages/tools/src/cmd/deploy-published-packages.ts: -------------------------------------------------------------------------------- 1 | import { Command } from '@commander-js/extra-typings' 2 | import { validateArg } from '@jahands/cli-tools' 3 | import z from 'zod' 4 | 5 | import { getPublishedPackages } from '../changesets' 6 | 7 | export const deployPublishedWorkersCmd = new Command('deploy-published-workers') 8 | .description( 9 | 'Deploy Cloudflare Workers (based on which packages changesets marked as published in the release)' 10 | ) 11 | .requiredOption( 12 | '-e, --env ', 13 | 'The environment to deploy to', 14 | validateArg(z.enum(['staging', 'production'])) 15 | ) 16 | .action(async ({ env }) => { 17 | const publishedPackages = await getPublishedPackages() 18 | 19 | // This technically includes all versioned packages (including non-Workers), 20 | // but that's fine because only Workers include a `deploy` package.json script. 21 | const filters = publishedPackages.flatMap((p) => ['-F', p.name]) satisfies string[] 22 | 23 | await $({ 24 | verbose: true, 25 | })`turbo deploy ${filters} -- --env ${env}` 26 | }) 27 | -------------------------------------------------------------------------------- /packages/tools/src/proc.ts: -------------------------------------------------------------------------------- 1 | export function getOutcome(exitCode: number | null) { 2 | if (exitCode === 0) { 3 | return chalk.green('Success!') 4 | } else { 5 | return chalk.red(`Failed with code: ${exitCode}`) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/tools/src/test/fixtures/changesets/empty/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/mcp-server-cloudflare/main/packages/tools/src/test/fixtures/changesets/empty/.gitkeep -------------------------------------------------------------------------------- /packages/tools/src/test/fixtures/changesets/invalid-json/published-packages.json: -------------------------------------------------------------------------------- 1 | this is not valid json 2 | -------------------------------------------------------------------------------- /packages/tools/src/test/fixtures/changesets/invalid-schema/published-packages.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "package-a", 4 | "version": 123 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /packages/tools/src/test/fixtures/changesets/valid/published-packages.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "package-a", 4 | "version": "1.0.0" 5 | }, 6 | { 7 | "name": "package-b", 8 | "version": "2.1.3" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /packages/tools/src/test/setup.ts: -------------------------------------------------------------------------------- 1 | // Ensure chalk doesn't add colors to output for consistent snapshots 2 | delete process.env.FORCE_COLOR 3 | 4 | // runx uses zx/globals imported in bin/runx.ts 5 | // This import ensures that tests work without 6 | // needing to import this manually. 7 | await import('zx/globals') 8 | -------------------------------------------------------------------------------- /packages/tools/src/tsconfig.ts: -------------------------------------------------------------------------------- 1 | import { inspect } from 'node:util' 2 | 3 | import type { 4 | convertCompilerOptionsFromJson, 5 | createProgram, 6 | readConfigFile, 7 | sys, 8 | CompilerOptions as TSCompilerOptions, 9 | } from 'typescript' 10 | 11 | export type { TSCompilerOptions } 12 | 13 | interface TSModule { 14 | readConfigFile: typeof readConfigFile 15 | convertCompilerOptionsFromJson: typeof convertCompilerOptionsFromJson 16 | sys: typeof sys 17 | createProgram: typeof createProgram 18 | } 19 | 20 | /** 21 | * TypeScript helpers. This is a class so that we can dynamically import the TypeScript module 22 | * to reduce runx start time for commands that don't use the typescript package. 23 | * 24 | * @example 25 | * 26 | * ```ts 27 | * const tsHelpers = await new TSHelpers().init() 28 | * const { ts } = tsHelpers 29 | * const tsConfig = tsHelpers.getTSConfig() 30 | * ts.createProgram(entryPoints, tsConfig).emit() 31 | * ``` 32 | */ 33 | export class TSHelpers { 34 | #ts: TSModule | undefined 35 | public get ts(): TSModule { 36 | if (!this.#ts) { 37 | throw new Error('TSHelpers not initialized. Call init() first.') 38 | } 39 | return this.#ts 40 | } 41 | 42 | async init(): Promise { 43 | this.#ts = (await import('typescript')) as TSModule 44 | return this 45 | } 46 | 47 | getTSConfig(configPath = 'tsconfig.json'): TSCompilerOptions { 48 | const jsonCompopts = this.getCompilerOptionsJSONFollowExtends(configPath) 49 | const tmp = this.ts.convertCompilerOptionsFromJson(jsonCompopts, '') 50 | if (tmp.errors.length > 0) { 51 | throw new Error(`failed parse config: ${inspect(tmp)}`) 52 | } 53 | const tsCompopts: TSCompilerOptions = tmp.options 54 | return tsCompopts 55 | } 56 | 57 | getCompilerOptionsJSONFollowExtends(configPath: string): { 58 | [key: string]: unknown 59 | } { 60 | let compopts = {} 61 | const config = this.ts.readConfigFile(configPath, this.ts.sys.readFile).config 62 | if (config.extends !== undefined) { 63 | const rqrpath = require.resolve(config.extends) 64 | compopts = this.getCompilerOptionsJSONFollowExtends(rqrpath) 65 | } 66 | return { 67 | ...compopts, 68 | ...config.compilerOptions, 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /packages/tools/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/tools.json", 3 | "include": ["**/*.ts"], 4 | "exclude": ["node_modules", "tsconfig.json"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/tools/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config' 2 | 3 | export default defineConfig({ 4 | test: { 5 | setupFiles: [`${__dirname}/src/test/setup.ts`], 6 | environment: 'node', 7 | }, 8 | }) 9 | -------------------------------------------------------------------------------- /packages/typescript-config/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @repo/typescript-config 2 | 3 | ## 0.2.5 4 | 5 | ### Patch Changes 6 | 7 | - bdb5b89: chore: remove publishConfig from typescript-config/package.json 8 | 9 | This did nothing. 10 | -------------------------------------------------------------------------------- /packages/typescript-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/typescript-config", 3 | "version": "0.2.5", 4 | "private": true, 5 | "sideEffects": false, 6 | "devDependencies": { 7 | "@types/node": "22.14.1" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/typescript-config/tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Default", 4 | "compilerOptions": { 5 | "target": "es2022", 6 | "module": "es2022", 7 | "composite": false, 8 | "declaration": true, 9 | "declarationMap": true, 10 | "esModuleInterop": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "inlineSources": false, 13 | "isolatedModules": true, 14 | "moduleResolution": "node", 15 | "noUnusedLocals": false, 16 | "noUnusedParameters": false, 17 | "preserveWatchOutput": true, 18 | "skipLibCheck": true, 19 | "strict": true, 20 | "noEmit": true, 21 | "experimentalDecorators": true 22 | }, 23 | "exclude": ["node_modules"] 24 | } 25 | -------------------------------------------------------------------------------- /packages/typescript-config/workers-lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@repo/typescript-config/workers.json", 4 | "include": ["${configDir}/**/*.ts", "${configDir}/**/*.tsx"], 5 | "exclude": ["${configDir}/node_modules/", "${configDir}/dist/"], 6 | "compilerOptions": { 7 | "types": ["@cloudflare/vitest-pool-workers"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/typescript-config/workers.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "include": [ 4 | "${configDir}/worker-configuration.d.ts", 5 | "${configDir}/env.d.ts", 6 | "${configDir}/**/*.ts", 7 | "${configDir}/**/*.tsx" 8 | ], 9 | "exclude": ["${configDir}/node_modules/", "${configDir}/dist/"], 10 | "compilerOptions": { 11 | "target": "es2022", 12 | "lib": ["es2022"], 13 | "jsx": "react", 14 | "module": "es2022", 15 | "moduleResolution": "bundler", 16 | "types": ["./worker-configuration.d.ts", "@cloudflare/vitest-pool-workers", "@types/node"], 17 | "resolveJsonModule": true, 18 | "allowJs": true, 19 | "checkJs": false, 20 | "noEmit": true, 21 | "isolatedModules": true, 22 | "allowSyntheticDefaultImports": true, 23 | "forceConsistentCasingInFileNames": true, 24 | "strict": true, 25 | "skipLibCheck": true 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'apps/*' 3 | - 'packages/*' 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@repo/typescript-config/tools.json" 3 | } 4 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "globalPassThroughEnv": ["FORCE_COLOR", "RUNNER_TEMP"], 4 | "tasks": { 5 | "deploy": { 6 | "cache": false, 7 | "env": ["CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_API_TOKEN", "CLOUDFLARE_STAGING_API_TOKEN"], 8 | "outputs": ["dist"], 9 | "outputLogs": "new-only" 10 | }, 11 | "check": { 12 | "dependsOn": ["^check:types", "^check:lint", "check:types", "check:lint"], 13 | "outputLogs": "new-only" 14 | }, 15 | "check:types": { 16 | "dependsOn": ["^check:types"], 17 | "outputLogs": "new-only" 18 | }, 19 | "check:lint": { 20 | "env": ["GITHUB_ACTIONS"], 21 | "dependsOn": ["^check:lint"], 22 | "outputLogs": "new-only" 23 | }, 24 | "eval:ci": { 25 | "dependsOn": ["^eval:ci"], 26 | "outputLogs": "new-only" 27 | }, 28 | "types": { 29 | "dependsOn": ["^types"], 30 | "outputLogs": "new-only" 31 | }, 32 | "//#check:format": { 33 | "outputLogs": "new-only" 34 | }, 35 | "//#check:deps": { 36 | "outputLogs": "new-only" 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /vitest.workspace.ts: -------------------------------------------------------------------------------- 1 | import { defineWorkspace } from 'vitest/config' 2 | 3 | export default defineWorkspace([ 4 | 'apps/*/vitest.config.ts', 5 | 'apps/*/vitest.config.node.ts', 6 | 'packages/*/vitest.config.ts', 7 | 'packages/*/vitest.config.node.ts', 8 | ]) 9 | --------------------------------------------------------------------------------