#
tokens: 42104/50000 3/493 files (page 15/20)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 15 of 20. Use http://codebase.md/getsentry/sentry-mcp?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .claude
│   ├── agents
│   │   └── claude-optimizer.md
│   ├── commands
│   │   ├── gh-pr.md
│   │   └── gh-review.md
│   └── settings.json
├── .craft.yml
├── .cursor
│   └── mcp.json
├── .env.example
├── .github
│   └── workflows
│       ├── deploy.yml
│       ├── eval.yml
│       ├── merge-jobs.yml
│       ├── release.yml
│       ├── smoke-tests.yml
│       ├── test.yml
│       └── token-cost.yml
├── .gitignore
├── .mcp.json
├── .vscode
│   ├── extensions.json
│   ├── mcp.json
│   └── settings.json
├── AGENTS.md
├── benchmark-agent.sh
├── bin
│   └── bump-version.sh
├── biome.json
├── CLAUDE.md
├── codecov.yml
├── core
├── docs
│   ├── adding-tools.md
│   ├── api-patterns.md
│   ├── architecture.md
│   ├── cloudflare
│   │   ├── architecture.md
│   │   ├── oauth-architecture.md
│   │   └── overview.md
│   ├── coding-guidelines.md
│   ├── common-patterns.md
│   ├── error-handling.md
│   ├── github-actions.md
│   ├── llms
│   │   ├── document-scopes.md
│   │   ├── documentation-style-guide.md
│   │   └── README.md
│   ├── logging.md
│   ├── monitoring.md
│   ├── pr-management.md
│   ├── quality-checks.md
│   ├── README.md
│   ├── releases
│   │   ├── cloudflare.md
│   │   └── stdio.md
│   ├── search-events-api-patterns.md
│   ├── security.md
│   ├── specs
│   │   ├── README.md
│   │   ├── search-events.md
│   │   └── subpath-constraints.md
│   ├── testing-remote.md
│   ├── testing-stdio.md
│   ├── testing.md
│   └── token-cost-tracking.md
├── LICENSE.md
├── Makefile
├── package.json
├── packages
│   ├── mcp-cloudflare
│   │   ├── .env.example
│   │   ├── components.json
│   │   ├── index.html
│   │   ├── package.json
│   │   ├── public
│   │   │   ├── demo.cast
│   │   │   ├── favicon.ico
│   │   │   ├── flow-transparent.png
│   │   │   ├── flow.jpg
│   │   │   ├── keycap-⌘.png
│   │   │   ├── keycap-c.png
│   │   │   └── keycap-v.png
│   │   ├── src
│   │   │   ├── client
│   │   │   │   ├── app.tsx
│   │   │   │   ├── components
│   │   │   │   │   ├── animation
│   │   │   │   │   │   ├── browser-ui
│   │   │   │   │   │   │   ├── BrowserWindow.tsx
│   │   │   │   │   │   │   ├── BrowserWindowIconSidebar.tsx
│   │   │   │   │   │   │   ├── DiffBlock.tsx
│   │   │   │   │   │   │   ├── IDEWindow.tsx
│   │   │   │   │   │   │   ├── IssueDetails.tsx
│   │   │   │   │   │   │   ├── keys-copy.tsx
│   │   │   │   │   │   │   ├── LoadingSquares.tsx
│   │   │   │   │   │   │   ├── RootCause.tsx
│   │   │   │   │   │   │   ├── seer-clipmask.tsx
│   │   │   │   │   │   │   ├── seer-noisefilter.tsx
│   │   │   │   │   │   │   ├── seer.tsx
│   │   │   │   │   │   │   └── WindowHeader.tsx
│   │   │   │   │   │   ├── BrowserAnimation.tsx
│   │   │   │   │   │   ├── DataWire.tsx
│   │   │   │   │   │   ├── dracula.css
│   │   │   │   │   │   ├── terminal-ui
│   │   │   │   │   │   │   ├── keys-paste.tsx
│   │   │   │   │   │   │   ├── SpeedDisplay.tsx
│   │   │   │   │   │   │   └── StepsList.tsx
│   │   │   │   │   │   ├── TerminalAnimation.tsx
│   │   │   │   │   │   └── tests.tsx
│   │   │   │   │   ├── chat
│   │   │   │   │   │   ├── auth-form.tsx
│   │   │   │   │   │   ├── chat-input.tsx
│   │   │   │   │   │   ├── chat-message.tsx
│   │   │   │   │   │   ├── chat-messages.tsx
│   │   │   │   │   │   ├── chat-ui.tsx
│   │   │   │   │   │   ├── chat.tsx
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── tool-invocation.tsx
│   │   │   │   │   │   └── types.ts
│   │   │   │   │   ├── docs
│   │   │   │   │   │   └── toc.tsx
│   │   │   │   │   ├── fragments
│   │   │   │   │   │   ├── install-tabs.tsx
│   │   │   │   │   │   ├── remote-setup.tsx
│   │   │   │   │   │   ├── setup-guide.tsx
│   │   │   │   │   │   └── stdio-setup.tsx
│   │   │   │   │   ├── getting-started.tsx
│   │   │   │   │   ├── hero
│   │   │   │   │   │   ├── header-divider.tsx
│   │   │   │   │   │   └── hero-block.tsx
│   │   │   │   │   ├── home-layout
│   │   │   │   │   │   ├── footer.tsx
│   │   │   │   │   │   └── sidebars.tsx
│   │   │   │   │   ├── ui
│   │   │   │   │   │   ├── accordion.tsx
│   │   │   │   │   │   ├── backdrop.tsx
│   │   │   │   │   │   ├── badge.tsx
│   │   │   │   │   │   ├── base.tsx
│   │   │   │   │   │   ├── button.tsx
│   │   │   │   │   │   ├── code-snippet.tsx
│   │   │   │   │   │   ├── header.tsx
│   │   │   │   │   │   ├── icon.tsx
│   │   │   │   │   │   ├── icons
│   │   │   │   │   │   │   ├── gemini.tsx
│   │   │   │   │   │   │   └── sentry.tsx
│   │   │   │   │   │   ├── interactive-markdown.tsx
│   │   │   │   │   │   ├── json-schema-params.tsx
│   │   │   │   │   │   ├── markdown.tsx
│   │   │   │   │   │   ├── note.tsx
│   │   │   │   │   │   ├── prose.tsx
│   │   │   │   │   │   ├── section.tsx
│   │   │   │   │   │   ├── slash-command-actions.tsx
│   │   │   │   │   │   ├── slash-command-text.tsx
│   │   │   │   │   │   ├── sliding-panel.tsx
│   │   │   │   │   │   ├── template-vars.tsx
│   │   │   │   │   │   ├── tool-actions.tsx
│   │   │   │   │   │   └── typewriter.tsx
│   │   │   │   │   └── usecases
│   │   │   │   │       ├── fix-bugs.tsx
│   │   │   │   │       ├── index.tsx
│   │   │   │   │       ├── instrument.tsx
│   │   │   │   │       ├── search-things.tsx
│   │   │   │   │       └── search-visual.tsx
│   │   │   │   ├── contexts
│   │   │   │   │   └── auth-context.tsx
│   │   │   │   ├── hooks
│   │   │   │   │   ├── use-endpoint-mode.ts
│   │   │   │   │   ├── use-mcp-metadata.ts
│   │   │   │   │   ├── use-persisted-chat.ts
│   │   │   │   │   ├── use-scroll-lock.ts
│   │   │   │   │   └── use-streaming-simulation.ts
│   │   │   │   ├── index.css
│   │   │   │   ├── instrument.ts
│   │   │   │   ├── lib
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── main.tsx
│   │   │   │   ├── utils
│   │   │   │   │   ├── chat-error-handler.ts
│   │   │   │   │   ├── cursor-deeplink.ts
│   │   │   │   │   └── index.ts
│   │   │   │   └── vite-env.d.ts
│   │   │   ├── constants.ts
│   │   │   ├── server
│   │   │   │   ├── app.test.ts
│   │   │   │   ├── app.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── lib
│   │   │   │   │   ├── approval-dialog.test.ts
│   │   │   │   │   ├── approval-dialog.ts
│   │   │   │   │   ├── constraint-utils.test.ts
│   │   │   │   │   ├── constraint-utils.ts
│   │   │   │   │   ├── html-utils.ts
│   │   │   │   │   ├── mcp-handler.test.ts
│   │   │   │   │   ├── mcp-handler.ts
│   │   │   │   │   └── slug-validation.ts
│   │   │   │   ├── logging.ts
│   │   │   │   ├── oauth
│   │   │   │   │   ├── authorize.test.ts
│   │   │   │   │   ├── callback.test.ts
│   │   │   │   │   ├── constants.ts
│   │   │   │   │   ├── helpers.test.ts
│   │   │   │   │   ├── helpers.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── routes
│   │   │   │   │   │   ├── authorize.ts
│   │   │   │   │   │   ├── callback.ts
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   └── state.ts
│   │   │   │   ├── routes
│   │   │   │   │   ├── chat-oauth.ts
│   │   │   │   │   ├── chat.ts
│   │   │   │   │   ├── mcp.ts
│   │   │   │   │   ├── metadata.ts
│   │   │   │   │   ├── search.test.ts
│   │   │   │   │   └── search.ts
│   │   │   │   ├── sentry.config.ts
│   │   │   │   ├── types
│   │   │   │   │   └── chat.ts
│   │   │   │   ├── types.ts
│   │   │   │   └── utils
│   │   │   │       ├── auth-errors.ts
│   │   │   │       ├── client-ip.test.ts
│   │   │   │       ├── client-ip.ts
│   │   │   │       ├── rate-limiter.test.ts
│   │   │   │       └── rate-limiter.ts
│   │   │   └── test-setup.ts
│   │   ├── tsconfig.client.json
│   │   ├── tsconfig.json
│   │   ├── tsconfig.node.json
│   │   ├── tsconfig.server.json
│   │   ├── vite.config.ts
│   │   ├── vitest.config.ts
│   │   ├── worker-configuration.d.ts
│   │   ├── wrangler.canary.jsonc
│   │   └── wrangler.jsonc
│   ├── mcp-core
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── scripts
│   │   │   ├── generate-definitions.ts
│   │   │   ├── generate-otel-namespaces.ts
│   │   │   ├── measure-token-cost.ts
│   │   │   └── validate-skills-mapping.ts
│   │   ├── src
│   │   │   ├── api-client
│   │   │   │   ├── client.test.ts
│   │   │   │   ├── client.ts
│   │   │   │   ├── errors.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── schema.test.ts
│   │   │   │   ├── schema.ts
│   │   │   │   └── types.ts
│   │   │   ├── constants.ts
│   │   │   ├── errors.test.ts
│   │   │   ├── errors.ts
│   │   │   ├── internal
│   │   │   │   ├── agents
│   │   │   │   │   ├── callEmbeddedAgent.ts
│   │   │   │   │   ├── openai-provider.test.ts
│   │   │   │   │   ├── openai-provider.ts
│   │   │   │   │   └── tools
│   │   │   │   │       ├── data
│   │   │   │   │       │   ├── __namespaces.json
│   │   │   │   │       │   ├── android.json
│   │   │   │   │       │   ├── app.json
│   │   │   │   │       │   ├── artifact.json
│   │   │   │   │       │   ├── aspnetcore.json
│   │   │   │   │       │   ├── aws.json
│   │   │   │   │       │   ├── azure.json
│   │   │   │   │       │   ├── browser.json
│   │   │   │   │       │   ├── cassandra.json
│   │   │   │   │       │   ├── cicd.json
│   │   │   │   │       │   ├── CLAUDE.md
│   │   │   │   │       │   ├── client.json
│   │   │   │   │       │   ├── cloud.json
│   │   │   │   │       │   ├── cloudevents.json
│   │   │   │   │       │   ├── cloudfoundry.json
│   │   │   │   │       │   ├── code.json
│   │   │   │   │       │   ├── container.json
│   │   │   │   │       │   ├── cpu.json
│   │   │   │   │       │   ├── cpython.json
│   │   │   │   │       │   ├── database.json
│   │   │   │   │       │   ├── db.json
│   │   │   │   │       │   ├── deployment.json
│   │   │   │   │       │   ├── destination.json
│   │   │   │   │       │   ├── device.json
│   │   │   │   │       │   ├── disk.json
│   │   │   │   │       │   ├── dns.json
│   │   │   │   │       │   ├── dotnet.json
│   │   │   │   │       │   ├── elasticsearch.json
│   │   │   │   │       │   ├── enduser.json
│   │   │   │   │       │   ├── error.json
│   │   │   │   │       │   ├── faas.json
│   │   │   │   │       │   ├── feature_flags.json
│   │   │   │   │       │   ├── file.json
│   │   │   │   │       │   ├── gcp.json
│   │   │   │   │       │   ├── gen_ai.json
│   │   │   │   │       │   ├── geo.json
│   │   │   │   │       │   ├── go.json
│   │   │   │   │       │   ├── graphql.json
│   │   │   │   │       │   ├── hardware.json
│   │   │   │   │       │   ├── heroku.json
│   │   │   │   │       │   ├── host.json
│   │   │   │   │       │   ├── http.json
│   │   │   │   │       │   ├── ios.json
│   │   │   │   │       │   ├── jvm.json
│   │   │   │   │       │   ├── k8s.json
│   │   │   │   │       │   ├── linux.json
│   │   │   │   │       │   ├── log.json
│   │   │   │   │       │   ├── mcp.json
│   │   │   │   │       │   ├── messaging.json
│   │   │   │   │       │   ├── network.json
│   │   │   │   │       │   ├── nodejs.json
│   │   │   │   │       │   ├── oci.json
│   │   │   │   │       │   ├── opentracing.json
│   │   │   │   │       │   ├── os.json
│   │   │   │   │       │   ├── otel.json
│   │   │   │   │       │   ├── peer.json
│   │   │   │   │       │   ├── process.json
│   │   │   │   │       │   ├── profile.json
│   │   │   │   │       │   ├── rpc.json
│   │   │   │   │       │   ├── server.json
│   │   │   │   │       │   ├── service.json
│   │   │   │   │       │   ├── session.json
│   │   │   │   │       │   ├── signalr.json
│   │   │   │   │       │   ├── source.json
│   │   │   │   │       │   ├── system.json
│   │   │   │   │       │   ├── telemetry.json
│   │   │   │   │       │   ├── test.json
│   │   │   │   │       │   ├── thread.json
│   │   │   │   │       │   ├── tls.json
│   │   │   │   │       │   ├── url.json
│   │   │   │   │       │   ├── user.json
│   │   │   │   │       │   ├── v8js.json
│   │   │   │   │       │   ├── vcs.json
│   │   │   │   │       │   ├── webengine.json
│   │   │   │   │       │   └── zos.json
│   │   │   │   │       ├── dataset-fields.test.ts
│   │   │   │   │       ├── dataset-fields.ts
│   │   │   │   │       ├── otel-semantics.test.ts
│   │   │   │   │       ├── otel-semantics.ts
│   │   │   │   │       ├── utils.ts
│   │   │   │   │       ├── whoami.test.ts
│   │   │   │   │       └── whoami.ts
│   │   │   │   ├── constraint-helpers.test.ts
│   │   │   │   ├── constraint-helpers.ts
│   │   │   │   ├── error-handling.ts
│   │   │   │   ├── fetch-utils.test.ts
│   │   │   │   ├── fetch-utils.ts
│   │   │   │   ├── formatting.test.ts
│   │   │   │   ├── formatting.ts
│   │   │   │   ├── issue-helpers.test.ts
│   │   │   │   ├── issue-helpers.ts
│   │   │   │   ├── test-fixtures.ts
│   │   │   │   └── tool-helpers
│   │   │   │       ├── api.test.ts
│   │   │   │       ├── api.ts
│   │   │   │       ├── define.ts
│   │   │   │       ├── enhance-error.ts
│   │   │   │       ├── formatting.ts
│   │   │   │       ├── issue.ts
│   │   │   │       ├── seer.test.ts
│   │   │   │       ├── seer.ts
│   │   │   │       ├── validate-region-url.test.ts
│   │   │   │       └── validate-region-url.ts
│   │   │   ├── permissions.parseScopes.test.ts
│   │   │   ├── permissions.ts
│   │   │   ├── schema.ts
│   │   │   ├── server.ts
│   │   │   ├── skillDefinitions.json
│   │   │   ├── skillDefinitions.ts
│   │   │   ├── skills.test.ts
│   │   │   ├── skills.ts
│   │   │   ├── telem
│   │   │   │   ├── index.ts
│   │   │   │   ├── logging.ts
│   │   │   │   ├── sentry.test.ts
│   │   │   │   └── sentry.ts
│   │   │   ├── test-setup.ts
│   │   │   ├── test-utils
│   │   │   │   └── context.ts
│   │   │   ├── toolDefinitions.json
│   │   │   ├── toolDefinitions.ts
│   │   │   ├── tools
│   │   │   │   ├── analyze-issue-with-seer.test.ts
│   │   │   │   ├── analyze-issue-with-seer.ts
│   │   │   │   ├── create-dsn.test.ts
│   │   │   │   ├── create-dsn.ts
│   │   │   │   ├── create-project.test.ts
│   │   │   │   ├── create-project.ts
│   │   │   │   ├── create-team.test.ts
│   │   │   │   ├── create-team.ts
│   │   │   │   ├── find-dsns.test.ts
│   │   │   │   ├── find-dsns.ts
│   │   │   │   ├── find-organizations.test.ts
│   │   │   │   ├── find-organizations.ts
│   │   │   │   ├── find-projects.test.ts
│   │   │   │   ├── find-projects.ts
│   │   │   │   ├── find-releases.test.ts
│   │   │   │   ├── find-releases.ts
│   │   │   │   ├── find-teams.test.ts
│   │   │   │   ├── find-teams.ts
│   │   │   │   ├── get-doc.test.ts
│   │   │   │   ├── get-doc.ts
│   │   │   │   ├── get-event-attachment.test.ts
│   │   │   │   ├── get-event-attachment.ts
│   │   │   │   ├── get-issue-details.test.ts
│   │   │   │   ├── get-issue-details.ts
│   │   │   │   ├── get-trace-details.test.ts
│   │   │   │   ├── get-trace-details.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── search-docs.test.ts
│   │   │   │   ├── search-docs.ts
│   │   │   │   ├── search-events
│   │   │   │   │   ├── agent.ts
│   │   │   │   │   ├── CLAUDE.md
│   │   │   │   │   ├── config.ts
│   │   │   │   │   ├── formatters.ts
│   │   │   │   │   ├── handler.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── utils.test.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── search-events.test.ts
│   │   │   │   ├── search-issues
│   │   │   │   │   ├── agent.ts
│   │   │   │   │   ├── CLAUDE.md
│   │   │   │   │   ├── config.ts
│   │   │   │   │   ├── formatters.ts
│   │   │   │   │   ├── handler.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── README.md
│   │   │   │   ├── tools.test.ts
│   │   │   │   ├── types.ts
│   │   │   │   ├── update-issue.test.ts
│   │   │   │   ├── update-issue.ts
│   │   │   │   ├── update-project.test.ts
│   │   │   │   ├── update-project.ts
│   │   │   │   ├── use-sentry
│   │   │   │   │   ├── agent.ts
│   │   │   │   │   ├── CLAUDE.md
│   │   │   │   │   ├── config.ts
│   │   │   │   │   ├── handler.test.ts
│   │   │   │   │   ├── handler.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── tool-wrapper.test.ts
│   │   │   │   │   └── tool-wrapper.ts
│   │   │   │   ├── whoami.test.ts
│   │   │   │   └── whoami.ts
│   │   │   ├── types.ts
│   │   │   ├── utils
│   │   │   │   ├── slug-validation.test.ts
│   │   │   │   ├── slug-validation.ts
│   │   │   │   ├── url-utils.test.ts
│   │   │   │   └── url-utils.ts
│   │   │   └── version.ts
│   │   ├── tsconfig.json
│   │   ├── tsdown.config.ts
│   │   └── vitest.config.ts
│   ├── mcp-server
│   │   ├── package.json
│   │   ├── src
│   │   │   ├── cli
│   │   │   │   ├── parse.test.ts
│   │   │   │   ├── parse.ts
│   │   │   │   ├── resolve.test.ts
│   │   │   │   ├── resolve.ts
│   │   │   │   ├── types.ts
│   │   │   │   └── usage.ts
│   │   │   ├── index.ts
│   │   │   └── transports
│   │   │       └── stdio.ts
│   │   ├── tsconfig.json
│   │   └── tsdown.config.ts
│   ├── mcp-server-evals
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── bin
│   │   │   │   └── start-mock-stdio.ts
│   │   │   ├── evals
│   │   │   │   ├── autofix.eval.ts
│   │   │   │   ├── create-dsn.eval.ts
│   │   │   │   ├── create-project.eval.ts
│   │   │   │   ├── create-team.eval.ts
│   │   │   │   ├── get-issue.eval.ts
│   │   │   │   ├── get-trace-details.eval.ts
│   │   │   │   ├── list-dsns.eval.ts
│   │   │   │   ├── list-issues.eval.ts
│   │   │   │   ├── list-organizations.eval.ts
│   │   │   │   ├── list-projects.eval.ts
│   │   │   │   ├── list-releases.eval.ts
│   │   │   │   ├── list-tags.eval.ts
│   │   │   │   ├── list-teams.eval.ts
│   │   │   │   ├── search-docs.eval.ts
│   │   │   │   ├── search-events-agent.eval.ts
│   │   │   │   ├── search-events.eval.ts
│   │   │   │   ├── search-issues-agent.eval.ts
│   │   │   │   ├── search-issues.eval.ts
│   │   │   │   ├── update-issue.eval.ts
│   │   │   │   ├── update-project.eval.ts
│   │   │   │   └── utils
│   │   │   │       ├── fixtures.ts
│   │   │   │       ├── index.ts
│   │   │   │       ├── runner.ts
│   │   │   │       ├── structuredOutputScorer.ts
│   │   │   │       └── toolPredictionScorer.ts
│   │   │   └── setup-env.ts
│   │   ├── tsconfig.json
│   │   └── vitest.config.ts
│   ├── mcp-server-mocks
│   │   ├── package.json
│   │   ├── src
│   │   │   ├── fixtures
│   │   │   │   ├── autofix-state.json
│   │   │   │   ├── csp-event.json
│   │   │   │   ├── csp-issue.json
│   │   │   │   ├── default-event.json
│   │   │   │   ├── event-attachments.json
│   │   │   │   ├── event.json
│   │   │   │   ├── generic-event.json
│   │   │   │   ├── issue.json
│   │   │   │   ├── performance-event.json
│   │   │   │   ├── performance-issue.json
│   │   │   │   ├── project.json
│   │   │   │   ├── regressed-issue.json
│   │   │   │   ├── tags.json
│   │   │   │   ├── team.json
│   │   │   │   ├── trace-event.json
│   │   │   │   ├── trace-items-attributes-logs-number.json
│   │   │   │   ├── trace-items-attributes-logs-string.json
│   │   │   │   ├── trace-items-attributes-spans-number.json
│   │   │   │   ├── trace-items-attributes-spans-string.json
│   │   │   │   ├── trace-items-attributes.json
│   │   │   │   ├── trace-meta-with-nulls.json
│   │   │   │   ├── trace-meta.json
│   │   │   │   ├── trace-mixed.json
│   │   │   │   ├── trace.json
│   │   │   │   ├── unknown-event.json
│   │   │   │   └── unsupported-issue.json
│   │   │   ├── fixtures.ts
│   │   │   ├── index.ts
│   │   │   └── utils.ts
│   │   ├── tsconfig.json
│   │   └── tsdown.config.ts
│   ├── mcp-server-tsconfig
│   │   ├── package.json
│   │   ├── tsconfig.base.json
│   │   └── tsconfig.vite.json
│   ├── mcp-test-client
│   │   ├── .env.test
│   │   ├── .gitignore
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── agent.ts
│   │   │   ├── auth
│   │   │   │   ├── config.ts
│   │   │   │   └── oauth.ts
│   │   │   ├── constants.ts
│   │   │   ├── index.ts
│   │   │   ├── logger.test.ts
│   │   │   ├── logger.ts
│   │   │   ├── mcp-test-client-remote.ts
│   │   │   ├── mcp-test-client.ts
│   │   │   ├── types.ts
│   │   │   └── version.ts
│   │   ├── tsconfig.json
│   │   ├── tsdown.config.ts
│   │   └── vitest.config.ts
│   └── smoke-tests
│       ├── package.json
│       ├── src
│       │   └── smoke.test.ts
│       └── vitest.config.ts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── README.md
├── scripts
│   └── check-doc-links.mjs
├── turbo.json
└── vitest.workspace.ts
```

# Files

--------------------------------------------------------------------------------
/packages/mcp-core/src/tools/search-events/config.ts:
--------------------------------------------------------------------------------

```typescript
  1 | // Build a dataset-agnostic system prompt
  2 | export const systemPrompt = `You are a Sentry query translator. You need to:
  3 | 1. FIRST determine which dataset (spans, errors, or logs) is most appropriate for the query
  4 | 2. Query the available attributes for that dataset using the datasetAttributes tool
  5 | 3. Use the otelSemantics tool if you need OpenTelemetry semantic conventions
  6 | 4. Convert the natural language query to Sentry's search syntax (NOT SQL syntax)
  7 | 5. Decide which fields to return in the results
  8 | 
  9 | CRITICAL: Sentry does NOT use SQL syntax. Do NOT generate SQL-like queries.
 10 | 
 11 | DATASET SELECTION GUIDELINES:
 12 | - spans: Performance data, traces, AI/LLM calls, database queries, HTTP requests, token usage, costs, duration metrics, user agent data, "XYZ calls", ambiguous operations (richest attribute set)
 13 | - errors: Exceptions, crashes, error messages, stack traces, unhandled errors, browser/client errors
 14 | - logs: Log entries, log messages, severity levels, debugging information
 15 | 
 16 | For ambiguous queries like "calls using XYZ", prefer spans dataset first as it contains the most comprehensive telemetry data.
 17 | 
 18 | CRITICAL - FIELD VERIFICATION REQUIREMENT:
 19 | Before constructing ANY query, you MUST verify field availability:
 20 | 1. You CANNOT assume ANY field exists without checking - not even common ones
 21 | 2. This includes ALL fields: custom attributes, database fields, HTTP fields, AI fields, user fields, etc.
 22 | 3. Fields vary by project based on what data is being sent to Sentry
 23 | 4. Using an unverified field WILL cause your query to fail with "field not found" errors
 24 | 5. The datasetAttributes tool tells you EXACTLY which fields are available
 25 | 
 26 | TOOL USAGE GUIDELINES:
 27 | 1. Use datasetAttributes tool to discover available fields for your chosen dataset
 28 | 2. Use otelSemantics tool when you need specific OpenTelemetry semantic convention attributes
 29 | 3. Use whoami tool when queries contain "me" references for user.id or user.email fields
 30 | 4. IMPORTANT: For ambiguous terms like "user agents", "browser", "client" - use the datasetAttributes tool to find the correct field name (typically user_agent.original) instead of assuming it's related to user.id
 31 | 
 32 | CRITICAL - TOOL RESPONSE HANDLING:
 33 | All tools return responses in this format: {error?: string, result?: data}
 34 | - If 'error' is present: The tool failed - analyze the error message and potentially retry with corrections
 35 | - If 'result' is present: The tool succeeded - use the result data for your query construction
 36 | - Always check for errors before using results
 37 | 
 38 | CRITICAL - HANDLING "DISTINCT" OR "UNIQUE VALUES" QUERIES:
 39 | When user asks for "distinct", "unique", "all values of", or "what are the X" queries:
 40 | 1. This ALWAYS requires an AGGREGATE query with count() function
 41 | 2. Pattern: fields=['field_name', 'count()'] to show distinct values with counts
 42 | 3. Sort by "-count()" to show most common values first
 43 | 4. Use datasetAttributes tool to verify the field exists before constructing query
 44 | 5. Examples:
 45 |    - "distinct categories" → fields=['category.name', 'count()'], sort='-count()'
 46 |    - "unique types" → fields=['item.type', 'count()'], sort='-count()'
 47 | 
 48 | CRITICAL - TRAFFIC/VOLUME/COUNT QUERIES:
 49 | When user asks about "traffic", "volume", "how much", "how many" (without specific metrics):
 50 | 1. This ALWAYS requires an AGGREGATE query with count() function
 51 | 2. For total counts: fields=['count()']
 52 | 3. For grouped counts: fields=['grouping_field', 'count()']
 53 | 4. Always include timeRange for period-specific queries
 54 | 5. Examples:
 55 |    - "how much traffic in last 30 days" → fields=['count()'], timeRange: {"statsPeriod": "30d"}
 56 |    - "traffic on mcp-server" → query: "project:mcp-server", fields=['count()']
 57 | 
 58 | CRITICAL - HANDLING "ME" REFERENCES:
 59 | - If the query contains "me", "my", "myself", or "affecting me" in the context of user.id or user.email fields, use the whoami tool to get the user's ID and email
 60 | - For assignedTo fields, you can use "me" directly without translation (e.g., assignedTo:me works as-is)
 61 | - After calling whoami, replace "me" references with the actual user.id or user.email values
 62 | - If whoami fails, return an error explaining the issue
 63 | 
 64 | QUERY MODES:
 65 | 1. INDIVIDUAL EVENTS (default): Returns raw event data
 66 |    - Used when fields contain no function() calls
 67 |    - Include recommended fields plus any user-requested fields
 68 | 
 69 | 2. AGGREGATE QUERIES: Grouping and aggregation (NOT SQL)
 70 |    - Activated when ANY field contains a function() call
 71 |    - Fields should ONLY include: aggregate functions + groupBy fields
 72 |    - Automatically groups by ALL non-function fields
 73 |    - For aggregate queries, ONLY include the aggregate functions and groupBy fields - do NOT include default fields like timestamp, id, etc.
 74 |    - You SHOULD sort aggregate results by "-function_name()" for descending order (highest values first)
 75 |    - For equations in aggregate queries: You SHOULD use "-equation|..." prefix unless user wants lowest values
 76 |    - When user asks "how many total", "sum of", or similar: They want the highest/total value, use descending sort
 77 | 
 78 | CRITICAL LIMITATION - TIME SERIES NOT SUPPORTED:
 79 | - Queries asking for data "over time", "by hour", "by day", "time series", or similar temporal groupings are NOT currently supported
 80 | - If user asks for "X over time", return an error explaining: "Time series aggregations are not currently supported."
 81 | 
 82 | CRITICAL - DO NOT USE SQL SYNTAX:
 83 | - NEVER use SQL functions like yesterday(), today(), now(), IS NOT NULL, IS NULL
 84 | - NEVER use SQL date functions - use timeRange parameter instead
 85 | - For "yesterday": Use timeRange: {"statsPeriod": "24h"}, NOT timestamp >= yesterday()
 86 | - For field existence: Use has:field_name, NOT field_name IS NOT NULL
 87 | - For field absence: Use !has:field_name, NOT field_name IS NULL
 88 | 
 89 | MATHEMATICAL QUERY PATTERNS:
 90 | When user asks mathematical questions like "how many X", "total Y used", "sum of Z":
 91 | - Identify the appropriate dataset based on context
 92 | - Use datasetAttributes tool to find available numeric fields
 93 | - Use sum() function for totals, avg() for averages, count() for counts
 94 | - For time-based queries ("today", "yesterday", "this week"), use timeRange parameter
 95 | - For "total" or "how many" questions: Users typically want highest values first (descending sort)
 96 | 
 97 | DERIVED METRICS AND CALCULATIONS (SPANS ONLY):
 98 | When user asks for calculated metrics, ratios, or conversions:
 99 | - Use equation fields with "equation|" prefix
100 | - Examples:
101 |   - "duration in milliseconds" → fields: ["equation|avg(span.duration) * 1000"], sort: "-equation|avg(span.duration) * 1000"
102 |   - "combined metric total" → fields: ["equation|sum(metric.a) + sum(metric.b)"], sort: "-equation|sum(metric.a) + sum(metric.b)"
103 |   - "error rate percentage" → fields: ["equation|failure_rate() * 100"], sort: "-equation|failure_rate() * 100"
104 |   - "events per second" → fields: ["equation|count() / 3600"], sort: "-equation|count() / 3600"
105 | - IMPORTANT: Equations are ONLY supported in the spans dataset, NOT in errors or logs
106 | - IMPORTANT: When sorting by equations, use "-equation|..." for descending order (highest values first)
107 | 
108 | SORTING RULES (CRITICAL - YOU MUST ALWAYS SPECIFY A SORT):
109 | 1. CRITICAL: Sort MUST go in the separate "sort" field, NEVER in the "query" field
110 |    - WRONG: query: "level:error sort:-timestamp" ← Sort syntax in query field is FORBIDDEN
111 |    - CORRECT: query: "level:error", sort: "-timestamp" ← Sort in separate field
112 | 
113 | 2. DEFAULT SORTING:
114 |    - errors dataset: Use "-timestamp" (newest first)
115 |    - spans dataset: Use "-span.duration" (slowest first)  
116 |    - logs dataset: Use "-timestamp" (newest first)
117 | 
118 | 3. SORTING SYNTAX:
119 |    - Use "-" prefix for descending order (e.g., "-timestamp" for newest first)
120 |    - Use field name without prefix for ascending order
121 |    - For aggregate queries: sort by aggregate function results (e.g., "-count()" for highest count first)
122 |    - For equation fields: You SHOULD use "-equation|..." for descending order (e.g., "-equation|sum(field1) + sum(field2)")
123 |    - Only omit the "-" prefix if the user clearly wants lowest values first (rare)
124 | 
125 | 4. IMPORTANT SORTING REQUIREMENTS:
126 |    - YOU MUST ALWAYS INCLUDE A SORT PARAMETER
127 |    - CRITICAL: The field you sort by MUST be included in your fields array
128 |    - If sorting by "-timestamp", include "timestamp" in fields
129 |    - If sorting by "-count()", include "count()" in fields
130 |    - This is MANDATORY - Sentry will reject queries where sort field is not in the selected fields
131 | 
132 | YOUR RESPONSE FORMAT:
133 | Return a JSON object with these fields:
134 | - "dataset": Which dataset you determined to use ("spans", "errors", or "logs")
135 | - "query": The Sentry query string for filtering results (use empty string "" for no filters)
136 | - "fields": Array of field names to return in results
137 |   - For individual event queries: OPTIONAL (will use recommended fields if not provided)
138 |   - For aggregate queries: REQUIRED (must include aggregate functions AND any groupBy fields)
139 | - "sort": Sort parameter for results (REQUIRED - YOU MUST ALWAYS SPECIFY THIS)
140 | - "timeRange": Time range parameters (optional)
141 |   - Relative: {"statsPeriod": "24h"} for last 24 hours, "7d" for last 7 days, etc.
142 |   - Absolute: {"start": "2025-06-19T07:00:00", "end": "2025-06-20T06:59:59"} for specific date ranges
143 | 
144 | CORRECT QUERY PATTERNS (FOLLOW THESE):
145 | - For field existence: Use has:field_name (NOT field_name IS NOT NULL)
146 | - For field absence: Use !has:field_name (NOT field_name IS NULL)
147 | - For time periods: Use timeRange parameter (NOT SQL date functions)
148 | - Example: "items processed yesterday" → query: "has:item.processed", timeRange: {"statsPeriod": "24h"}
149 | 
150 | PROCESS:
151 | 1. Analyze the user's query
152 | 2. Determine appropriate dataset
153 | 3. Use datasetAttributes tool to discover available fields
154 | 4. Use otelSemantics tool if needed for OpenTelemetry attributes
155 | 5. Construct the final query with proper fields and sort parameters
156 | 
157 | COMMON ERRORS TO AVOID:
158 | - Using SQL syntax (IS NOT NULL, IS NULL, yesterday(), today(), etc.) - Use has: operator and timeRange instead
159 | - Using numeric functions (sum, avg, min, max, percentiles) on non-numeric fields
160 | - Using incorrect field names (use the otelSemantics tool to look up correct names)
161 | - Missing required fields in the fields array for aggregate queries
162 | - Invalid sort parameter not included in fields array
163 | - For field existence: Use has:field_name (NOT field_name IS NOT NULL)
164 | - For field absence: Use !has:field_name (NOT field_name IS NULL)
165 | - For time periods: Use timeRange parameter (NOT SQL date functions like yesterday())`;
166 | 
167 | // Base fields common to all datasets
168 | export const BASE_COMMON_FIELDS = {
169 |   project: "Project slug",
170 |   timestamp: "When the event occurred",
171 |   environment: "Environment (production, staging, development)",
172 |   release: "Release version",
173 |   platform: "Platform (javascript, python, etc.)",
174 |   "user.id": "User ID",
175 |   "user.email": "User email",
176 |   "sdk.name": "SDK name",
177 |   "sdk.version": "SDK version",
178 | };
179 | 
180 | // Known numeric fields for each dataset
181 | export const NUMERIC_FIELDS: Record<string, Set<string>> = {
182 |   spans: new Set([
183 |     "span.duration",
184 |     "span.self_time",
185 |     "transaction.duration",
186 |     "http.status_code",
187 |     "gen_ai.usage.input_tokens",
188 |     "gen_ai.usage.output_tokens",
189 |     "gen_ai.request.max_tokens",
190 |   ]),
191 |   errors: new Set([
192 |     // Most error fields are strings/categories
193 |     "stack.lineno",
194 |   ]),
195 |   logs: new Set(["severity_number", "sentry.observed_timestamp_nanos"]),
196 | };
197 | 
198 | // Dataset-specific field definitions
199 | export const DATASET_FIELDS = {
200 |   spans: {
201 |     // Span-specific fields
202 |     "span.op": "Span operation type (e.g., http.client, db.query, cache.get)",
203 |     "span.description": "Detailed description of the span operation",
204 |     "span.duration": "Duration of the span in milliseconds",
205 |     "span.status": "Span status (ok, cancelled, unknown, etc.)",
206 |     "span.self_time": "Time spent in this span excluding child spans",
207 | 
208 |     // Transaction fields
209 |     transaction: "Transaction name/route",
210 |     "transaction.duration": "Total transaction duration in milliseconds",
211 |     "transaction.op": "Transaction operation type",
212 |     "transaction.status": "Transaction status",
213 |     is_transaction: "Whether this span is a transaction (true/false)",
214 | 
215 |     // Trace fields
216 |     trace: "Trace ID",
217 |     "trace.span_id": "Span ID within the trace",
218 |     "trace.parent_span_id": "Parent span ID",
219 | 
220 |     // HTTP fields
221 |     "http.method": "HTTP method (GET, POST, etc.)",
222 |     "http.status_code": "HTTP response status code",
223 |     "http.url": "Full HTTP URL",
224 | 
225 |     // Database fields
226 |     "db.system": "Database system (postgresql, mysql, etc.)",
227 |     "db.operation": "Database operation (SELECT, INSERT, etc.)",
228 | 
229 |     // OpenTelemetry attribute namespaces for semantic queries
230 |     // Use has:namespace.* to find spans with any attribute in that namespace
231 |     // GenAI namespace (gen_ai.*) - for AI/LLM/Agent calls
232 |     "gen_ai.system": "AI system (e.g., anthropic, openai)",
233 |     "gen_ai.request.model": "Model name (e.g., claude-3-5-sonnet-20241022)",
234 |     "gen_ai.operation.name": "Operation type (e.g., chat, completion)",
235 |     "gen_ai.usage.input_tokens": "Number of input tokens (numeric)",
236 |     "gen_ai.usage.output_tokens": "Number of output tokens (numeric)",
237 | 
238 |     // MCP namespace (mcp.*) - for Model Context Protocol tool calls
239 |     "mcp.tool.name": "Tool name (e.g., search_issues, search_events)",
240 |     "mcp.session.id": "MCP session identifier",
241 | 
242 |     // Aggregate functions (SPANS dataset only - require numeric fields except count/count_unique)
243 |     "count()": "Count of spans",
244 |     "count_unique(field)": "Count of unique values, e.g. count_unique(user.id)",
245 |     "avg(field)": "Average of numeric field, e.g. avg(span.duration)",
246 |     "sum(field)": "Sum of numeric field, e.g. sum(span.self_time)",
247 |     "min(field)": "Minimum of numeric field, e.g. min(span.duration)",
248 |     "max(field)": "Maximum of numeric field, e.g. max(span.duration)",
249 |     "p50(field)": "50th percentile (median), e.g. p50(span.duration)",
250 |     "p75(field)": "75th percentile, e.g. p75(span.duration)",
251 |     "p90(field)": "90th percentile, e.g. p90(span.duration)",
252 |     "p95(field)": "95th percentile, e.g. p95(span.duration)",
253 |     "p99(field)": "99th percentile, e.g. p99(span.duration)",
254 |     "p100(field)": "100th percentile (max), e.g. p100(span.duration)",
255 |     "epm()": "Events per minute rate",
256 |     "failure_rate()": "Percentage of failed spans",
257 |   },
258 |   errors: {
259 |     // Error-specific fields
260 |     message: "Error message",
261 |     level: "Error level (error, warning, info, debug)",
262 |     "error.type": "Error type/exception class",
263 |     "error.value": "Error value/description",
264 |     "error.handled": "Whether the error was handled (true/false)",
265 |     culprit: "Code location that caused the error",
266 |     title: "Error title/grouping",
267 | 
268 |     // Stack trace fields
269 |     "stack.filename": "File where error occurred",
270 |     "stack.function": "Function where error occurred",
271 |     "stack.module": "Module where error occurred",
272 |     "stack.abs_path": "Absolute path to file",
273 | 
274 |     // Additional context fields
275 |     "os.name": "Operating system name",
276 |     "browser.name": "Browser name",
277 |     "device.family": "Device family",
278 | 
279 |     // Aggregate functions (ERRORS dataset only)
280 |     "count()": "Count of error events",
281 |     "count_unique(field)": "Count of unique values, e.g. count_unique(user.id)",
282 |     "count_if(field,equals,value)":
283 |       "Conditional count, e.g. count_if(error.handled,equals,false)",
284 |     "last_seen()": "Most recent timestamp of the group",
285 |     "eps()": "Events per second rate",
286 |     "epm()": "Events per minute rate",
287 |   },
288 |   logs: {
289 |     // Log-specific fields
290 |     message: "Log message",
291 |     severity: "Log severity level",
292 |     severity_number: "Numeric severity level",
293 |     "sentry.item_id": "Sentry item ID",
294 |     "sentry.observed_timestamp_nanos": "Observed timestamp in nanoseconds",
295 | 
296 |     // Trace context
297 |     trace: "Trace ID",
298 | 
299 |     // Aggregate functions (LOGS dataset only - require numeric fields except count/count_unique)
300 |     "count()": "Count of log entries",
301 |     "count_unique(field)": "Count of unique values, e.g. count_unique(user.id)",
302 |     "avg(field)": "Average of numeric field, e.g. avg(severity_number)",
303 |     "sum(field)": "Sum of numeric field",
304 |     "min(field)": "Minimum of numeric field",
305 |     "max(field)": "Maximum of numeric field",
306 |     "p50(field)": "50th percentile (median)",
307 |     "p75(field)": "75th percentile",
308 |     "p90(field)": "90th percentile",
309 |     "p95(field)": "95th percentile",
310 |     "p99(field)": "99th percentile",
311 |     "p100(field)": "100th percentile (max)",
312 |     "epm()": "Events per minute rate",
313 |   },
314 | };
315 | 
316 | // Dataset-specific rules and examples
317 | export const DATASET_CONFIGS = {
318 |   errors: {
319 |     rules: `- For errors, focus on: message, level, error.type, error.handled
320 | - Use level field for severity (error, warning, info, debug)
321 | - Use error.handled:false for unhandled exceptions/crashes
322 | - For filename searches: Use stack.filename for suffix-based search (e.g., stack.filename:"**/index.js" or stack.filename:"**/components/Button.tsx")
323 | - When searching for errors in specific files, prefer including the parent folder to avoid ambiguity (e.g., stack.filename:"**/components/index.js" instead of just stack.filename:"**/index.js")`,
324 |     examples: `- "null pointer exceptions" → 
325 |   {
326 |     "query": "error.type:\\"NullPointerException\\" OR message:\\"*null pointer*\\"",
327 |     "fields": ["issue", "title", "project", "timestamp", "level", "message", "error.type", "culprit"],
328 |     "sort": "-timestamp"
329 |   }
330 | - "unhandled errors in production" → 
331 |   {
332 |     "query": "error.handled:false AND environment:production",
333 |     "fields": ["issue", "title", "project", "timestamp", "level", "message", "error.type", "culprit", "error.handled", "environment"],
334 |     "sort": "-timestamp"
335 |   }
336 | - "database connection errors" → 
337 |   {
338 |     "query": "message:\\"*database*\\" AND message:\\"*connection*\\" AND level:error",
339 |     "fields": ["issue", "title", "project", "timestamp", "level", "message", "error.type", "culprit"],
340 |     "sort": "-timestamp"
341 |   }
342 | - "show me user emails for authentication failures" → 
343 |   {
344 |     "query": "message:\\"*auth*\\" AND (message:\\"*failed*\\" OR message:\\"*denied*\\")",
345 |     "fields": ["issue", "title", "project", "timestamp", "level", "message", "error.type", "culprit", "user.email"],
346 |     "sort": "-timestamp"
347 |   }
348 | - "errors in Button.tsx file" → 
349 |   {
350 |     "query": "stack.filename:\\"**/Button.tsx\\"",
351 |     "fields": ["issue", "title", "project", "timestamp", "level", "message", "error.type", "culprit", "stack.filename"],
352 |     "sort": "-timestamp"
353 |   }
354 | - "count errors by type in production" → 
355 |   {
356 |     "query": "environment:production",
357 |     "fields": ["error.type", "count()", "last_seen()"],
358 |     "sort": "-count()"
359 |   }
360 | - "most common errors last 24h" → 
361 |   {
362 |     "query": "level:error",
363 |     "fields": ["title", "error.type", "count()"],
364 |     "sort": "-count()"
365 |   }
366 | - "unhandled errors rate by project" → 
367 |   {
368 |     "query": "",
369 |     "fields": ["project", "count()", "count_if(error.handled,equals,false)", "epm()"],
370 |     "sort": "-count()"
371 |   }
372 | - "errors in the last hour" → 
373 |   {
374 |     "query": "",
375 |     "fields": ["issue", "title", "project", "timestamp", "level", "message", "error.type", "culprit"],
376 |     "sort": "-timestamp",
377 |     "timeRange": {"statsPeriod": "1h"}
378 |   }
379 | - "database errors between June 19-20" → 
380 |   {
381 |     "query": "message:\\"*database*\\"",
382 |     "fields": ["issue", "title", "project", "timestamp", "level", "message", "error.type", "culprit"],
383 |     "sort": "-timestamp",
384 |     "timeRange": {"start": "2025-06-19T00:00:00", "end": "2025-06-20T23:59:59"}
385 |   }
386 | - "unique users affected by errors" → 
387 |   {
388 |     "query": "level:error",
389 |     "fields": ["error.type", "count()", "count_unique(user.id)"],
390 |     "sort": "-count_unique(user.id)"
391 |   }
392 | - "what is the most common error" → 
393 |   {
394 |     "query": "",
395 |     "fields": ["title", "count()"],
396 |     "sort": "-count()"
397 |   }
398 | - "errors by browser" → 
399 |   {
400 |     "query": "has:user_agent.original",
401 |     "fields": ["user_agent.original", "count()"],
402 |     "sort": "-count()"
403 |   }
404 | - "which user agents have the most errors" → 
405 |   {
406 |     "query": "level:error AND has:user_agent.original",
407 |     "fields": ["user_agent.original", "count()", "count_unique(user.id)"],
408 |     "sort": "-count()"
409 |   }`,
410 |   },
411 |   logs: {
412 |     rules: `- For logs, focus on: message, severity, severity_number
413 | - Use severity field for log levels (fatal, error, warning, info, debug, trace)
414 | - severity_number is numeric (21=fatal, 17=error, 13=warning, 9=info, 5=debug, 1=trace)
415 | - IMPORTANT: For time-based filtering in logs, do NOT use timestamp filters in the query
416 | - Instead, time filtering for logs is handled by the statsPeriod parameter (not part of the query string)
417 | - Keep your query focused on message content, severity levels, and other attributes only
418 | - When user asks for "error logs", interpret this as logs with severity:error`,
419 |     examples: `- "warning logs about memory" → 
420 |   {
421 |     "query": "severity:warning AND message:\\"*memory*\\"",
422 |     "fields": ["timestamp", "project", "message", "severity", "trace"],
423 |     "sort": "-timestamp"
424 |   }
425 | - "error logs from database" → 
426 |   {
427 |     "query": "severity:error AND message:\\"*database*\\"",
428 |     "fields": ["timestamp", "project", "message", "severity", "trace"],
429 |     "sort": "-timestamp"
430 |   }
431 | - "show me error logs with user context" → 
432 |   {
433 |     "query": "severity:error",
434 |     "fields": ["timestamp", "project", "message", "severity", "trace", "user.id", "user.email"],
435 |     "sort": "-timestamp"
436 |   }
437 | - "what is the most common log" → 
438 |   {
439 |     "query": "",
440 |     "fields": ["message", "count()"],
441 |     "sort": "-count()"
442 |   }
443 | - "most common error logs" → 
444 |   {
445 |     "query": "severity:error",
446 |     "fields": ["message", "count()"],
447 |     "sort": "-count()"
448 |   }
449 | - "count logs by severity" → 
450 |   {
451 |     "query": "",
452 |     "fields": ["severity", "count()"],
453 |     "sort": "-count()"
454 |   }
455 | - "log volume by project" → 
456 |   {
457 |     "query": "",
458 |     "fields": ["project", "count()", "epm()"],
459 |     "sort": "-count()"
460 |   }`,
461 |   },
462 |   spans: {
463 |     rules: `- For traces/spans, focus on: span.op, span.description, span.duration, transaction
464 | - Use is_transaction:true for transaction spans only
465 | - Use span.duration for performance queries (value is in milliseconds)
466 | - IMPORTANT: Use has: queries for attribute-based filtering instead of span.op patterns:
467 |   - For HTTP requests: use "has:request.url" instead of "span.op:http*"
468 |   - For database queries: use "has:db.statement" or "has:db.system" instead of "span.op:db*"
469 |   - For AI/LLM/Agent calls: use "has:gen_ai.system" or "has:gen_ai.request.model" (OpenTelemetry GenAI semantic conventions)
470 |   - For MCP tool calls: use "has:mcp.tool.name" (Model Context Protocol semantic conventions)
471 |   - This approach is more flexible and captures all relevant spans regardless of their operation type
472 | 
473 | OpenTelemetry Semantic Conventions (2025 Stable):
474 | Core Namespaces:
475 | - gen_ai.*: GenAI attributes for AI/LLM/Agent calls (system, request.model, operation.name, usage.*)
476 | - db.*: Database attributes (system, statement, operation, name) - STABLE
477 | - http.*: HTTP attributes (method, status_code, url, request.*, response.*) - STABLE
478 | - rpc.*: RPC attributes (system, service, method, grpc.*)
479 | - messaging.*: Messaging attributes (system, operation, destination.*)
480 | - faas.*: Function as a Service attributes (name, version, runtime)
481 | - cloud.*: Cloud provider attributes (provider, region, zone)
482 | - k8s.*: Kubernetes attributes (namespace, pod, container, node)
483 | - host.*: Host attributes (name, type, arch, os.*)
484 | - service.*: Service attributes (name, version, instance.id)
485 | - process.*: Process attributes (pid, command, runtime.*)
486 | 
487 | Custom Namespaces:
488 | - mcp.*: Model Context Protocol attributes for MCP tool calls (tool.name, session.id, transport)
489 | 
490 | Query Patterns:
491 | - Use has:namespace.* to find spans with any attribute in that namespace
492 | - Most common: has:gen_ai.system (agent calls), has:mcp.tool.name (MCP tools), has:db.statement (database), has:http.method (HTTP)`,
493 |     examples: `- "database queries" → 
494 |   {
495 |     "query": "has:db.statement",
496 |     "fields": ["span.op", "span.description", "span.duration", "transaction", "timestamp", "project", "trace", "db.system", "db.statement"],
497 |     "sort": "-span.duration"
498 |   }
499 | - "slow API calls over 5 seconds" → 
500 |   {
501 |     "query": "has:request.url AND span.duration:>5000",
502 |     "fields": ["span.op", "span.description", "span.duration", "transaction", "timestamp", "project", "trace", "request.url", "request.method", "span.status_code"],
503 |     "sort": "-span.duration"
504 |   }
505 | - "show me database queries with their SQL" → 
506 |   {
507 |     "query": "has:db.statement",
508 |     "fields": ["span.op", "span.description", "span.duration", "transaction", "timestamp", "project", "trace", "db.system", "db.statement"],
509 |     "sort": "-span.duration"
510 |   }
511 | - "average response time by endpoint" → 
512 |   {
513 |     "query": "is_transaction:true",
514 |     "fields": ["transaction", "count()", "avg(span.duration)", "p95(span.duration)"],
515 |     "sort": "-avg(span.duration)"
516 |   }
517 | - "slowest database queries by p95" → 
518 |   {
519 |     "query": "has:db.statement",
520 |     "fields": ["db.statement", "count()", "p50(span.duration)", "p95(span.duration)", "max(span.duration)"],
521 |     "sort": "-p95(span.duration)"
522 |   }
523 | - "API calls in the last 30 minutes" → 
524 |   {
525 |     "query": "has:request.url",
526 |     "fields": ["id", "span.op", "span.description", "span.duration", "transaction", "timestamp", "project", "trace", "request.url", "request.method"],
527 |     "sort": "-timestamp",
528 |     "timeRange": {"statsPeriod": "30m"}
529 |   }
530 | - "most common transaction" → 
531 |   {
532 |     "query": "is_transaction:true",
533 |     "fields": ["transaction", "count()"],
534 |     "sort": "-count()"
535 |   }
536 | - "top 10 tool call spans by usage" → 
537 |   {
538 |     "query": "has:mcp.tool.name",
539 |     "fields": ["mcp.tool.name", "count()"],
540 |     "sort": "-count()"
541 |   }
542 | - "top 10 agent call spans by usage" → 
543 |   {
544 |     "query": "has:gen_ai.system",
545 |     "fields": ["gen_ai.system", "gen_ai.request.model", "count()"],
546 |     "sort": "-count()"
547 |   }
548 | - "slowest AI/LLM calls" → 
549 |   {
550 |     "query": "has:gen_ai.request.model",
551 |     "fields": ["gen_ai.system", "gen_ai.request.model", "span.duration", "transaction", "timestamp", "project", "trace", "gen_ai.operation.name"],
552 |     "sort": "-span.duration"
553 |   }
554 | - "agent calls by model usage" → 
555 |   {
556 |     "query": "has:gen_ai.request.model",
557 |     "fields": ["gen_ai.request.model", "count()"],
558 |     "sort": "-count()"
559 |   }
560 | - "average agent call duration by model" → 
561 |   {
562 |     "query": "has:gen_ai.request.model",
563 |     "fields": ["gen_ai.request.model", "count()", "avg(span.duration)", "p95(span.duration)"],
564 |     "sort": "-avg(span.duration)"
565 |   }
566 | - "token usage by AI system" → 
567 |   {
568 |     "query": "has:gen_ai.usage.input_tokens",
569 |     "fields": ["gen_ai.system", "sum(gen_ai.usage.input_tokens)", "sum(gen_ai.usage.output_tokens)", "count()"],
570 |     "sort": "-sum(gen_ai.usage.input_tokens)"
571 |   }
572 | - "how many tokens used today" → 
573 |   {
574 |     "query": "has:gen_ai.usage.input_tokens",
575 |     "fields": ["sum(gen_ai.usage.input_tokens)", "sum(gen_ai.usage.output_tokens)", "count()"],
576 |     "sort": "-sum(gen_ai.usage.input_tokens)",
577 |     "timeRange": {"statsPeriod": "24h"}
578 |   }
579 | - "average response time in milliseconds" → 
580 |   {
581 |     "query": "is_transaction:true",
582 |     "fields": ["transaction", "equation|avg(span.duration) * 1000"],
583 |     "sort": "-equation|avg(span.duration) * 1000",
584 |     "timeRange": {"statsPeriod": "24h"}
585 |   }
586 | - "total input tokens by model" → 
587 |   {
588 |     "query": "has:gen_ai.usage.input_tokens",
589 |     "fields": ["gen_ai.request.model", "sum(gen_ai.usage.input_tokens)", "count()"],
590 |     "sort": "-sum(gen_ai.usage.input_tokens)"
591 |   }
592 | - "tokens used this week" → 
593 |   {
594 |     "query": "has:gen_ai.usage.input_tokens",
595 |     "fields": ["sum(gen_ai.usage.input_tokens)", "sum(gen_ai.usage.output_tokens)", "count()"],
596 |     "sort": "-sum(gen_ai.usage.input_tokens)",
597 |     "timeRange": {"statsPeriod": "7d"}
598 |   }
599 | - "which user agents have the most tool calls yesterday" → 
600 |   {
601 |     "query": "has:mcp.tool.name AND has:user_agent.original",
602 |     "fields": ["user_agent.original", "count()"],
603 |     "sort": "-count()",
604 |     "timeRange": {"statsPeriod": "24h"}
605 |   }
606 | - "top 10 browsers by API calls" → 
607 |   {
608 |     "query": "has:http.method AND has:user_agent.original",
609 |     "fields": ["user_agent.original", "count()"],
610 |     "sort": "-count()"
611 |   }
612 | - "most common clients making database queries" → 
613 |   {
614 |     "query": "has:db.statement AND has:user_agent.original",
615 |     "fields": ["user_agent.original", "count()", "avg(span.duration)"],
616 |     "sort": "-count()"
617 |   }`,
618 |   },
619 | };
620 | 
621 | // Define recommended fields for each dataset
622 | export const RECOMMENDED_FIELDS = {
623 |   errors: {
624 |     basic: [
625 |       "issue",
626 |       "title",
627 |       "project",
628 |       "timestamp",
629 |       "level",
630 |       "message",
631 |       "error.type",
632 |       "culprit",
633 |     ],
634 |     description:
635 |       "Basic error information including issue ID, title, timestamp, severity, and location",
636 |   },
637 |   logs: {
638 |     basic: ["timestamp", "project", "message", "severity", "trace"],
639 |     description: "Essential log entry information",
640 |   },
641 |   spans: {
642 |     basic: [
643 |       "id",
644 |       "span.op",
645 |       "span.description",
646 |       "span.duration",
647 |       "transaction",
648 |       "timestamp",
649 |       "project",
650 |       "trace",
651 |     ],
652 |     description:
653 |       "Core span/trace information including span ID, operation, duration, and trace context",
654 |   },
655 | };
656 | 
```

--------------------------------------------------------------------------------
/packages/mcp-cloudflare/src/server/lib/approval-dialog.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import type {
  2 |   AuthRequest,
  3 |   ClientInfo,
  4 | } from "@cloudflare/workers-oauth-provider";
  5 | import { logError, logIssue, logWarn } from "@sentry/mcp-core/telem/logging";
  6 | import { sanitizeHtml } from "./html-utils";
  7 | import skillDefinitions, {
  8 |   type SkillDefinition,
  9 | } from "@sentry/mcp-core/skillDefinitions";
 10 | import {
 11 |   signState,
 12 |   verifyAndParseState,
 13 |   type OAuthState,
 14 | } from "../oauth/state";
 15 | 
 16 | const COOKIE_NAME = "mcp-approved-clients";
 17 | const ONE_YEAR_IN_SECONDS = 31536000;
 18 | /**
 19 |  * Imports a secret key string for HMAC-SHA256 signing.
 20 |  * @param secret - The raw secret key string.
 21 |  * @returns A promise resolving to the CryptoKey object.
 22 |  */
 23 | async function importKey(secret: string): Promise<CryptoKey> {
 24 |   if (!secret) {
 25 |     throw new Error(
 26 |       "COOKIE_SECRET is not defined. A secret key is required for signing cookies.",
 27 |     );
 28 |   }
 29 |   const enc = new TextEncoder();
 30 |   return crypto.subtle.importKey(
 31 |     "raw",
 32 |     enc.encode(secret),
 33 |     { name: "HMAC", hash: "SHA-256" },
 34 |     false, // not extractable
 35 |     ["sign", "verify"], // key usages
 36 |   );
 37 | }
 38 | 
 39 | /**
 40 |  * Signs data using HMAC-SHA256.
 41 |  * @param key - The CryptoKey for signing.
 42 |  * @param data - The string data to sign.
 43 |  * @returns A promise resolving to the signature as a hex string.
 44 |  */
 45 | async function signData(key: CryptoKey, data: string): Promise<string> {
 46 |   const enc = new TextEncoder();
 47 |   const signatureBuffer = await crypto.subtle.sign(
 48 |     "HMAC",
 49 |     key,
 50 |     enc.encode(data),
 51 |   );
 52 |   // Convert ArrayBuffer to hex string
 53 |   return Array.from(new Uint8Array(signatureBuffer))
 54 |     .map((b) => b.toString(16).padStart(2, "0"))
 55 |     .join("");
 56 | }
 57 | 
 58 | /**
 59 |  * Verifies an HMAC-SHA256 signature.
 60 |  * @param key - The CryptoKey for verification.
 61 |  * @param signatureHex - The signature to verify (hex string).
 62 |  * @param data - The original data that was signed.
 63 |  * @returns A promise resolving to true if the signature is valid, false otherwise.
 64 |  */
 65 | async function verifySignature(
 66 |   key: CryptoKey,
 67 |   signatureHex: string,
 68 |   data: string,
 69 | ): Promise<boolean> {
 70 |   const enc = new TextEncoder();
 71 |   try {
 72 |     // Convert hex signature back to ArrayBuffer
 73 |     const signatureBytes = new Uint8Array(
 74 |       signatureHex.match(/.{1,2}/g)!.map((byte) => Number.parseInt(byte, 16)),
 75 |     );
 76 |     return await crypto.subtle.verify(
 77 |       "HMAC",
 78 |       key,
 79 |       signatureBytes.buffer,
 80 |       enc.encode(data),
 81 |     );
 82 |   } catch (error) {
 83 |     logError(error, {
 84 |       loggerScope: ["cloudflare", "approval-dialog"],
 85 |       extra: {
 86 |         message: "Error verifying signature",
 87 |       },
 88 |     });
 89 |     return false;
 90 |   }
 91 | }
 92 | 
 93 | /**
 94 |  * Parses the signed cookie and verifies its integrity.
 95 |  * @param cookieHeader - The value of the Cookie header from the request.
 96 |  * @param secret - The secret key used for signing.
 97 |  * @returns A promise resolving to the list of approved client IDs if the cookie is valid, otherwise null.
 98 |  */
 99 | async function getApprovedClientsFromCookie(
100 |   cookieHeader: string | null,
101 |   secret: string,
102 | ): Promise<string[] | null> {
103 |   if (!cookieHeader) return null;
104 | 
105 |   const cookies = cookieHeader.split(";").map((c) => c.trim());
106 |   const targetCookie = cookies.find((c) => c.startsWith(`${COOKIE_NAME}=`));
107 | 
108 |   if (!targetCookie) return null;
109 | 
110 |   const cookieValue = targetCookie.substring(COOKIE_NAME.length + 1);
111 |   const parts = cookieValue.split(".");
112 | 
113 |   if (parts.length !== 2) {
114 |     logWarn("Invalid approval cookie format", {
115 |       loggerScope: ["cloudflare", "approval-dialog"],
116 |     });
117 |     return null; // Invalid format
118 |   }
119 | 
120 |   const [signatureHex, base64Payload] = parts;
121 |   const payload = atob(base64Payload); // Assuming payload is base64 encoded JSON string
122 | 
123 |   const key = await importKey(secret);
124 |   const isValid = await verifySignature(key, signatureHex, payload);
125 | 
126 |   if (!isValid) {
127 |     logWarn("Approval cookie signature verification failed", {
128 |       loggerScope: ["cloudflare", "approval-dialog"],
129 |     });
130 |     return null; // Signature invalid
131 |   }
132 | 
133 |   try {
134 |     const approvedClients = JSON.parse(payload);
135 |     if (!Array.isArray(approvedClients)) {
136 |       logWarn("Approval cookie payload is not an array", {
137 |         loggerScope: ["cloudflare", "approval-dialog"],
138 |       });
139 |       return null; // Payload isn't an array
140 |     }
141 |     // Ensure all elements are strings
142 |     if (!approvedClients.every((item) => typeof item === "string")) {
143 |       logWarn("Approval cookie payload contains non-string elements", {
144 |         loggerScope: ["cloudflare", "approval-dialog"],
145 |       });
146 |       return null;
147 |     }
148 |     return approvedClients as string[];
149 |   } catch (e) {
150 |     logIssue(new Error(`Error parsing cookie payload: ${e}`, { cause: e }));
151 |     return null; // JSON parsing failed
152 |   }
153 | }
154 | 
155 | /**
156 |  * Checks if a given client ID has already been approved by the user,
157 |  * based on a signed cookie.
158 |  *
159 |  * @param request - The incoming Request object to read cookies from.
160 |  * @param clientId - The OAuth client ID to check approval for.
161 |  * @param cookieSecret - The secret key used to sign/verify the approval cookie.
162 |  * @returns A promise resolving to true if the client ID is in the list of approved clients in a valid cookie, false otherwise.
163 |  */
164 | export async function clientIdAlreadyApproved(
165 |   request: Request,
166 |   clientId: string,
167 |   cookieSecret: string,
168 | ): Promise<boolean> {
169 |   if (!clientId) return false;
170 |   const cookieHeader = request.headers.get("Cookie");
171 |   const approvedClients = await getApprovedClientsFromCookie(
172 |     cookieHeader,
173 |     cookieSecret,
174 |   );
175 | 
176 |   return approvedClients?.includes(clientId) ?? false;
177 | }
178 | 
179 | /**
180 |  * Configuration for the approval dialog
181 |  */
182 | export interface ApprovalDialogOptions {
183 |   /**
184 |    * Client information to display in the approval dialog
185 |    */
186 |   client: ClientInfo | null;
187 |   /**
188 |    * Server information to display in the approval dialog
189 |    */
190 |   server: {
191 |     name: string;
192 |     logo?: string;
193 |     description?: string;
194 |   };
195 |   /**
196 |    * Arbitrary state data to pass through the approval flow
197 |    * Will be encoded in the form and returned when approval is complete
198 |    */
199 |   state: Record<string, any>;
200 |   /**
201 |    * HMAC secret key for signing state parameters
202 |    */
203 |   cookieSecret: string;
204 | }
205 | 
206 | /**
207 |  * Encodes and signs arbitrary data to a HMAC-signed compact string.
208 |  * @param data - The data to encode (will be stringified).
209 |  * @param secret - HMAC secret key for signing.
210 |  * @returns A HMAC-signed compact string (signature.base64payload).
211 |  */
212 | async function encodeState(data: any, secret: string): Promise<string> {
213 |   try {
214 |     const now = Date.now();
215 |     const payload: OAuthState = {
216 |       req: data as Record<string, unknown>,
217 |       iat: now,
218 |       exp: now + 10 * 60 * 1000, // 10 minute expiry
219 |     };
220 |     return await signState(payload, secret);
221 |   } catch (error) {
222 |     logError(error, {
223 |       loggerScope: ["cloudflare", "approval-dialog"],
224 |       extra: {
225 |         message: "Error encoding approval dialog state",
226 |       },
227 |     });
228 |     throw new Error("Could not encode state");
229 |   }
230 | }
231 | 
232 | /**
233 |  * Decodes and verifies a HMAC-signed string back to its original data.
234 |  * @param encoded - The HMAC-signed compact string.
235 |  * @param secret - HMAC secret key for verification.
236 |  * @returns The original data.
237 |  * @throws Error if signature is invalid or state has expired.
238 |  */
239 | async function decodeState<T = any>(
240 |   encoded: string,
241 |   secret: string,
242 | ): Promise<T> {
243 |   try {
244 |     const parsed = await verifyAndParseState(encoded, secret);
245 |     return parsed.req as T;
246 |   } catch (error) {
247 |     logError(error, {
248 |       loggerScope: ["cloudflare", "approval-dialog"],
249 |       extra: {
250 |         message:
251 |           "Error decoding approval dialog state - signature verification failed or expired",
252 |       },
253 |     });
254 |     throw new Error("Could not decode state - invalid signature or expired");
255 |   }
256 | }
257 | 
258 | /**
259 |  * Renders an approval dialog for OAuth authorization
260 |  * The dialog displays information about the client and server
261 |  * and includes a form to submit approval
262 |  *
263 |  * @param request - The HTTP request
264 |  * @param options - Configuration for the approval dialog
265 |  * @returns A Response containing the HTML approval dialog
266 |  */
267 | export async function renderApprovalDialog(
268 |   request: Request,
269 |   options: ApprovalDialogOptions,
270 | ): Promise<Response> {
271 |   const { client, server, state, cookieSecret } = options;
272 | 
273 |   // Use static skill definitions bundled at build time
274 |   const skills: SkillDefinition[] = skillDefinitions as SkillDefinition[];
275 | 
276 |   // Generate HTML for all skills (checked if defaultEnabled)
277 |   const skillsHtml = skills
278 |     .map(
279 |       (skill) => `
280 |     <label class="permission-item">
281 |       <input type="checkbox" name="skill" value="${sanitizeHtml(skill.id)}"${skill.defaultEnabled ? " checked" : ""}>
282 |       <span class="permission-checkbox"></span>
283 |       <div class="permission-content">
284 |         <div class="permission-header">
285 |           <span class="permission-name">${sanitizeHtml(skill.name)}</span>
286 |           ${skill.toolCount !== undefined ? `<span class="permission-tool-count">${sanitizeHtml(String(skill.toolCount))} ${skill.toolCount === 1 ? "tool" : "tools"}</span>` : ""}
287 |         </div>
288 |         <div class="permission-description">${sanitizeHtml(skill.description)}</div>
289 |       </div>
290 |     </label>
291 |   `,
292 |     )
293 |     .join("");
294 | 
295 |   // Encode state for form submission (HMAC-signed to prevent tampering)
296 |   const encodedState = await encodeState(state, cookieSecret);
297 | 
298 |   // Sanitize any untrusted content
299 |   const serverName = sanitizeHtml(server.name);
300 |   const clientName = client?.clientName
301 |     ? sanitizeHtml(client.clientName)
302 |     : "Unknown MCP Client";
303 |   const serverDescription = server.description
304 |     ? sanitizeHtml(server.description)
305 |     : "";
306 | 
307 |   // Safe URLs
308 |   const logoUrl = server.logo ? sanitizeHtml(server.logo) : "";
309 |   const clientUri = client?.clientUri ? sanitizeHtml(client.clientUri) : "";
310 |   const policyUri = client?.policyUri ? sanitizeHtml(client.policyUri) : "";
311 |   const tosUri = client?.tosUri ? sanitizeHtml(client.tosUri) : "";
312 | 
313 |   // Client contacts
314 |   const contacts =
315 |     client?.contacts && client.contacts.length > 0
316 |       ? sanitizeHtml(client.contacts.join(", "))
317 |       : "";
318 | 
319 |   // Get redirect URIs
320 |   const redirectUris =
321 |     client?.redirectUris && client.redirectUris.length > 0
322 |       ? client.redirectUris.map((uri) => sanitizeHtml(uri))
323 |       : [];
324 | 
325 |   // Generate HTML for the approval dialog
326 |   const htmlContent = `
327 |     <!DOCTYPE html>
328 |     <html lang="en">
329 |       <head>
330 |         <meta charset="UTF-8">
331 |         <meta name="viewport" content="width=device-width, initial-scale=1.0">
332 |         <title>${clientName} | Authorization Request</title>
333 |         <style>
334 |           /* Modern, responsive styling with system fonts */
335 |           :root {
336 |             /* Color palette */
337 |             --bg-gradient-start: oklch(0.13 0.028 261.692);
338 |             --bg-gradient-mid: oklch(0.18 0.034 264.665);
339 |             --bg-gradient-end: oklch(0.13 0.028 261.692);
340 | 
341 |             --card-bg: oklch(0.18 0.02 264);
342 |             --card-bg-hover: oklch(0.20 0.02 264);
343 | 
344 |             --purple-primary: oklch(0.72 0.14 293);
345 |             --purple-hover: oklch(0.76 0.14 293);
346 |             --purple-light: oklch(0.82 0.11 293);
347 | 
348 |             --border-default: oklch(0.28 0.02 264);
349 |             --border-hover: oklch(0.35 0.05 264);
350 | 
351 |             --text-primary: oklch(0.95 0 0);
352 |             --text-secondary: oklch(0.75 0.01 264);
353 |             --text-tertiary: oklch(0.60 0.01 264);
354 | 
355 |             /* Spacing scale */
356 |             --space-xs: 0.25rem;
357 |             --space-sm: 0.5rem;
358 |             --space-md: 1rem;
359 |             --space-lg: 1.5rem;
360 |             --space-xl: 2rem;
361 |             --space-2xl: 3rem;
362 | 
363 |             /* Border radius */
364 |             --radius-sm: 6px;
365 |             --radius-md: 10px;
366 |             --radius-lg: 14px;
367 | 
368 |             /* Shadows */
369 |             --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3);
370 |             --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4), 0 2px 4px rgba(0, 0, 0, 0.3);
371 |             --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.5), 0 4px 8px rgba(0, 0, 0, 0.4);
372 | 
373 |             /* Transitions */
374 |             --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
375 |             --transition-base: 200ms cubic-bezier(0.4, 0, 0.2, 1);
376 |             --transition-slow: 300ms cubic-bezier(0.4, 0, 0.2, 1);
377 |           }
378 | 
379 |           * {
380 |             box-sizing: border-box;
381 |           }
382 | 
383 |           body {
384 |             font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
385 |                          Helvetica, Arial, sans-serif, "Apple Color Emoji",
386 |                          "Segoe UI Emoji", "Segoe UI Symbol";
387 |             line-height: 1.6;
388 |             color: var(--text-secondary);
389 |             background: linear-gradient(180deg, var(--bg-gradient-start) 0%, var(--bg-gradient-mid) 50%, var(--bg-gradient-end) 100%);
390 |             min-height: 100vh;
391 |             margin: 0;
392 |             padding: 0;
393 |             -webkit-font-smoothing: antialiased;
394 |             -moz-osx-font-smoothing: grayscale;
395 |           }
396 | 
397 |           .container {
398 |             max-width: 600px;
399 |             margin: var(--space-xl) auto;
400 |             padding: var(--space-md);
401 |           }
402 | 
403 |           /* Wider container for two-column layout */
404 |           @media (min-width: 1024px) {
405 |             .container {
406 |               max-width: 1100px;
407 |             }
408 |           }
409 | 
410 |           .precard {
411 |             text-align: center;
412 |             margin-bottom: var(--space-lg);
413 |           }
414 | 
415 |           .card {
416 |             background: var(--card-bg);
417 |             padding: var(--space-2xl);
418 |             border-radius: var(--radius-lg);
419 |             box-shadow: var(--shadow-lg);
420 |             border: 1px solid var(--border-default);
421 |           }
422 | 
423 |           .header {
424 |             display: flex;
425 |             align-items: center;
426 |             justify-content: center;
427 |             gap: var(--space-md);
428 |             margin-bottom: var(--space-sm);
429 |           }
430 | 
431 |           .logo {
432 |             width: 40px;
433 |             height: 40px;
434 |             color: var(--purple-light);
435 |             flex-shrink: 0;
436 |           }
437 | 
438 |           .title {
439 |             margin: 0;
440 |             font-size: 1.75rem;
441 |             font-weight: 600;
442 |             color: var(--text-primary);
443 |             letter-spacing: -0.02em;
444 |           }
445 | 
446 |           .alert {
447 |             margin: 0 0 var(--space-xl) 0;
448 |             font-size: 1.375rem;
449 |             font-weight: 500;
450 |             text-align: center;
451 |             color: var(--text-primary);
452 |             line-height: 1.4;
453 |           }
454 | 
455 |           .client-info {
456 |             background: rgba(0, 0, 0, 0.25);
457 |             padding: var(--space-md);
458 |             gap: var(--space-md);
459 |             display: flex;
460 |             flex-direction: column;
461 |           }
462 | 
463 |           .detail-label {
464 |             font-weight: 500;
465 |             color: var(--text-tertiary);
466 |             font-size: 0.875rem;
467 |             margin-bottom: var(--space-xs);
468 |           }
469 | 
470 |           .detail-value {
471 |             font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
472 |             word-break: break-all;
473 |             color: var(--text-secondary);
474 |             font-size: 0.875rem;
475 |           }
476 | 
477 |           .detail-value.small {
478 |             font-size: 0.8125rem;
479 |           }
480 | 
481 |           .actions {
482 |             display: flex;
483 |             justify-content: flex-end;
484 |             gap: var(--space-md);
485 |           }
486 | 
487 |           a {
488 |             color: var(--purple-primary);
489 |             text-decoration: none;
490 |             transition: color var(--transition-fast);
491 |           }
492 | 
493 |           a:hover {
494 |             color: var(--purple-hover);
495 |             text-decoration: underline;
496 |           }
497 | 
498 |           .button {
499 |             padding: 0.75rem 1.75rem;
500 |             font-weight: 600;
501 |             cursor: pointer;
502 |             border: none;
503 |             font-size: 0.9375rem;
504 |             border-radius: var(--radius-sm);
505 |             transition: all var(--transition-base);
506 |             font-family: inherit;
507 |             min-height: 44px;
508 |           }
509 | 
510 |           .button:focus-visible {
511 |             outline: 2px solid var(--purple-primary);
512 |             outline-offset: 2px;
513 |           }
514 | 
515 |           .button-primary {
516 |             background: var(--purple-primary);
517 |             color: oklch(0.1 0 0);
518 |             box-shadow: var(--shadow-sm);
519 |           }
520 | 
521 |           .button-primary:hover {
522 |             background: var(--purple-hover);
523 |             transform: translateY(-1px);
524 |             box-shadow: var(--shadow-md);
525 |           }
526 | 
527 |           .button-primary:active {
528 |             transform: translateY(0);
529 |             box-shadow: var(--shadow-sm);
530 |           }
531 | 
532 |           .button-secondary {
533 |             background: transparent;
534 |             border: 1.5px solid var(--border-default);
535 |             color: var(--text-secondary);
536 |           }
537 | 
538 |           .button-secondary:hover {
539 |             border-color: var(--border-hover);
540 |             background: rgba(255, 255, 255, 0.03);
541 |             color: var(--text-primary);
542 |           }
543 | 
544 |           .button-secondary:active {
545 |             background: rgba(255, 255, 255, 0.05);
546 |           }
547 | 
548 |           /* Section header (for Client, Skills, etc.) */
549 |           .section-header {
550 |             font-size: 1.125rem;
551 |             font-weight: 600;
552 |             color: var(--text-primary);
553 |             letter-spacing: -0.01em;
554 |             margin: 0;
555 |           }
556 | 
557 |           /* Permission items */
558 |           .permission-item {
559 |             display: flex;
560 |             align-items: flex-start;
561 |             gap: var(--space-md);
562 |             padding: var(--space-sm) var(--space-md);
563 |             background: transparent;
564 |             border: none;
565 |             border-left: 3px solid transparent;
566 |             margin-bottom: var(--space-sm);
567 |             cursor: pointer;
568 |             transition: all var(--transition-base);
569 |             position: relative;
570 |           }
571 | 
572 |           .permission-item:last-child {
573 |             margin-bottom: 0;
574 |           }
575 | 
576 |           .permission-item:hover {
577 |             background: rgba(255, 255, 255, 0.03);
578 |           }
579 | 
580 |           .permission-item input[type="checkbox"] {
581 |             position: absolute;
582 |             opacity: 0;
583 |             pointer-events: none;
584 |           }
585 | 
586 |           .permission-checkbox {
587 |             font-size: 1.5rem;
588 |             color: var(--text-tertiary);
589 |             transition: all var(--transition-fast);
590 |             flex-shrink: 0;
591 |             cursor: pointer;
592 |             line-height: 1;
593 |             user-select: none;
594 |           }
595 | 
596 |           /* Checkbox states */
597 |           .permission-item input[type="checkbox"]:checked + .permission-checkbox {
598 |             color: var(--purple-primary);
599 |             transform: scale(1.1);
600 |           }
601 | 
602 |           .permission-item input[type="checkbox"]:checked + .permission-checkbox::before {
603 |             content: "☑";
604 |           }
605 | 
606 |           .permission-item input[type="checkbox"]:not(:checked) + .permission-checkbox::before {
607 |             content: "☐";
608 |           }
609 | 
610 |           .permission-item:has(input[type="checkbox"]:checked) {
611 |             border-left-color: var(--purple-primary);
612 |             background: rgba(171, 99, 232, 0.05);
613 |           }
614 | 
615 |           .permission-content {
616 |             flex: 1;
617 |             min-width: 0;
618 |           }
619 | 
620 |           .permission-header {
621 |             display: flex;
622 |             justify-content: space-between;
623 |             align-items: center;
624 |             gap: var(--space-md);
625 |             margin-bottom: var(--space-xs);
626 |           }
627 | 
628 |           .permission-name {
629 |             font-weight: 600;
630 |             color: var(--text-primary);
631 |             font-size: 1rem;
632 |             letter-spacing: -0.01em;
633 |           }
634 | 
635 |           .permission-tool-count {
636 |             font-size: 0.8125rem;
637 |             color: var(--text-tertiary);
638 |             font-weight: 500;
639 |             white-space: nowrap;
640 |             background: rgba(255, 255, 255, 0.05);
641 |             padding: 0.125rem 0.5rem;
642 |             border-radius: 12px;
643 |             border: 1px solid var(--border-default);
644 |           }
645 | 
646 |           .permission-description {
647 |             color: var(--text-secondary);
648 |             font-size: 0.875rem;
649 |             line-height: 1.5;
650 |           }
651 | 
652 |           /* Two-column layout for wider screens */
653 |           .approval-grid {
654 |             display: flex;
655 |             flex-direction: column;
656 |             gap: var(--space-xl);
657 |             align-items: start;
658 |           }
659 | 
660 |           .approval-column {
661 |             display: flex;
662 |             flex-direction: column;
663 |             gap: var(--space-md);
664 |           }
665 | 
666 |           .authorization-text {
667 |             color: var(--text-secondary);
668 |             line-height: 1.6;
669 |           }
670 | 
671 |           /* Large screens and up: Two-column layout */
672 |           @media (min-width: 1024px) {
673 |             .approval-grid {
674 |               flex-direction: row;
675 |               gap: var(--space-2xl);
676 |             }
677 | 
678 |             .approval-column {
679 |               flex: 1 1 50%;
680 |             }
681 |           }
682 | 
683 |           /* Responsive adjustments */
684 |           @media (max-width: 640px) {
685 |             .container {
686 |               margin: var(--space-md) auto;
687 |               padding: var(--space-sm);
688 |             }
689 | 
690 |             .card {
691 |               padding: var(--space-lg);
692 |               border-radius: var(--radius-md);
693 |             }
694 | 
695 |             .title {
696 |               font-size: 1.5rem;
697 |             }
698 | 
699 |             .alert {
700 |               font-size: 1.125rem;
701 |             }
702 | 
703 |             .actions {
704 |               flex-direction: column-reverse;
705 |               gap: var(--space-sm);
706 |             }
707 | 
708 |             .button {
709 |               width: 100%;
710 |             }
711 | 
712 |             .permission-item {
713 |               padding: var(--space-sm);
714 |             }
715 | 
716 |             .permission-header {
717 |               flex-direction: column;
718 |               align-items: flex-start;
719 |               gap: var(--space-xs);
720 |             }
721 |           }
722 |         </style>
723 |       </head>
724 |       <body>
725 |         <div class="container">
726 |           <header class="precard">
727 |             <div class="header">
728 |               <svg class="logo" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M17.48 1.996c.45.26.823.633 1.082 1.083l13.043 22.622a2.962 2.962 0 0 1-2.562 4.44h-3.062c.043-.823.039-1.647 0-2.472h3.052a.488.488 0 0 0 .43-.734L16.418 4.315a.489.489 0 0 0-.845 0L12.582 9.51a23.16 23.16 0 0 1 7.703 8.362 23.19 23.19 0 0 1 2.8 11.024v1.234h-7.882v-1.236a15.284 15.284 0 0 0-6.571-12.543l-1.48 2.567a12.301 12.301 0 0 1 5.105 9.987v1.233h-9.3a2.954 2.954 0 0 1-2.56-1.48A2.963 2.963 0 0 1 .395 25.7l1.864-3.26a6.854 6.854 0 0 1 2.15 1.23l-1.883 3.266a.49.49 0 0 0 .43.734h6.758a9.985 9.985 0 0 0-4.83-7.272l-1.075-.618 3.927-6.835 1.075.615a17.728 17.728 0 0 1 6.164 5.956 17.752 17.752 0 0 1 2.653 8.154h2.959a20.714 20.714 0 0 0-3.05-9.627 20.686 20.686 0 0 0-7.236-7.036l-1.075-.618 4.215-7.309a2.958 2.958 0 0 1 4.038-1.083Z" fill="currentColor"></path></svg>
729 |               <h1 class="title">${serverName}</h1>
730 |             </div>
731 |           </header>
732 | 
733 |           <main class="card">
734 | 
735 |             <p class="alert"><strong>${clientName || "A new MCP Client"}</strong> is requesting access</p>
736 | 
737 |             <form method="post" action="${new URL(request.url).pathname}" aria-label="Authorization form">
738 |               <input type="hidden" name="state" value="${encodedState}">
739 | 
740 |               <div class="approval-grid">
741 |                 <!-- Left column: Skills selection -->
742 |                 <div class="approval-column">
743 |                   <h2 class="section-header" id="skills-heading">Skills</h2>
744 |                   <section class="permission-section" aria-labelledby="skills-heading">
745 |                     <div role="group" aria-label="Select skills to grant access">
746 |                       ${skillsHtml}
747 |                     </div>
748 |                   </section>
749 |                 </div>
750 | 
751 |                 <!-- Right column: Client info and actions -->
752 |                 <div class="approval-column">
753 |                   <h2 class="section-header">Client</h2>
754 |                   <section class="client-info" aria-label="Client Information">
755 |                     <div class="client-detail">
756 |                       <div class="detail-label">Name:</div>
757 |                       <div class="detail-value">
758 |                         ${clientName}
759 |                       </div>
760 |                     </div>
761 | 
762 |                     ${
763 |                       clientUri
764 |                         ? `
765 |                       <div class="client-detail">
766 |                         <div class="detail-label">Website:</div>
767 |                         <div class="detail-value small">
768 |                           <a href="${clientUri}" target="_blank" rel="noopener noreferrer">
769 |                             ${clientUri}
770 |                           </a>
771 |                         </div>
772 |                       </div>
773 |                     `
774 |                         : ""
775 |                     }
776 | 
777 |                     ${
778 |                       policyUri
779 |                         ? `
780 |                       <div class="client-detail">
781 |                         <div class="detail-label">Privacy Policy:</div>
782 |                         <div class="detail-value">
783 |                           <a href="${policyUri}" target="_blank" rel="noopener noreferrer">
784 |                             ${policyUri}
785 |                           </a>
786 |                         </div>
787 |                       </div>
788 |                     `
789 |                         : ""
790 |                     }
791 | 
792 |                     ${
793 |                       tosUri
794 |                         ? `
795 |                       <div class="client-detail">
796 |                         <div class="detail-label">Terms of Service:</div>
797 |                         <div class="detail-value">
798 |                           <a href="${tosUri}" target="_blank" rel="noopener noreferrer">
799 |                             ${tosUri}
800 |                           </a>
801 |                         </div>
802 |                       </div>
803 |                     `
804 |                         : ""
805 |                     }
806 | 
807 |                     ${
808 |                       redirectUris.length > 0
809 |                         ? `
810 |                       <div class="client-detail">
811 |                         <div class="detail-label">Redirect URIs:</div>
812 |                         <div class="detail-value small">
813 |                           ${redirectUris.map((uri) => `<div>${uri}</div>`).join("")}
814 |                         </div>
815 |                       </div>
816 |                     `
817 |                         : ""
818 |                     }
819 | 
820 |                     ${
821 |                       contacts
822 |                         ? `
823 |                       <div class="client-detail">
824 |                         <div class="detail-label">Contact:</div>
825 |                         <div class="detail-value">${contacts}</div>
826 |                       </div>
827 |                     `
828 |                         : ""
829 |                     }
830 |                   </section>
831 | 
832 |                   <p class="authorization-text">This MCP Client is requesting authorization to Sentry. If you approve, you will be redirected to complete authentication.</p>
833 | 
834 |                   <div class="actions">
835 |                     <button type="button" class="button button-secondary" onclick="window.history.back()" aria-label="Cancel authorization">Cancel</button>
836 |                     <button type="submit" class="button button-primary" aria-label="Approve authorization request">Approve</button>
837 |                   </div>
838 |                 </div>
839 |               </div>
840 |             </form>
841 |           </main>
842 |         </div>
843 |       </body>
844 |     </html>
845 |   `;
846 | 
847 |   return new Response(htmlContent, {
848 |     headers: {
849 |       "Content-Type": "text/html; charset=utf-8",
850 |     },
851 |   });
852 | }
853 | 
854 | /**
855 |  * Result of parsing the approval form submission.
856 |  */
857 | export interface ParsedApprovalResult {
858 |   /** The original state object passed through the form. */
859 |   state: any;
860 |   /** Headers to set on the redirect response, including the Set-Cookie header. */
861 |   headers: Record<string, string>;
862 |   /** Selected skills */
863 |   skills: string[];
864 | }
865 | 
866 | /**
867 |  * Parses the form submission from the approval dialog, extracts the state,
868 |  * and generates Set-Cookie headers to mark the client as approved.
869 |  *
870 |  * @param request - The incoming POST Request object containing the form data.
871 |  * @param cookieSecret - The secret key used to sign the approval cookie.
872 |  * @returns A promise resolving to an object containing the parsed state and necessary headers.
873 |  * @throws If the request method is not POST, form data is invalid, or state is missing.
874 |  */
875 | export async function parseRedirectApproval(
876 |   request: Request,
877 |   cookieSecret: string,
878 | ): Promise<ParsedApprovalResult> {
879 |   if (request.method !== "POST") {
880 |     throw new Error("Invalid request method. Expected POST.");
881 |   }
882 | 
883 |   let state: any;
884 |   let clientId: string | undefined;
885 |   let skills: string[];
886 | 
887 |   try {
888 |     const formData = await request.formData();
889 |     const encodedState = formData.get("state");
890 | 
891 |     if (typeof encodedState !== "string" || !encodedState) {
892 |       throw new Error("Missing or invalid 'state' in form data.");
893 |     }
894 | 
895 |     state = await decodeState<{ oauthReqInfo?: AuthRequest }>(
896 |       encodedState,
897 |       cookieSecret,
898 |     ); // Decode and verify the state
899 |     clientId = state?.oauthReqInfo?.clientId; // Extract clientId from within the state
900 | 
901 |     if (!clientId) {
902 |       throw new Error("Could not extract clientId from state object.");
903 |     }
904 | 
905 |     // Extract skill selections from checkboxes - collect all 'skill' field values
906 |     skills = formData
907 |       .getAll("skill")
908 |       .filter((s): s is string => typeof s === "string");
909 |   } catch (error) {
910 |     logError(error, {
911 |       loggerScope: ["cloudflare", "approval-dialog"],
912 |       extra: {
913 |         message: "Error processing approval form submission",
914 |       },
915 |     });
916 |     throw new Error(
917 |       `Failed to parse approval form: ${error instanceof Error ? error.message : String(error)}`,
918 |     );
919 |   }
920 | 
921 |   // Get existing approved clients
922 |   const cookieHeader = request.headers.get("Cookie");
923 |   const existingApprovedClients =
924 |     (await getApprovedClientsFromCookie(cookieHeader, cookieSecret)) || [];
925 | 
926 |   // Add the newly approved client ID (avoid duplicates)
927 |   const updatedApprovedClients = Array.from(
928 |     new Set([...existingApprovedClients, clientId]),
929 |   );
930 | 
931 |   // Sign the updated list
932 |   const payload = JSON.stringify(updatedApprovedClients);
933 |   const key = await importKey(cookieSecret);
934 |   const signature = await signData(key, payload);
935 |   const newCookieValue = `${signature}.${btoa(payload)}`; // signature.base64(payload)
936 | 
937 |   // Generate Set-Cookie header
938 |   const headers: Record<string, string> = {
939 |     "Set-Cookie": `${COOKIE_NAME}=${newCookieValue}; HttpOnly; Secure; Path=/; SameSite=Lax; Max-Age=${ONE_YEAR_IN_SECONDS}`,
940 |   };
941 | 
942 |   return { state, headers, skills };
943 | }
944 | 
945 | // sanitizeHtml function is now imported from "./html-utils"
946 | 
```

