Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Add a new Seer embed widget — a rich component rendered inline in Seer's markdown output via tag syntax. Covers schema, component, registration, and backend codegen. Use when asked to "add an embed", "new seer embed", "create a seer widget", "add a markdown widget", "new seer tag", or "embed widget".
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 17% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 49% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 37% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 8% | 0% |
| case-14 | ✗→✓ | ▲ Improved | -1% | 0% |
Seer embeds are rich widgets rendered inline in Seer's markdown output using Markdoc-style tag syntax ({% name %}{ ... }{% /name %}). Each embed has a Zod schema, a React component, and a registry entry.
static/app/components/seer/markdown/embeds/schemas.ts to see existing schemas.static/app/components/seer/markdown/embeds/index.ts to see registered embeds.In static/app/components/seer/markdown/embeds/schemas.ts, add an entry to SEER_EMBED_SCHEMAS:
tsexport const SEER_EMBED_SCHEMAS = { // ...existing entries myEmbed: { description: "One sentence describing what this embed does—this passes through directly to the LLM's system prompt.", level: ['inline'], // 'inline', 'block', or both schema: z.object({ // Define the data shape the LLM will produce someField: z.string(), optionalField: z.number().optional(), }), examples: [{label: 'Basic', data: {someField: 'hello'}}], // featureFlag: 'organizations:seer-explorer-my-embed', // optional }, } as const satisfies Record<string, SeerEmbedSchema>;
Key decisions:
description: Write for the LLM — it uses this to decide when to emit the embed. Be specific about the use case.level: Use ['inline'] for widgets that flow within text (timestamps, badges). Use ['block'] for widgets that need their own line (cards, charts). Use both if the embed adapts.schema: Use Zod. Keep it flat and simple — the LLM has to produce valid JSON. Use .default() for optional fields with sensible defaults. Use .enum() to constrain string values.examples: An array of {label, data, level?} objects. Each data must be valid against the schema. These are included in the generated JSON sent to the LLM as few-shot examples. In the stories page, all examples for an embed are composed into a single markdown block and rendered through one <SeerMarkdown> — inline examples are wrapped in prose text, block examples are appended at the end. Use multiple examples to show different prop combinations or block vs inline rendering. Set level on an example only when it differs from the schema's default (first entry in level).featureFlag: Set this to gate the embed behind a feature flag. The backend filters it out of the schema sent to the LLM when the flag is off.Create static/app/components/seer/markdown/embeds/components/<name>.tsx:
tsximport {defineSeerEmbed} from 'sentry/components/seer/markdown/embeds/utils'; export const MyEmbed = defineSeerEmbed({ name: 'myEmbed', // must match the key in SEER_EMBED_SCHEMAS render({someField, optionalField}) { // Props are typed from the Zod schema — already validated return <span>{someField}</span>; }, });
What defineSeerEmbed does for you:
safeParses the data prop against itnull for invalid data (logs a warning in dev)displayName on the component (used by the registry)Rules:
name parameter must match the key in SEER_EMBED_SCHEMAS exactly.render function receives the Zod output type — props are already parsed and validated.DateTime, TimeSince, Link, etc.) rather than building from scratch.In static/app/components/seer/markdown/embeds/index.ts, import and add it to the embeds array:
tsimport {MyEmbed} from './components/myEmbed'; import {Timestamp} from './components/timestamp'; import {SeerEmbedRegistry} from './registry'; const embeds = [Timestamp, MyEmbed]; for (const embed of embeds) { SeerEmbedRegistry.register(embed.displayName, embed); }
Registration uses displayName (set by defineSeerEmbed) as the registry key.
Run the codegen script to update the JSON Schema file the backend sends to the Seer agent:
bashpnpm gen:embed-widgets
This writes to src/sentry/seer/agent/embed_widgets.generated.json. Commit this generated file — it's checked in, not gitignored.
pnpm run lint:js on your new files.pnpm run typecheck to confirm the schema types flow through.tsx<SeerMarkdown raw={`{% myEmbed %}{"someField":"hello"}{% /myEmbed %}`} />
| File | What to do | | ------------------------------------------------------------------ | --------------------------------------- | | static/app/components/seer/markdown/embeds/schemas.ts | Add Zod schema entry | | static/app/components/seer/markdown/embeds/components/<name>.tsx | Create component with defineSeerEmbed | | static/app/components/seer/markdown/embeds/index.ts | Import and register | | src/sentry/seer/agent/embed_widgets.generated.json | Regenerated by pnpm gen:embed-widgets |
If the embed should be gated:
featureFlag: 'organizations:seer-explorer-<name>' to the schema entry.src/sentry/features/temporary.py.src/sentry/seer/agent/embed_widgets.py) automatically filters flagged embeds using features.has().Other measured skills in the registry, with their headline benchmark lift.