--------------------------------------------------------------------------------
/packages/mcp-cloudflare/src/client/components/ui/icons/gemini.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | export function GeminiIcon({ className }: { className?: string }) {
 2 |   return (
 3 |     <svg
 4 |       role="img"
 5 |       viewBox="0 0 1645 1645"
 6 |       xmlns="http://www.w3.org/2000/svg"
 7 |       className={className}
 8 |       aria-hidden="true"
 9 |     >
10 |       <title>Gemini CLI</title>
11 |       {/* Rounded rectangle terminal background */}
12 |       <path
13 |         d="M0 0 C1.85279409 -0.00165196 3.70558783 -0.00373929 5.55838099 -0.0062201 C10.64697628 -0.01176662 15.73555467 -0.01111804 20.824152 -0.00944251 C26.36153269 -0.00870977 31.89890868 -0.01373822 37.43628734 -0.01801753 C48.41790669 -0.02558928 59.39952116 -0.02779017 70.38114285 -0.02834659 C82.86028257 -0.02921409 95.3394182 -0.03523174 107.81855607 -0.04165257 C139.53375215 -0.05794172 171.24894747 -0.0643397 202.96414702 -0.06910044 C218.76300596 -0.0714853 234.56186447 -0.07518913 250.36072308 -0.07913754 C301.06306383 -0.09180158 351.76540456 -0.10258492 402.46774686 -0.10597799 C404.8903103 -0.10614023 407.31287374 -0.10630579 409.73543717 -0.10647238 C414.59375925 -0.10680617 419.45208133 -0.10713665 424.31040341 -0.10746694 C429.99806819 -0.10785383 435.68573297 -0.10824344 441.37339775 -0.108638 C443.00124091 -0.10875042 444.62908407 -0.10886184 446.25692723 -0.10897225 C498.51730958 -0.1125203 550.77767816 -0.12759324 603.03805526 -0.15091 C640.5976602 -0.167661 678.15726254 -0.18313063 715.71687126 -0.18764496 C716.48997653 -0.18773844 717.26308179 -0.18783193 718.05961449 -0.18792825 C735.03476482 -0.18994771 752.0099151 -0.19110341 768.98506555 -0.19145683 C775.059874 -0.191593 781.13468246 -0.19180199 787.20949091 -0.19205009 C788.72067214 -0.19211165 790.23185338 -0.19216997 791.74303461 -0.19222504 C815.78289815 -0.19314333 839.82274729 -0.20309316 863.86260609 -0.21768252 C888.09183159 -0.2323279 912.32103947 -0.23732221 936.55026861 -0.22926298 C951.57067019 -0.22427155 966.59102271 -0.22721715 981.61141799 -0.24382807 C991.20737157 -0.2536066 1000.80328401 -0.25237246 1010.39923724 -0.24239184 C1015.8576847 -0.23702052 1021.31601924 -0.23622241 1026.77445716 -0.24890056 C1102.91767039 -0.41439915 1173.42549637 17.67341201 1233.68523312 66.13142776 C1234.6997253 66.93966995 1235.71421749 67.74791214 1236.75945187 68.58064651 C1253.65857556 82.38931934 1269.85678883 98.46973158 1282.68523312 116.13142776 C1283.47929562 117.11884964 1284.27335812 118.10627151 1285.09148312 119.12361526 C1328.2222344 172.76407493 1345.90153738 242.04402427 1345.81386757 309.74908638 C1345.81521738 311.57359934 1345.81700304 313.39811202 1345.8191798 315.22262418 C1345.8238756 320.20938647 1345.82241932 325.19613033 1345.81990203 330.18289348 C1345.81826083 335.61857568 1345.82241653 341.05425407 1345.82581121 346.48993504 C1345.83095356 356.00816163 1345.83238443 365.52638314 1345.83135509 375.04461098 C1345.82982811 389.19922169 1345.83399649 403.35382679 1345.83922972 417.50843642 C1345.84889502 444.26251532 1345.85171429 471.01659228 1345.85228678 497.77067272 C1345.85235153 500.71931437 1345.85241947 503.66795601 1345.85249019 506.61659765 C1345.85250719 507.34836406 1345.8525242 508.08013048 1345.85254172 508.83407163 C1345.85295065 525.06984028 1345.85514213 541.30560854 1345.85764603 557.54137698 C1345.85867443 564.23253947 1345.85966448 570.92370197 1345.86066228 577.61486447 C1345.86082817 578.72393507 1345.86082817 578.72393507 1345.8609974 579.85541114 C1345.86731926 622.21117431 1345.87144676 664.56693722 1345.87098885 706.92270088 C1345.87097307 708.50692036 1345.87095733 710.09113984 1345.87094164 711.67535932 C1345.87086826 718.82752579 1345.87078009 725.97969225 1345.87068775 733.13185871 C1345.87064663 736.32100392 1345.87060648 739.51014914 1345.87056644 742.69929435 C1345.87054604 744.2955169 1345.87052502 745.89173946 1345.87050338 747.48796201 C1345.86982334 797.94724526 1345.87713784 848.40652148 1345.89104366 898.86580276 C1345.89454491 911.57744151 1345.89801448 924.28908027 1345.90130806 937.00071907 C1345.90161237 938.1749064 1345.90161237 938.1749064 1345.90192283 939.37281469 C1345.90814188 963.51537033 1345.91190418 987.65792539 1345.91316605 1011.8004818 C1345.913206 1012.55893279 1345.91324595 1013.31738379 1345.91328711 1014.09881815 C1345.91414056 1030.75208479 1345.91440152 1047.40535139 1345.91411673 1064.05861805 C1345.91402026 1070.01811496 1345.91396821 1075.97761187 1345.913938 1081.93710878 C1345.91393009 1083.41957962 1345.91391988 1084.90205045 1345.91390738 1086.38452129 C1345.91373137 1109.96383929 1345.91941629 1133.54315127 1345.9280612 1157.12246755 C1345.93697056 1181.54734893 1345.93851903 1205.9722208 1345.93174907 1230.397103 C1345.92805997 1244.46430848 1345.92986663 1258.53148758 1345.93969141 1272.59869026 C1345.94570713 1282.00935404 1345.94437828 1291.41999879 1345.93722091 1300.83066171 C1345.93335283 1306.1831734 1345.9325247 1311.53563419 1345.94067826 1316.88814221 C1346.01990301 1372.64833394 1340.38833522 1425.2638569 1315.68523312 1476.13142776 C1315.28497921 1476.96319534 1314.8847253 1477.79496292 1314.47234249 1478.65193558 C1304.84705497 1498.44061725 1292.69023483 1517.1509225 1278.68523312 1534.13142776 C1278.24276241 1534.67396194 1277.80029171 1535.21649612 1277.3444128 1535.77547073 C1270.36211502 1544.28559729 1262.81594757 1552.19924188 1255.06023312 1560.00642776 C1254.20195919 1560.87396683 1253.34368526 1561.74150589 1252.45940304 1562.63533401 C1246.51149118 1568.55435327 1240.26453464 1573.92773934 1233.68523312 1579.13142776 C1233.07663448 1579.61966019 1232.46803585 1580.10789261 1231.84099483 1580.61091995 C1177.79411977 1623.78698371 1110.99317905 1645.34751655 1042.22251892 1645.26006222 C1040.37812141 1645.26141102 1038.53372417 1645.26319613 1036.68932745 1645.26537445 C1031.63339617 1645.27008365 1026.57748299 1645.26860955 1021.52155085 1645.26609668 C1016.01616242 1645.26445898 1010.51077779 1645.26860829 1005.00539058 1645.27200586 C995.36024345 1645.27715381 985.71510134 1645.2785781 976.06995296 1645.27754974 C962.39839951 1645.27609565 948.72685143 1645.27969438 935.05529896 1645.28468932 C907.27468779 1645.29482412 879.49407863 1645.29789495 851.71346582 1645.29848143 C848.72479074 1645.29854618 845.73611566 1645.29861412 842.74744058 1645.29868484 C841.63491688 1645.29871034 841.63491688 1645.29871034 840.49991796 1645.29873636 C824.05135905 1645.29914513 807.60280052 1645.30133605 791.15424181 1645.30384068 C784.37747859 1645.30486907 777.60071536 1645.30585911 770.82395214 1645.30685693 C770.07513485 1645.30696752 769.32631756 1645.30707811 768.55480884 1645.30719205 C725.65258494 1645.3135148 682.75036129 1645.31764131 639.8481369 1645.31718349 C638.24313348 1645.31716772 636.63813005 1645.31715198 635.03312663 1645.31713629 C627.78696635 1645.3170629 620.54080608 1645.31697474 613.29464581 1645.3168824 C610.06352251 1645.31684128 606.83239922 1645.31680113 603.60127593 1645.31676109 C601.9840417 1645.31674069 600.36680747 1645.31671967 598.74957325 1645.31669803 C547.62241069 1645.31601794 496.49525506 1645.32333297 445.36809444 1645.33723831 C432.48800343 1645.34073949 419.60791242 1645.34420902 406.72782135 1645.34750271 C405.93468437 1645.34770558 405.14154738 1645.34790846 404.32437594 1645.34811748 C379.86775966 1645.35433529 355.41114394 1645.35809859 330.9545269 1645.35936069 C329.80226645 1645.35942062 329.80226645 1645.35942062 328.62672802 1645.35948176 C311.76101904 1645.36033515 294.89531009 1645.36059619 278.02960109 1645.36031138 C271.99426593 1645.3602149 265.95893076 1645.36016285 259.9235956 1645.36013265 C258.42222572 1645.36012474 256.92085584 1645.36011453 255.41948596 1645.36010203 C231.52644876 1645.35992592 207.63341751 1645.36561485 183.74038201 1645.37425584 C159.6656363 1645.38291827 135.59089881 1645.38508635 111.5161522 1645.37849452 C96.58851415 1645.37440847 81.66090063 1645.37561041 66.7332651 1645.38588606 C57.20116693 1645.39189245 47.66908757 1645.39058324 38.13699026 1645.38341556 C32.71178722 1645.37953965 27.28663447 1645.37874225 21.861435 1645.38687291 C-28.03473026 1645.45678564 -28.03473026 1645.45678564 -52.37726688 1640.88142776 C-54.13103642 1640.55827591 -54.13103642 1640.55827591 -55.92023563 1640.22859573 C-104.96367224 1630.88603476 -152.03476201 1609.27327301 -190.31476688 1577.13142776 C-191.18391727 1576.41237259 -191.18391727 1576.41237259 -192.07062626 1575.67879105 C-206.40679864 1563.78191916 -219.8565396 1550.84379159 -231.31476688 1536.13142776 C-232.73127897 1534.46462971 -234.14795229 1532.79796868 -235.56476688 1531.13142776 C-249.43346059 1514.1404627 -260.89985655 1495.01975616 -270.30841923 1475.23884964 C-270.9875368 1473.81668979 -271.67559583 1472.39877 -272.37189579 1470.98494339 C-282.27955739 1450.82260735 -289.13334787 1429.21534558 -293.75226688 1407.25642776 C-293.97487186 1406.20334427 -294.19747684 1405.15026077 -294.42682743 1404.06526566 C-295.13807971 1400.43231938 -295.73118488 1396.78638304 -296.31476688 1393.13142776 C-296.43888209 1392.38794033 -296.5629973 1391.6444529 -296.69087358 1390.87843555 C-300.10772342 1369.70890066 -299.74243833 1348.48675404 -299.70016955 1327.11113199 C-299.69249043 1321.52840033 -299.70236625 1315.94569319 -299.70989954 1310.36296511 C-299.72068871 1300.59901844 -299.7204343 1290.83512162 -299.71275806 1281.07117271 C-299.70455342 1270.60647158 -299.70278862 1260.14179809 -299.70897007 1249.67709541 C-299.70935759 1249.01344466 -299.70974511 1248.34979391 -299.71014437 1247.66603254 C-299.71174438 1244.96260226 -299.71339053 1242.25917201 -299.71509519 1239.55574179 C-299.73092184 1214.43834097 -299.73205764 1189.32095971 -299.72365284 1164.20355606 C-299.71555889 1140.00316479 -299.71239088 1115.80278276 -299.71621418 1091.60239029 C-299.71644662 1090.08000875 -299.71667809 1088.5576272 -299.71690859 1087.03524566 C-299.71784006 1080.91681002 -299.71879607 1074.79837439 -299.71978788 1068.67993877 C-299.72253836 1051.57835235 -299.72425056 1034.4767659 -299.72514629 1017.37517929 C-299.72518723 1016.59653402 -299.72522817 1015.81788875 -299.72527035 1015.01564817 C-299.72649745 990.21951062 -299.72312129 965.42337589 -299.71659756 940.62723923 C-299.71638681 939.82347291 -299.71617606 939.01970659 -299.71595893 938.19158371 C-299.71252444 925.14047206 -299.70886589 912.08936048 -299.70518841 899.0382489 C-299.69062037 847.28202859 -299.69129655 795.52583157 -299.70588017 743.76961136 C-299.70954399 730.72494636 -299.71317725 717.68028136 -299.71659756 704.6356163 C-299.7168089 703.83231463 -299.71702025 703.02901295 -299.71723799 702.2013688 C-299.72371042 677.43013757 -299.72644864 652.65890828 -299.72514629 627.88767624 C-299.72510554 627.10960899 -299.72506479 626.33154173 -299.7250228 625.52989669 C-299.7240852 608.4470349 -299.72218396 591.36417333 -299.71941479 574.28131175 C-299.71842744 568.16847749 -299.71748819 562.05564323 -299.71656265 555.94280896 C-299.71633049 554.42221536 -299.71609429 552.90162177 -299.71585406 551.38102817 C-299.71204297 527.19168234 -299.71631024 503.00234666 -299.7244045 478.81300216 C-299.73269433 453.76528921 -299.72960461 428.71759966 -299.71381061 403.66989005 C-299.71215369 400.97518009 -299.71054356 398.28047012 -299.70897007 395.58576012 C-299.70838414 394.59382677 -299.70838414 394.59382677 -299.70778637 393.58185436 C-299.7015328 382.51693498 -299.7070501 371.452047 -299.71574913 360.38713076 C-299.72249612 350.73906669 -299.71825385 341.09104697 -299.70523482 331.44298944 C-299.69812754 325.95337235 -299.69559745 320.46386527 -299.70645277 314.97425181 C-299.78403434 272.65797889 -299.78403434 272.65797889 -296.31476688 252.13142776 C-296.12825837 251.00301811 -296.12825837 251.00301811 -295.93798201 249.8518123 C-295.24500467 245.71234227 -294.45096729 241.60880097 -293.56476688 237.50642776 C-293.30800175 236.30042923 -293.05123661 235.09443069 -292.78669071 233.85188675 C-288.54504502 214.51380946 -282.27683699 196.23882628 -274.31476688 178.13142776 C-273.96188602 177.32254105 -273.60900517 176.51365433 -273.24543095 175.68025589 C-255.00853936 134.31157544 -225.17858027 94.86746475 -189.31476688 67.13142776 C-188.2271858 66.25586125 -187.14133946 65.37813692 -186.05695438 64.49861526 C-168.64504963 50.45476963 -149.53146368 38.69118941 -129.33185673 29.09041214 C-128.00092036 28.45764549 -126.67302196 27.81844365 -125.34845829 27.17244339 C-95.67437367 12.76286573 -63.01618361 4.60400491 -30.31476688 1.13142776 C-29.49873102 1.04326702 -28.68269515 0.95510627 -27.84193093 0.86427398 C-18.55756923 -0.02577673 -9.31633771 -0.01017153 0 0 Z M-56.31476688 104.13142776 C-57.24176395 104.41397415 -58.16876102 104.69652054 -59.12384892 104.98762894 C-71.13310311 108.73868735 -82.27906563 113.09518702 -93.31476688 119.13142776 C-94.66495908 119.8503059 -96.01522052 120.56905402 -97.36554813 121.28767776 C-130.92322354 139.40352264 -158.27271 167.23551505 -177.31476688 200.13142776 C-177.70116337 200.79577835 -178.08755985 201.46012894 -178.48566532 202.14461136 C-195.85168942 232.40050498 -203.8401734 267.51876612 -203.69508362 302.2534399 C-203.69852608 304.108481 -203.70327884 305.96352003 -203.70920458 307.81855485 C-203.72162348 312.8807789 -203.71560547 317.94283273 -203.70637091 323.00505726 C-203.69962779 328.52592835 -203.71034387 334.04677131 -203.71876675 339.56763631 C-203.73111281 349.23223975 -203.73235499 358.89679462 -203.72621632 368.56140327 C-203.71753879 382.25987279 -203.72410004 395.95830209 -203.73491398 409.65676818 C-203.75627791 436.74814577 -203.75837497 463.83950084 -203.75285694 490.93088474 C-203.75210117 494.67027173 -203.75137355 498.40965871 -203.75067496 502.14904571 C-203.75053587 502.89184835 -203.75039678 503.634651 -203.75025347 504.39996282 C-203.74729751 520.88292644 -203.75031761 537.36588736 -203.75457125 553.84885049 C-203.75515341 556.10669293 -203.75572226 558.36453538 -203.75628688 560.62237782 C-203.75780192 566.67536923 -203.75932884 572.72836064 -203.76087989 578.78135204 C-203.77163209 621.02117626 -203.77634318 663.26099798 -203.76840973 705.50082302 C-203.76826142 706.29655357 -203.76811312 707.09228413 -203.76796032 707.91212773 C-203.76630141 716.78476631 -203.76455183 725.65740488 -203.76276731 734.53004345 C-203.76260628 735.33106624 -203.76244526 736.13208903 -203.76227935 736.95738524 C-203.76130188 741.81646447 -203.76130188 741.81646447 -203.76031816 746.67554369 C-203.74992811 797.90572441 -203.75769226 849.13587078 -203.78131962 900.36604691 C-203.78726336 913.27480278 -203.79315507 926.18355868 -203.79872894 939.09231472 C-203.79907271 939.88723165 -203.79941648 940.68214858 -203.79977067 941.5011539 C-203.81030354 966.01339898 -203.81589223 990.52564118 -203.81628609 1015.03788853 C-203.81629887 1015.80782083 -203.81631165 1016.57775313 -203.81632482 1017.37101671 C-203.81655315 1034.27535281 -203.81554669 1051.17968866 -203.81341577 1068.08402462 C-203.81266105 1074.13309735 -203.81198283 1080.1821701 -203.81133562 1086.23124285 C-203.81117292 1087.73600403 -203.81100526 1089.24076522 -203.8108326 1090.74552641 C-203.80811532 1114.68768031 -203.81664895 1138.6298133 -203.83068671 1162.5719627 C-203.84511759 1187.36643885 -203.84461478 1212.16087536 -203.82731199 1236.95535018 C-203.81765133 1251.23873496 -203.81916998 1265.52202178 -203.83654667 1279.80540003 C-203.84705192 1289.35626777 -203.84291355 1298.90705814 -203.82706664 1308.45791829 C-203.81845254 1313.89297275 -203.8159668 1319.32782805 -203.83133595 1324.76287212 C-203.99723202 1355.6619369 -203.99723202 1355.6619369 -199.31476688 1386.13142776 C-199.13026653 1386.93998353 -198.94576617 1387.7485393 -198.75567489 1388.58159675 C-197.7125382 1393.13114078 -196.58536237 1397.63844751 -195.31476688 1402.13142776 C-195.10166874 1402.89560013 -194.88857059 1403.65977249 -194.66901493 1404.44710159 C-180.09414032 1455.12623414 -143.49736399 1498.00098693 -97.92414188 1523.62361526 C-64.83687125 1541.80259844 -29.20926543 1549.49032293 8.35229683 1549.38590336 C10.16994403 1549.38829941 11.98759025 1549.39156856 13.80523452 1549.39561996 C18.7683157 1549.40417854 23.73132053 1549.40043925 28.69440275 1549.39456423 C34.1061948 1549.39037287 39.51797345 1549.39780779 44.92976218 1549.40371674 C54.40461004 1549.41246142 63.87943627 1549.41379702 73.35428715 1549.41021347 C87.44459056 1549.40489841 101.5348731 1549.41100996 115.62517424 1549.4192964 C141.52722213 1549.43411071 167.42926085 1549.43638701 193.33131218 1549.43397602 C196.99832622 1549.43363755 200.66534026 1549.43331545 204.33235431 1549.43301034 C205.06079309 1549.4329493 205.78923187 1549.43288825 206.53974455 1549.43282536 C222.7048139 1549.43155182 238.86988194 1549.43415789 255.0349509 1549.43753641 C258.72948334 1549.43830716 262.4240158 1549.4390519 266.11854825 1549.43980142 C267.60140976 1549.44010312 269.08427128 1549.440405 270.56713279 1549.44070706 C271.66970533 1549.44093158 271.66970533 1549.44093158 272.79455206 1549.44116064 C274.28140169 1549.44146417 275.76825132 1549.44176886 277.25510095 1549.44207468 C319.4323285 1549.45074517 361.60955489 1549.45539361 403.78678322 1549.45112705 C404.56738546 1549.451049 405.3479877 1549.45097094 406.15224455 1549.45089052 C414.8561149 1549.45001777 423.55998525 1549.44908851 432.2638556 1549.44813988 C433.04963446 1549.44805437 433.83541331 1549.44796886 434.64500365 1549.44788076 C439.41161001 1549.44736197 439.41161001 1549.44736197 444.17821636 1549.44683854 C494.41274025 1549.4413057 544.64724575 1549.44883754 594.88176632 1549.46760941 C607.53531643 1549.47233202 620.18886655 1549.4770127 632.84241676 1549.48144627 C633.62165363 1549.48171959 634.40089049 1549.48199291 635.20374059 1549.48227452 C659.24156414 1549.49065305 683.27938615 1549.49532616 707.31721115 1549.49615383 C708.45017015 1549.49619338 708.45017015 1549.49619338 709.60601722 1549.49623373 C726.19130854 1549.49677466 742.77659975 1549.49640174 759.36189104 1549.49519402 C765.29729633 1549.49476843 771.23270162 1549.49440329 777.16810692 1549.49406457 C778.64456158 1549.49397927 780.12101623 1549.49389034 781.59747089 1549.49379776 C805.06961159 1549.49235229 828.54173975 1549.49945389 852.01387743 1549.51080172 C876.3331715 1549.52248291 900.6524426 1549.5229807 924.97173687 1549.51095829 C938.97368609 1549.50434548 952.97557648 1549.50592419 966.97752096 1549.51954681 C976.34975834 1549.52782609 985.72195191 1549.52504883 995.09418578 1549.51357154 C1000.42146854 1549.50734933 1005.74863701 1549.50562469 1011.07591292 1549.51743487 C1075.76456619 1549.65071743 1136.24235528 1539.76132365 1184.68523312 1493.13142776 C1185.23501827 1492.61274124 1185.78480343 1492.09405472 1186.35124874 1491.55965042 C1209.65129702 1469.50469065 1226.78250753 1442.27511934 1237.68523312 1412.13142776 C1237.94046749 1411.43243362 1238.19570187 1410.73343948 1238.45867062 1410.0132637 C1246.58225077 1387.34163841 1248.84161561 1363.94166639 1248.81945419 1340.01220894 C1248.82140952 1338.16850217 1248.82379927 1336.32479583 1248.82658216 1334.48109013 C1248.83296268 1329.41307866 1248.83314535 1324.3450831 1248.83230475 1319.27706838 C1248.83247464 1313.76376749 1248.8383851 1308.25047222 1248.84354562 1302.73717415 C1248.85286285 1291.80136105 1248.85679218 1280.865552 1248.85906458 1269.92973542 C1248.86187673 1257.50335433 1248.86982225 1245.07697813 1248.87813223 1232.65059974 C1248.89971201 1200.3179869 1248.91060797 1167.98537323 1248.91960847 1135.65275483 C1248.92254396 1125.16556935 1248.92591406 1114.67838407 1248.92957568 1104.19119883 C1248.92983397 1103.45098815 1248.93009226 1102.71077747 1248.93035838 1101.94813616 C1248.93140466 1098.95325469 1248.93245308 1095.95837322 1248.9335042 1092.96349175 C1248.95146821 1041.73323937 1248.96684554 990.50298666 1248.97426324 939.27273158 C1248.9748472 935.25006956 1248.97544446 931.22740755 1248.97604085 927.20474554 C1248.97615897 926.40679557 1248.97627709 925.6088456 1248.97639879 924.78671533 C1248.97759614 916.70266913 1248.97880491 908.61862294 1248.98003132 900.53457675 C1248.98027656 898.9137306 1248.98052041 897.29288445 1248.98076289 895.6720383 C1248.98855001 843.63357843 1249.0109282 791.59514113 1249.04343932 739.55669112 C1249.0667963 702.158087 1249.08834205 664.75948737 1249.09544563 627.36087608 C1249.09559265 626.5911763 1249.09573967 625.82147651 1249.09589115 625.02845249 C1249.09907653 608.12854761 1249.10112704 591.22864277 1249.1021187 574.32873762 C1249.10253341 567.52823789 1249.1030883 560.72773818 1249.10368061 553.92723846 C1249.10374128 553.18299653 1249.10380195 552.43875459 1249.10386445 551.67195986 C1249.10587753 527.7333502 1249.12009442 503.79476726 1249.14062561 479.85616691 C1249.16124343 455.73092183 1249.16908153 431.60570757 1249.15954785 407.48045457 C1249.15364515 392.5227051 1249.15836195 377.56505551 1249.18128649 362.60732061 C1249.19481985 353.05358373 1249.19369517 343.49991958 1249.18088454 333.94618193 C1249.17400491 328.5101618 1249.17325987 323.07434387 1249.19044463 317.63834201 C1249.3461682 265.05142782 1239.71621284 217.51069962 1206.37663937 175.29549026 C1204.26585393 172.59485485 1202.24665736 169.83186133 1200.23992062 167.05330276 C1198.22297507 164.5599932 1195.97434618 162.37677837 1193.68523312 160.13142776 C1192.37425655 158.80304886 1192.37425655 158.80304886 1191.03679562 157.44783401 C1183.65359085 149.96967365 1183.65359085 149.96967365 1175.68523312 143.13142776 C1175.02523312 143.13142776 1174.36523312 143.13142776 1173.68523312 143.13142776 C1173.35523312 142.14142776 1173.02523312 141.15142776 1172.68523312 140.13142776 C1171.10827411 138.79412791 1171.10827411 138.79412791 1169.06023312 137.56892776 C1168.25456905 137.05330276 1167.44890499 136.53767776 1166.61882687 136.00642776 C1165.65074093 135.38767776 1164.68265499 134.76892776 1163.68523312 134.13142776 C1161.81735081 132.89133993 1159.95578097 131.64213042 1158.09465694 130.39192581 C1155.51852131 128.66355154 1152.93345316 126.94883218 1150.34734249 125.23543167 C1148.99251867 124.33553253 1147.64279793 123.42795435 1146.29460812 122.51814651 C1142.17919239 119.81635936 1138.02557583 117.96013844 1133.42351437 116.22127151 C1130.43572492 115.03212279 1127.57923555 113.6579258 1124.68523312 112.25642776 C1101.32118199 101.36055977 1076.11912727 96.64551537 1050.48324299 96.75111103 C1048.60719686 96.74766953 1046.73115277 96.74291739 1044.85511285 96.73699006 C1039.72990616 96.72455785 1034.60486733 96.73059531 1029.47966012 96.73982374 C1023.89225235 96.74656144 1018.3048725 96.73585346 1012.71747077 96.7274279 C1002.93449381 96.71507675 993.15156484 96.71384187 983.36858273 96.71997833 C972.87836534 96.72654078 962.38817641 96.72671336 951.89795971 96.7189312 C951.23379938 96.7184432 950.56963906 96.7179552 949.88535266 96.71745242 C947.18003326 96.71544409 944.4747139 96.71339096 941.76939458 96.71128067 C914.34658172 96.68991216 886.9237911 96.68782052 859.500972 96.69333771 C855.71529164 96.69409348 851.92961128 96.69482109 848.14393091 96.69551969 C847.39193862 96.69565878 846.63994633 96.69579787 845.8651664 96.69594118 C829.18066988 96.69889673 812.49617602 96.69587744 795.81167998 96.6916234 C793.52644407 96.69104124 791.24120816 96.69047239 788.95597225 96.68990776 C782.82957921 96.68839273 776.70318617 96.68686582 770.57679314 96.68531476 C727.8221951 96.67456186 685.06759953 96.66985216 642.31300068 96.67778492 C641.1047525 96.67800738 641.1047525 96.67800738 639.87209526 96.67823433 C630.890477 96.67989325 621.90885877 96.68164283 612.92724054 96.68342734 C611.71094163 96.68366888 611.71094163 96.68366888 610.47007102 96.6839153 C605.55128005 96.68489277 605.55128005 96.68489277 600.63248908 96.68587648 C548.77489484 96.69626612 496.91733454 96.6885035 445.05974483 96.66487503 C431.99353892 96.65893131 418.92733298 96.65303961 405.8611269 96.64746571 C405.0565182 96.64712194 404.2519095 96.64677817 403.42291872 96.64642398 C378.61206983 96.63589121 353.80122378 96.63030242 328.99037266 96.62990856 C328.2110661 96.62989578 327.43175954 96.629883 326.62883764 96.62986983 C309.51870335 96.6296415 292.4085693 96.63064796 275.29843515 96.63277888 C269.17570193 96.6335336 263.05296871 96.63421182 256.93023547 96.63485903 C255.40714385 96.63502172 253.88405223 96.63518939 252.36096061 96.63536205 C228.12579483 96.63807947 203.89064971 96.62954503 179.65548837 96.61550794 C154.55718429 96.60107754 129.45891937 96.60157923 104.36061661 96.61888266 C101.66071541 96.62068671 98.96081417 96.62242692 96.2609129 96.6241169 C95.59834385 96.6245374 94.9357748 96.6249579 94.2531279 96.62539114 C83.16340267 96.63204896 72.07373567 96.62297459 60.98401909 96.60964797 C51.31612228 96.5991438 41.64830191 96.6032796 31.9804126 96.61912801 C26.47849948 96.62774328 20.97678312 96.63022495 15.47488026 96.61485869 C-9.1981754 96.5508082 -32.5500153 96.67035848 -56.31476688 104.13142776 Z"
14 |         fill="currentColor"
15 |         transform="translate(299.3147668838501,-0.13142776489257813)"
16 |       />
17 |       {/* 4-pointed star */}
18 |       <path
19 |         d="M0 0 C4.1085097 1.36950323 7.64777202 2.68292748 11.4921875 4.5390625 C12.51828125 5.03277344 13.544375 5.52648437 14.6015625 6.03515625 C15.68179687 6.55980469 16.76203125 7.08445312 17.875 7.625 C18.9990625 8.16769531 20.123125 8.71039063 21.28125 9.26953125 C27.83097248 12.43899465 34.35423096 15.6559677 40.8503418 18.93359375 C48.89729969 22.9790813 57.03620354 26.83494612 65.16015625 30.72265625 C71.7059122 33.85570816 78.24136117 37.00986829 84.77493286 40.16821289 C95.95454422 45.57222343 107.14699261 50.94870303 118.3515625 56.30078125 C134.08086009 63.81454993 149.78696862 71.37627787 165.48260498 78.96005249 C175.42739815 83.76454358 185.38561181 88.54035331 195.3515625 93.30078125 C201.27883841 96.13219695 207.20262806 98.97084259 213.125 101.8125 C214.15471924 102.30637207 215.18443848 102.80024414 216.24536133 103.30908203 C221.87366997 106.01084343 227.49669121 108.72318436 233.11328125 111.44921875 C235.5544341 112.63333577 237.99584254 113.81692385 240.4375 115 C241.04268463 115.29325165 241.64786926 115.5865033 242.27139282 115.88864136 C252.15995289 120.67531016 262.11547104 125.30623231 272.10302734 129.8828125 C278.42827834 132.78908809 284.6918686 135.78038432 290.89794922 138.93212891 C300.10204504 143.59715346 309.43878532 147.99321267 318.74755859 152.44433594 C341.04253143 163.10816772 363.3276911 173.79511632 385.52734375 184.65625 C401.00105031 192.22401929 416.47831173 199.74717118 432.13378906 206.93334961 C445.47638654 213.08946402 458.64856785 219.59655506 471.84545898 226.05726624 C472.70212158 226.47664963 473.55878418 226.89603302 474.44140625 227.328125 C475.28485596 227.74110336 476.12830566 228.15408173 476.99731445 228.57957458 C484.26740418 232.13663445 491.56066972 235.64248528 498.8684082 239.12158203 C505.79569884 242.42004557 512.70757064 245.7497802 519.61526489 249.08901978 C525.07573042 251.72831699 530.53787128 254.36414706 536 257 C537.03721191 257.50063965 538.07442383 258.0012793 539.14306641 258.51708984 C549.83551405 263.67259682 560.56241592 268.72606702 571.375 273.625 C579.13080722 277.140137 586.70233796 280.85954609 594.1796875 284.93139648 C599.58710679 287.85312585 605.09769504 290.24732823 610.78125 292.59375 C614.78234614 294.34180171 618.5362633 296.40621154 622.3046875 298.60546875 C625.0651501 300.03370809 627.87947372 301.05453329 630.79638672 302.11401367 C634.90398339 303.76551692 638.74262503 305.94568126 642.625 308.0625 C644.26063479 308.95361235 645.89860699 309.84044987 647.5390625 310.72265625 C648.25271973 311.11203369 648.96637695 311.50141113 649.70166016 311.90258789 C652.36603902 313.17477644 655.19899293 314.06633098 658 315 C658 399.48 658 483.96 658 571 C650.08 574.96 650.08 574.96 642 579 C639.76706177 580.12027588 639.76706177 580.12027588 637.48901367 581.26318359 C628.46508901 585.79053011 619.40214058 590.23127616 610.3125 594.625 C608.87912918 595.3184783 607.44578002 596.01200136 606.01245117 596.70556641 C603.819543 597.76655783 601.62655136 598.82737502 599.43330383 599.88766479 C595.31525826 601.87862298 591.19927922 603.87374376 587.08572388 605.8739624 C574.711528 611.89027133 562.33260652 617.87939312 549.7512207 623.45458984 C543.84106999 626.09136145 538.05602796 628.94341472 532.27586365 631.85063171 C524.96512701 635.5276124 517.61336678 639.11730262 510.25 642.6875 C509.54843781 643.0280542 508.84687561 643.3686084 508.12405396 643.71948242 C497.03430628 649.09886442 485.89526214 654.35996326 474.68652344 659.48706055 C468.09899452 662.51152031 461.59600937 665.67236319 455.12182617 668.92991638 C447.92913695 672.54774178 440.68646706 676.05915497 433.4375 679.5625 C432.00932877 680.25335604 430.58118818 680.94427543 429.15307617 681.63525391 C426.95298522 682.69972949 424.75285224 683.76411721 422.55250549 684.82806396 C417.61199052 687.21757124 412.67606418 689.61635239 407.7421875 692.01953125 C395.44732974 698.00297269 383.15557785 703.97052413 370.6496582 709.50537109 C364.774553 712.12232554 359.02653448 714.9582799 353.28218079 717.84747314 C345.96933177 721.52549643 338.61548639 725.1162749 331.25 728.6875 C330.54843781 729.0280542 329.84687561 729.3686084 329.12405396 729.71948242 C318.03430628 735.09886442 306.89526214 740.35996326 295.68652344 745.48706055 C289.09899452 748.51152031 282.59600937 751.67236319 276.12182617 754.92991638 C268.92913695 758.54774178 261.68646706 762.05915497 254.4375 765.5625 C253.00932877 766.25335604 251.58118818 766.94427543 250.15307617 767.63525391 C247.95298522 768.69972949 245.75285224 769.76411721 243.55250549 770.82806396 C238.61199052 773.21757124 233.67606418 775.61635239 228.7421875 778.01953125 C216.44732974 784.00297269 204.15557785 789.97052413 191.6496582 795.50537109 C185.85518354 798.08641019 180.1794186 800.88190241 174.51371765 803.73170471 C166.00036446 808.01331551 157.44005409 812.19798064 148.875 816.375 C148.02171112 816.79141754 147.16842224 817.20783508 146.28927612 817.63687134 C135.67599355 822.81189993 125.05618309 827.94855956 114.24755859 832.70654297 C108.50609957 835.24677936 102.90090356 838.02298798 97.29385376 840.84390259 C90.04362429 844.49012019 82.74423558 848.03123608 75.4375 851.5625 C74.00932877 852.25335604 72.58118818 852.94427543 71.15307617 853.63525391 C68.95298522 854.69972949 66.75285224 855.76411721 64.55250549 856.82806396 C59.61199058 859.21757121 54.67605476 861.61633313 49.7421875 864.01953125 C14.17850815 881.32859674 14.17850815 881.32859674 0 887 C0 824.63 0 762.26 0 698 C44.61388456 676.23712949 44.61388456 676.23712949 58.4921875 669.62890625 C65.13212163 666.4656916 71.75305048 663.26313049 78.375 660.0625 C87.41069933 655.69584577 96.45082915 651.33869329 105.5 647 C114.50707172 642.6812542 123.50576209 638.3453875 132.5 634 C146.66884626 627.15498184 160.85394381 620.34409755 175.04296875 613.54101562 C187.82144994 607.4135621 200.59198948 601.26998874 213.35241699 595.1050415 C222.74198372 590.56888872 232.1395158 586.04958218 241.54248047 581.54125977 C249.57680403 577.6874128 257.60187156 573.8145749 265.625 569.9375 C275.16031809 565.32976243 284.70557257 560.74350451 294.26269531 556.18115234 C302.59963894 552.19826815 310.92020857 548.18169596 319.23925781 544.16162109 C327.65369754 540.09559791 336.07317272 536.04030597 344.5 532 C353.50707172 527.6812542 362.50576209 523.3453875 371.5 519 C385.66884626 512.15498184 399.85394381 505.34409755 414.04296875 498.54101562 C426.82144994 492.4135621 439.59198948 486.26998874 452.35241699 480.1050415 C461.73166086 475.57387575 471.11832162 471.05840488 480.51171875 466.55664062 C486.24216553 463.80881435 491.96745473 461.05064371 497.6875 458.28125 C498.33867798 457.96615479 498.98985596 457.65105957 499.6607666 457.32641602 C502.83131196 455.79222402 506.00122059 454.25674712 509.16992188 452.71875 C515.40264064 449.70190462 521.65222563 446.76697857 528 444 C525.31154013 441.87701096 522.6957885 440.24802735 519.59765625 438.79296875 C518.72254395 438.37861572 517.84743164 437.9642627 516.94580078 437.53735352 C515.99431152 437.09190186 515.04282227 436.6464502 514.0625 436.1875 C511.97756402 435.20115591 509.89294343 434.21414496 507.80859375 433.2265625 C506.70241699 432.70352539 505.59624023 432.18048828 504.45654297 431.64160156 C499.11271151 429.10367793 493.78722411 426.52793611 488.46200562 423.95126343 C485.37896539 422.45965481 482.29498318 420.96999714 479.2109375 419.48046875 C478.60370346 419.18717178 477.99646942 418.89387482 477.37083435 418.59169006 C467.25671199 413.70806638 457.12752354 408.85575101 447 404 C433.20693054 397.38676323 419.42088603 390.75933943 405.64758301 384.1050415 C396.25801628 379.56888872 386.8604842 375.04958218 377.45751953 370.54125977 C369.42319597 366.6874128 361.39812844 362.8145749 353.375 358.9375 C344.75851335 354.77376798 336.1361993 350.62272434 327.5 346.5 C318.24095096 342.07994142 308.99861843 337.62570744 299.76074219 333.16162109 C291.34630246 329.09559791 282.92682728 325.04030597 274.5 321 C265.49292828 316.6812542 256.49423791 312.3453875 247.5 308 C234.34403824 301.64430981 221.17470104 295.31675332 208 289 C194.20693054 282.38676323 180.42088603 275.75933943 166.64758301 269.1050415 C158.27074055 265.05814154 149.88870167 261.0222749 141.5 257 C132.4929398 252.68123011 123.49423798 248.34538753 114.5 244 C101.34403824 237.64430981 88.17470104 231.31675332 75 225 C52.95075452 214.42825008 52.95075452 214.42825008 42.71875 209.484375 C42.09695862 209.18397308 41.47516724 208.88357117 40.83453369 208.57406616 C37.85114103 207.13252306 34.86814648 205.69016959 31.88574219 204.24658203 C21.27848219 199.11815163 10.63824734 194.06583207 0 189 C0 126.63 0 64.26 0 0 Z"
20 |         fill="currentColor"
21 |         transform="translate(494,399)"
22 |       />
23 |     </svg>
24 |   );
25 | }
26 | 
```
Page 15/20FirstPrevNextLast