[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-sentry-document-api-endpoint":3,"mdc-lizde5-key":36,"related-org-sentry-document-api-endpoint":707,"related-repo-sentry-document-api-endpoint":881},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":12,"stars":24,"repoUrl":25,"updatedAt":26,"license":27,"forks":28,"topics":29,"repo":31,"sourceUrl":34,"mdContent":35},"document-api-endpoint","document and type Sentry API endpoints","Document and type a Sentry API endpoint. Write or fix @extend_schema decorators, specify response TypedDicts, type request parameters, correct type drift between the declared schema and the runtime response, and validate the generated spec. Use when asked to \"document an endpoint\", \"add OpenAPI docs\", \"add\u002Ffix @extend_schema\", \"type an endpoint response\", \"fix the response type\", \"fix type drift\", \"reuse a response type\", \"split an overloaded endpoint\", \"specify the response schema\", \"add a TypedDict response\", \"migrate a legacy api-docs path\", \"fix a parameter type\", or \"make an endpoint public\" \u002F \"promote an endpoint\" (promotion is one section here).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},"sentry","Sentry","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fsentry.png","getsentry",[13,17,20,21],{"name":14,"slug":15,"type":16},"Documentation","documentation","tag",{"name":18,"slug":19,"type":16},"API Development","api-development",{"name":9,"slug":8,"type":16},{"name":22,"slug":23,"type":16},"Code Analysis","code-analysis",861,"https:\u002F\u002Fgithub.com\u002Fgetsentry\u002Fskills","2026-06-05T07:29:04.479281",null,45,[30],"tag-production",{"repoUrl":25,"stars":24,"forks":28,"topics":32,"description":33},[30],"Agent Skills used by the Sentry team for development.","https:\u002F\u002Fgithub.com\u002Fgetsentry\u002Fskills\u002Ftree\u002FHEAD\u002Fskills\u002Fdocument-api-endpoint","---\nname: document-api-endpoint\ndescription: Document and type a Sentry API endpoint. Write or fix @extend_schema decorators, specify response TypedDicts, type request parameters, correct type drift between the declared schema and the runtime response, and validate the generated spec. Use when asked to \"document an endpoint\", \"add OpenAPI docs\", \"add\u002Ffix @extend_schema\", \"type an endpoint response\", \"fix the response type\", \"fix type drift\", \"reuse a response type\", \"split an overloaded endpoint\", \"specify the response schema\", \"add a TypedDict response\", \"migrate a legacy api-docs path\", \"fix a parameter type\", or \"make an endpoint public\" \u002F \"promote an endpoint\" (promotion is one section here).\n---\n\n# Document & Type a Sentry API Endpoint\n\nAdd or fix OpenAPI docs for a Sentry endpoint with drf-spectacular. Full reference is at https:\u002F\u002Fdevelop.sentry.dev\u002Fbackend\u002Fapi\u002Fpublic\u002F, the most useful section to you will be https:\u002F\u002Fdevelop.sentry.dev\u002Fbackend\u002Fapi\u002Fpublic\u002F#5-method-decorator. This skill captures the non-obvious lessons on top of it. Most of the work is making the declared schema match what the endpoint actually returns. Before documenting, identify which endpoint class serves the route and what it does; the MCP tool that calls it is usually the fastest way to confirm its behavior. Promoting a PRIVATE\u002FEXPERIMENTAL endpoint to PUBLIC is one application (see below).\n\n## Workflow\n\n1. Class-level `@extend_schema(tags=[...])` — use the closest existing `OPENAPI_TAGS` entry.\n2. Method-level `@extend_schema(operation_id=..., parameters=[...], responses={...}, examples=...)`.\n3. Reuse `src\u002Fsentry\u002Fapidocs\u002Fparameters.py` and `examples\u002F*.py`; ensure `owner = ApiOwner.\u003CTEAM>` is set.\n4. If a legacy `api-docs\u002Fpaths\u002F**\u002F*.json` covers the path, remove it (see lesson 4).\n5. Validate, then verify against the live endpoint (lesson 1).\n\n## Lessons\n\n### 1. Carefully compare what the code does vs declared types\nIdeally, hit the live endpoint with a real token and diff the keys and types against your TypedDict. Serializers are sometimes inaccurate. Look out for counts coming back as floats instead of integers, IDs declared `int` emitted as strings, nested types declaring the wrong number of fields. Correct the declared type to match runtime.\n```bash\ncurl -s -H \"Authorization: Bearer $TOKEN\" \"https:\u002F\u002Fus.sentry.io\u002Fapi\u002F0\u002F\u003Cendpoint>\" | jq 'keys'\n```\n\n### 2. Reuse the canonical response type\nMatch the codebase's `XxxResponseOptional(TypedDict, total=False)` mixin (main class declares required fields). Nullable-vs-absent: `T | None` = key always present, value may be null; `NotRequired[T]` = key only set under a condition (e.g. an `expand` query param). Reuse the existing canonical type instead of re-declaring a second or third copy in a `*_types.py`. If there's no clean canonical type to reuse (e.g. a payload proxied from another service like vroom\u002Fprofiling), type it `dict[str, Any]` rather than inventing a new mirror, and confirm the shape from the owning service's repo, not just the serializer.\n\n### 3. Infer the type. Avoid `cast` and `# type: ignore`\nWhen a serializer returns a base type plus extra fields, refactor the producing code so the response type is inferred rather than forced.\n\n### 4. Legacy doc migration is all-or-nothing per path\nDelete the `api-docs\u002Fpaths\u002F**\u002F*.json` file AND its `$ref` in `api-docs\u002Fopenapi.json`. drf-spectacular's `APPEND_PATHS` does not merge HTTP methods, so once any method on a path uses `@extend_schema`, all *legacy* methods on that path vanish — migrate every method on the path in one commit.\n\n## Promoting to PUBLIC\n\nDo the workflow above, then on the concrete endpoint only (leave siblings PRIVATE):\n\n- Bump `publish_status[\u003CMETHOD>]` → `PUBLIC` and set `owner = ApiOwner.\u003CTEAM>`.\n- Remove the method from `API_OWNERSHIP_ALLOWLIST_DONT_MODIFY` in the same change as the flip.\n- If the endpoint is redundant or being renamed, delete or deprecate the old version in its own change first, then stack the publish on top.\n- Note in the PR if scopes widen (`event:read` → `event:{admin,read,write}`) — that's drf-spectacular regenerating from `permission_classes`, documentation-only.\n\nThe change reaches the `@sentry\u002Fapi` SDK \u002F MCP only after `sentry-api-schema` regenerates downstream.\n\n## Validate\n\n```bash\nmake build-api-docs\npnpm run validate-api-examples\n.venv\u002Fbin\u002Fpytest -q --reuse-db tests\u002Fapidocs\u002Fendpoints\u002F\u003Carea>\u002Ftest_\u003Cname>.py\n.venv\u002Fbin\u002Fprek run -q --files \u003Cchanged paths>\n```\n",{"data":37,"body":38},{"name":4,"description":6},{"type":39,"children":40},"root",[41,50,74,81,168,174,181,194,288,294,347,366,371,377,429,435,440,517,538,544,701],{"type":42,"tag":43,"props":44,"children":46},"element","h1",{"id":45},"document-type-a-sentry-api-endpoint",[47],{"type":48,"value":49},"text","Document & Type a Sentry API Endpoint",{"type":42,"tag":51,"props":52,"children":53},"p",{},[54,56,64,66,72],{"type":48,"value":55},"Add or fix OpenAPI docs for a Sentry endpoint with drf-spectacular. Full reference is at ",{"type":42,"tag":57,"props":58,"children":62},"a",{"href":59,"rel":60},"https:\u002F\u002Fdevelop.sentry.dev\u002Fbackend\u002Fapi\u002Fpublic\u002F",[61],"nofollow",[63],{"type":48,"value":59},{"type":48,"value":65},", the most useful section to you will be ",{"type":42,"tag":57,"props":67,"children":70},{"href":68,"rel":69},"https:\u002F\u002Fdevelop.sentry.dev\u002Fbackend\u002Fapi\u002Fpublic\u002F#5-method-decorator",[61],[71],{"type":48,"value":68},{"type":48,"value":73},". This skill captures the non-obvious lessons on top of it. Most of the work is making the declared schema match what the endpoint actually returns. Before documenting, identify which endpoint class serves the route and what it does; the MCP tool that calls it is usually the fastest way to confirm its behavior. Promoting a PRIVATE\u002FEXPERIMENTAL endpoint to PUBLIC is one application (see below).",{"type":42,"tag":75,"props":76,"children":78},"h2",{"id":77},"workflow",[79],{"type":48,"value":80},"Workflow",{"type":42,"tag":82,"props":83,"children":84},"ol",{},[85,108,121,150,163],{"type":42,"tag":86,"props":87,"children":88},"li",{},[89,91,98,100,106],{"type":48,"value":90},"Class-level ",{"type":42,"tag":92,"props":93,"children":95},"code",{"className":94},[],[96],{"type":48,"value":97},"@extend_schema(tags=[...])",{"type":48,"value":99}," — use the closest existing ",{"type":42,"tag":92,"props":101,"children":103},{"className":102},[],[104],{"type":48,"value":105},"OPENAPI_TAGS",{"type":48,"value":107}," entry.",{"type":42,"tag":86,"props":109,"children":110},{},[111,113,119],{"type":48,"value":112},"Method-level ",{"type":42,"tag":92,"props":114,"children":116},{"className":115},[],[117],{"type":48,"value":118},"@extend_schema(operation_id=..., parameters=[...], responses={...}, examples=...)",{"type":48,"value":120},".",{"type":42,"tag":86,"props":122,"children":123},{},[124,126,132,134,140,142,148],{"type":48,"value":125},"Reuse ",{"type":42,"tag":92,"props":127,"children":129},{"className":128},[],[130],{"type":48,"value":131},"src\u002Fsentry\u002Fapidocs\u002Fparameters.py",{"type":48,"value":133}," and ",{"type":42,"tag":92,"props":135,"children":137},{"className":136},[],[138],{"type":48,"value":139},"examples\u002F*.py",{"type":48,"value":141},"; ensure ",{"type":42,"tag":92,"props":143,"children":145},{"className":144},[],[146],{"type":48,"value":147},"owner = ApiOwner.\u003CTEAM>",{"type":48,"value":149}," is set.",{"type":42,"tag":86,"props":151,"children":152},{},[153,155,161],{"type":48,"value":154},"If a legacy ",{"type":42,"tag":92,"props":156,"children":158},{"className":157},[],[159],{"type":48,"value":160},"api-docs\u002Fpaths\u002F**\u002F*.json",{"type":48,"value":162}," covers the path, remove it (see lesson 4).",{"type":42,"tag":86,"props":164,"children":165},{},[166],{"type":48,"value":167},"Validate, then verify against the live endpoint (lesson 1).",{"type":42,"tag":75,"props":169,"children":171},{"id":170},"lessons",[172],{"type":48,"value":173},"Lessons",{"type":42,"tag":175,"props":176,"children":178},"h3",{"id":177},"_1-carefully-compare-what-the-code-does-vs-declared-types",[179],{"type":48,"value":180},"1. Carefully compare what the code does vs declared types",{"type":42,"tag":51,"props":182,"children":183},{},[184,186,192],{"type":48,"value":185},"Ideally, hit the live endpoint with a real token and diff the keys and types against your TypedDict. Serializers are sometimes inaccurate. Look out for counts coming back as floats instead of integers, IDs declared ",{"type":42,"tag":92,"props":187,"children":189},{"className":188},[],[190],{"type":48,"value":191},"int",{"type":48,"value":193}," emitted as strings, nested types declaring the wrong number of fields. Correct the declared type to match runtime.",{"type":42,"tag":195,"props":196,"children":201},"pre",{"className":197,"code":198,"language":199,"meta":200,"style":200},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","curl -s -H \"Authorization: Bearer $TOKEN\" \"https:\u002F\u002Fus.sentry.io\u002Fapi\u002F0\u002F\u003Cendpoint>\" | jq 'keys'\n","bash","",[202],{"type":42,"tag":92,"props":203,"children":204},{"__ignoreMap":200},[205],{"type":42,"tag":206,"props":207,"children":210},"span",{"class":208,"line":209},"line",1,[211,217,223,228,234,239,245,250,254,259,263,268,273,278,283],{"type":42,"tag":206,"props":212,"children":214},{"style":213},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[215],{"type":48,"value":216},"curl",{"type":42,"tag":206,"props":218,"children":220},{"style":219},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[221],{"type":48,"value":222}," -s",{"type":42,"tag":206,"props":224,"children":225},{"style":219},[226],{"type":48,"value":227}," -H",{"type":42,"tag":206,"props":229,"children":231},{"style":230},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[232],{"type":48,"value":233}," \"",{"type":42,"tag":206,"props":235,"children":236},{"style":219},[237],{"type":48,"value":238},"Authorization: Bearer ",{"type":42,"tag":206,"props":240,"children":242},{"style":241},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[243],{"type":48,"value":244},"$TOKEN",{"type":42,"tag":206,"props":246,"children":247},{"style":230},[248],{"type":48,"value":249},"\"",{"type":42,"tag":206,"props":251,"children":252},{"style":230},[253],{"type":48,"value":233},{"type":42,"tag":206,"props":255,"children":256},{"style":219},[257],{"type":48,"value":258},"https:\u002F\u002Fus.sentry.io\u002Fapi\u002F0\u002F\u003Cendpoint>",{"type":42,"tag":206,"props":260,"children":261},{"style":230},[262],{"type":48,"value":249},{"type":42,"tag":206,"props":264,"children":265},{"style":230},[266],{"type":48,"value":267}," |",{"type":42,"tag":206,"props":269,"children":270},{"style":213},[271],{"type":48,"value":272}," jq",{"type":42,"tag":206,"props":274,"children":275},{"style":230},[276],{"type":48,"value":277}," '",{"type":42,"tag":206,"props":279,"children":280},{"style":219},[281],{"type":48,"value":282},"keys",{"type":42,"tag":206,"props":284,"children":285},{"style":230},[286],{"type":48,"value":287},"'\n",{"type":42,"tag":175,"props":289,"children":291},{"id":290},"_2-reuse-the-canonical-response-type",[292],{"type":48,"value":293},"2. Reuse the canonical response type",{"type":42,"tag":51,"props":295,"children":296},{},[297,299,305,307,313,315,321,323,329,331,337,339,345],{"type":48,"value":298},"Match the codebase's ",{"type":42,"tag":92,"props":300,"children":302},{"className":301},[],[303],{"type":48,"value":304},"XxxResponseOptional(TypedDict, total=False)",{"type":48,"value":306}," mixin (main class declares required fields). Nullable-vs-absent: ",{"type":42,"tag":92,"props":308,"children":310},{"className":309},[],[311],{"type":48,"value":312},"T | None",{"type":48,"value":314}," = key always present, value may be null; ",{"type":42,"tag":92,"props":316,"children":318},{"className":317},[],[319],{"type":48,"value":320},"NotRequired[T]",{"type":48,"value":322}," = key only set under a condition (e.g. an ",{"type":42,"tag":92,"props":324,"children":326},{"className":325},[],[327],{"type":48,"value":328},"expand",{"type":48,"value":330}," query param). Reuse the existing canonical type instead of re-declaring a second or third copy in a ",{"type":42,"tag":92,"props":332,"children":334},{"className":333},[],[335],{"type":48,"value":336},"*_types.py",{"type":48,"value":338},". If there's no clean canonical type to reuse (e.g. a payload proxied from another service like vroom\u002Fprofiling), type it ",{"type":42,"tag":92,"props":340,"children":342},{"className":341},[],[343],{"type":48,"value":344},"dict[str, Any]",{"type":48,"value":346}," rather than inventing a new mirror, and confirm the shape from the owning service's repo, not just the serializer.",{"type":42,"tag":175,"props":348,"children":350},{"id":349},"_3-infer-the-type-avoid-cast-and-type-ignore",[351,353,359,360],{"type":48,"value":352},"3. Infer the type. Avoid ",{"type":42,"tag":92,"props":354,"children":356},{"className":355},[],[357],{"type":48,"value":358},"cast",{"type":48,"value":133},{"type":42,"tag":92,"props":361,"children":363},{"className":362},[],[364],{"type":48,"value":365},"# type: ignore",{"type":42,"tag":51,"props":367,"children":368},{},[369],{"type":48,"value":370},"When a serializer returns a base type plus extra fields, refactor the producing code so the response type is inferred rather than forced.",{"type":42,"tag":175,"props":372,"children":374},{"id":373},"_4-legacy-doc-migration-is-all-or-nothing-per-path",[375],{"type":48,"value":376},"4. Legacy doc migration is all-or-nothing per path",{"type":42,"tag":51,"props":378,"children":379},{},[380,382,387,389,395,397,403,405,411,413,419,421,427],{"type":48,"value":381},"Delete the ",{"type":42,"tag":92,"props":383,"children":385},{"className":384},[],[386],{"type":48,"value":160},{"type":48,"value":388}," file AND its ",{"type":42,"tag":92,"props":390,"children":392},{"className":391},[],[393],{"type":48,"value":394},"$ref",{"type":48,"value":396}," in ",{"type":42,"tag":92,"props":398,"children":400},{"className":399},[],[401],{"type":48,"value":402},"api-docs\u002Fopenapi.json",{"type":48,"value":404},". drf-spectacular's ",{"type":42,"tag":92,"props":406,"children":408},{"className":407},[],[409],{"type":48,"value":410},"APPEND_PATHS",{"type":48,"value":412}," does not merge HTTP methods, so once any method on a path uses ",{"type":42,"tag":92,"props":414,"children":416},{"className":415},[],[417],{"type":48,"value":418},"@extend_schema",{"type":48,"value":420},", all ",{"type":42,"tag":422,"props":423,"children":424},"em",{},[425],{"type":48,"value":426},"legacy",{"type":48,"value":428}," methods on that path vanish — migrate every method on the path in one commit.",{"type":42,"tag":75,"props":430,"children":432},{"id":431},"promoting-to-public",[433],{"type":48,"value":434},"Promoting to PUBLIC",{"type":42,"tag":51,"props":436,"children":437},{},[438],{"type":48,"value":439},"Do the workflow above, then on the concrete endpoint only (leave siblings PRIVATE):",{"type":42,"tag":441,"props":442,"children":443},"ul",{},[444,471,484,489],{"type":42,"tag":86,"props":445,"children":446},{},[447,449,455,457,463,465,470],{"type":48,"value":448},"Bump ",{"type":42,"tag":92,"props":450,"children":452},{"className":451},[],[453],{"type":48,"value":454},"publish_status[\u003CMETHOD>]",{"type":48,"value":456}," → ",{"type":42,"tag":92,"props":458,"children":460},{"className":459},[],[461],{"type":48,"value":462},"PUBLIC",{"type":48,"value":464}," and set ",{"type":42,"tag":92,"props":466,"children":468},{"className":467},[],[469],{"type":48,"value":147},{"type":48,"value":120},{"type":42,"tag":86,"props":472,"children":473},{},[474,476,482],{"type":48,"value":475},"Remove the method from ",{"type":42,"tag":92,"props":477,"children":479},{"className":478},[],[480],{"type":48,"value":481},"API_OWNERSHIP_ALLOWLIST_DONT_MODIFY",{"type":48,"value":483}," in the same change as the flip.",{"type":42,"tag":86,"props":485,"children":486},{},[487],{"type":48,"value":488},"If the endpoint is redundant or being renamed, delete or deprecate the old version in its own change first, then stack the publish on top.",{"type":42,"tag":86,"props":490,"children":491},{},[492,494,500,501,507,509,515],{"type":48,"value":493},"Note in the PR if scopes widen (",{"type":42,"tag":92,"props":495,"children":497},{"className":496},[],[498],{"type":48,"value":499},"event:read",{"type":48,"value":456},{"type":42,"tag":92,"props":502,"children":504},{"className":503},[],[505],{"type":48,"value":506},"event:{admin,read,write}",{"type":48,"value":508},") — that's drf-spectacular regenerating from ",{"type":42,"tag":92,"props":510,"children":512},{"className":511},[],[513],{"type":48,"value":514},"permission_classes",{"type":48,"value":516},", documentation-only.",{"type":42,"tag":51,"props":518,"children":519},{},[520,522,528,530,536],{"type":48,"value":521},"The change reaches the ",{"type":42,"tag":92,"props":523,"children":525},{"className":524},[],[526],{"type":48,"value":527},"@sentry\u002Fapi",{"type":48,"value":529}," SDK \u002F MCP only after ",{"type":42,"tag":92,"props":531,"children":533},{"className":532},[],[534],{"type":48,"value":535},"sentry-api-schema",{"type":48,"value":537}," regenerates downstream.",{"type":42,"tag":75,"props":539,"children":541},{"id":540},"validate",[542],{"type":48,"value":543},"Validate",{"type":42,"tag":195,"props":545,"children":547},{"className":197,"code":546,"language":199,"meta":200,"style":200},"make build-api-docs\npnpm run validate-api-examples\n.venv\u002Fbin\u002Fpytest -q --reuse-db tests\u002Fapidocs\u002Fendpoints\u002F\u003Carea>\u002Ftest_\u003Cname>.py\n.venv\u002Fbin\u002Fprek run -q --files \u003Cchanged paths>\n",[548],{"type":42,"tag":92,"props":549,"children":550},{"__ignoreMap":200},[551,564,583,654],{"type":42,"tag":206,"props":552,"children":553},{"class":208,"line":209},[554,559],{"type":42,"tag":206,"props":555,"children":556},{"style":213},[557],{"type":48,"value":558},"make",{"type":42,"tag":206,"props":560,"children":561},{"style":219},[562],{"type":48,"value":563}," build-api-docs\n",{"type":42,"tag":206,"props":565,"children":567},{"class":208,"line":566},2,[568,573,578],{"type":42,"tag":206,"props":569,"children":570},{"style":213},[571],{"type":48,"value":572},"pnpm",{"type":42,"tag":206,"props":574,"children":575},{"style":219},[576],{"type":48,"value":577}," run",{"type":42,"tag":206,"props":579,"children":580},{"style":219},[581],{"type":48,"value":582}," validate-api-examples\n",{"type":42,"tag":206,"props":584,"children":586},{"class":208,"line":585},3,[587,592,597,602,607,612,617,621,626,631,635,640,645,649],{"type":42,"tag":206,"props":588,"children":589},{"style":213},[590],{"type":48,"value":591},".venv\u002Fbin\u002Fpytest",{"type":42,"tag":206,"props":593,"children":594},{"style":219},[595],{"type":48,"value":596}," -q",{"type":42,"tag":206,"props":598,"children":599},{"style":219},[600],{"type":48,"value":601}," --reuse-db",{"type":42,"tag":206,"props":603,"children":604},{"style":219},[605],{"type":48,"value":606}," tests\u002Fapidocs\u002Fendpoints\u002F",{"type":42,"tag":206,"props":608,"children":609},{"style":230},[610],{"type":48,"value":611},"\u003C",{"type":42,"tag":206,"props":613,"children":614},{"style":219},[615],{"type":48,"value":616},"are",{"type":42,"tag":206,"props":618,"children":619},{"style":241},[620],{"type":48,"value":57},{"type":42,"tag":206,"props":622,"children":623},{"style":230},[624],{"type":48,"value":625},">",{"type":42,"tag":206,"props":627,"children":628},{"style":219},[629],{"type":48,"value":630},"\u002Ftest_",{"type":42,"tag":206,"props":632,"children":633},{"style":230},[634],{"type":48,"value":611},{"type":42,"tag":206,"props":636,"children":637},{"style":219},[638],{"type":48,"value":639},"nam",{"type":42,"tag":206,"props":641,"children":642},{"style":241},[643],{"type":48,"value":644},"e",{"type":42,"tag":206,"props":646,"children":647},{"style":230},[648],{"type":48,"value":625},{"type":42,"tag":206,"props":650,"children":651},{"style":219},[652],{"type":48,"value":653},".py\n",{"type":42,"tag":206,"props":655,"children":657},{"class":208,"line":656},4,[658,663,667,671,676,681,686,691,696],{"type":42,"tag":206,"props":659,"children":660},{"style":213},[661],{"type":48,"value":662},".venv\u002Fbin\u002Fprek",{"type":42,"tag":206,"props":664,"children":665},{"style":219},[666],{"type":48,"value":577},{"type":42,"tag":206,"props":668,"children":669},{"style":219},[670],{"type":48,"value":596},{"type":42,"tag":206,"props":672,"children":673},{"style":219},[674],{"type":48,"value":675}," --files",{"type":42,"tag":206,"props":677,"children":678},{"style":230},[679],{"type":48,"value":680}," \u003C",{"type":42,"tag":206,"props":682,"children":683},{"style":219},[684],{"type":48,"value":685},"changed",{"type":42,"tag":206,"props":687,"children":688},{"style":219},[689],{"type":48,"value":690}," path",{"type":42,"tag":206,"props":692,"children":693},{"style":241},[694],{"type":48,"value":695},"s",{"type":42,"tag":206,"props":697,"children":698},{"style":230},[699],{"type":48,"value":700},">\n",{"type":42,"tag":702,"props":703,"children":704},"style",{},[705],{"type":48,"value":706},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":708,"total":880},[709,734,748,759,773,790,806,820,828,839,849,867],{"slug":710,"name":710,"fn":711,"description":712,"org":713,"tags":714,"stars":731,"repoUrl":732,"updatedAt":733},"xcodebuildmcp","build and test Apple apps with XcodeBuildMCP","Official skill for XcodeBuildMCP. Use when doing iOS\u002FmacOS\u002FwatchOS\u002FtvOS\u002FvisionOS work (build, test, run, debug, log, UI automation).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[715,718,721,724,725,728],{"name":716,"slug":717,"type":16},"Debugging","debugging",{"name":719,"slug":720,"type":16},"iOS","ios",{"name":722,"slug":723,"type":16},"macOS","macos",{"name":9,"slug":8,"type":16},{"name":726,"slug":727,"type":16},"Testing","testing",{"name":729,"slug":730,"type":16},"Xcode","xcode",6176,"https:\u002F\u002Fgithub.com\u002Fgetsentry\u002FXcodeBuildMCP","2026-04-06T18:13:34.8719",{"slug":735,"name":735,"fn":736,"description":737,"org":738,"tags":739,"stars":731,"repoUrl":732,"updatedAt":747},"xcodebuildmcp-cli","build and test Apple apps via CLI","Official skill for the XcodeBuildMCP CLI. Use when doing iOS\u002FmacOS\u002FwatchOS\u002FtvOS\u002FvisionOS work (build, test, run, debug, log, UI automation).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[740,743,744,745,746],{"name":741,"slug":742,"type":16},"CLI","cli",{"name":719,"slug":720,"type":16},{"name":722,"slug":723,"type":16},{"name":726,"slug":727,"type":16},{"name":729,"slug":730,"type":16},"2026-04-06T18:13:36.13414",{"slug":749,"name":749,"fn":750,"description":751,"org":752,"tags":753,"stars":24,"repoUrl":25,"updatedAt":758},"agents-md","maintain project instruction files","Creates and maintains concise AGENTS.md and CLAUDE.md project instruction files. Use when asked to create AGENTS.md, update AGENTS.md, maintain agent docs, set up CLAUDE.md, document repository agent conventions, or keep coding-agent instructions minimal and reference-backed.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[754,755],{"name":14,"slug":15,"type":16},{"name":756,"slug":757,"type":16},"Engineering","engineering","2026-05-15T06:16:29.695991",{"slug":760,"name":760,"fn":761,"description":762,"org":763,"tags":764,"stars":24,"repoUrl":25,"updatedAt":772},"blog-writing-guide","write and review engineering blog posts","Write, review, and improve blog posts for the Sentry engineering blog following Sentry's specific writing standards, voice, and quality bar. Use this skill whenever someone asks to write a blog post, draft a technical article, review blog content, improve a draft, write a product announcement, create an engineering deep-dive, or produce any written content destined for the Sentry blog or developer audience. Also trigger when the user mentions \"blog post,\" \"blog draft,\" \"write-up,\" \"announcement post,\" \"engineering post,\" \"deep dive,\" \"postmortem,\" or asks for help with technical writing for Sentry. Even if the user just says \"help me write about [feature\u002Ftopic]\" — if it sounds like it could become a Sentry blog post, use this skill.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[765,768,769],{"name":766,"slug":767,"type":16},"Communications","communications",{"name":9,"slug":8,"type":16},{"name":770,"slug":771,"type":16},"Technical Writing","technical-writing","2026-05-15T06:16:33.38217",{"slug":774,"name":774,"fn":775,"description":776,"org":777,"tags":778,"stars":24,"repoUrl":25,"updatedAt":789},"brand-guidelines","write copy following Sentry brand guidelines","Write copy following Sentry brand guidelines. Use when writing UI text, error messages, empty states, onboarding flows, 404 pages, documentation, marketing copy, or any user-facing content. Covers both Plain Speech (default) and Sentry Voice tones.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[779,782,785,786],{"name":780,"slug":781,"type":16},"Branding","branding",{"name":783,"slug":784,"type":16},"Content Creation","content-creation",{"name":9,"slug":8,"type":16},{"name":787,"slug":788,"type":16},"UX Copy","ux-copy","2026-05-15T06:16:22.395707",{"slug":791,"name":791,"fn":792,"description":793,"org":794,"tags":795,"stars":24,"repoUrl":25,"updatedAt":805},"claude-settings-audit","generate Claude Code settings permissions","Analyze a repository to generate recommended Claude Code settings.json permissions. Use when setting up a new project, auditing existing settings, or determining which read-only bash commands to allow. Detects tech stack, build tools, and monorepo structure.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[796,799,802],{"name":797,"slug":798,"type":16},"Claude Code","claude-code",{"name":800,"slug":801,"type":16},"Configuration","configuration",{"name":803,"slug":804,"type":16},"Security","security","2026-05-15T06:16:44.335977",{"slug":807,"name":807,"fn":808,"description":809,"org":810,"tags":811,"stars":24,"repoUrl":25,"updatedAt":819},"code-review","perform code reviews for Sentry projects","Perform code reviews following Sentry engineering practices. Use when reviewing pull requests, examining code changes, or providing feedback on code quality. Covers security, performance, testing, and design review.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[812,814,815,818],{"name":813,"slug":807,"type":16},"Code Review",{"name":756,"slug":757,"type":16},{"name":816,"slug":817,"type":16},"Performance","performance",{"name":803,"slug":804,"type":16},"2026-05-15T06:16:35.824864",{"slug":821,"name":821,"fn":822,"description":823,"org":824,"tags":825,"stars":24,"repoUrl":25,"updatedAt":827},"code-simplifier","simplify and refine source code","Simplifies and refines code for clarity, consistency, and maintainability while preserving all functionality. Use when asked to \"simplify code\", \"clean up code\", \"refactor for clarity\", \"improve readability\", or review recently modified code for elegance. Focuses on project-specific best practices.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[826],{"name":22,"slug":23,"type":16},"2026-05-15T06:16:32.127981",{"slug":829,"name":829,"fn":830,"description":831,"org":832,"tags":833,"stars":24,"repoUrl":25,"updatedAt":838},"commit","create commits with Sentry conventions","Use for every request to commit changes or draft a commit message. Creates Sentry-style conventional commits with issue references.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[834,837],{"name":835,"slug":836,"type":16},"Git","git",{"name":9,"slug":8,"type":16},"2026-07-18T05:15:10.723937",{"slug":840,"name":840,"fn":841,"description":842,"org":843,"tags":844,"stars":24,"repoUrl":25,"updatedAt":848},"create-branch","create git branches for Sentry workflows","Create a git branch following Sentry naming conventions. Use when asked to \"create a branch\", \"new branch\", \"start a branch\", \"make a branch\", \"switch to a new branch\", or when starting new work on the default branch.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[845,846,847],{"name":756,"slug":757,"type":16},{"name":835,"slug":836,"type":16},{"name":9,"slug":8,"type":16},"2026-05-15T06:16:39.458431",{"slug":850,"name":850,"fn":851,"description":852,"org":853,"tags":854,"stars":24,"repoUrl":25,"updatedAt":866},"django-access-review","review Django access control and IDOR","Django access control and IDOR security review. Use when reviewing Django views, DRF viewsets, ORM queries, or any Python\u002FDjango code handling user authorization. Trigger keywords: \"IDOR\", \"access control\", \"authorization\", \"Django permissions\", \"object permissions\", \"tenant isolation\", \"broken access\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[855,858,859,862,865],{"name":856,"slug":857,"type":16},"Access Control","access-control",{"name":22,"slug":23,"type":16},{"name":860,"slug":861,"type":16},"Django","django",{"name":863,"slug":864,"type":16},"Python","python",{"name":803,"slug":804,"type":16},"2026-05-15T06:16:43.098698",{"slug":868,"name":868,"fn":869,"description":870,"org":871,"tags":872,"stars":24,"repoUrl":25,"updatedAt":879},"django-perf-review","review and optimize Django performance","Django performance code review. Use when asked to \"review Django performance\", \"find N+1 queries\", \"optimize Django\", \"check queryset performance\", \"database performance\", \"Django ORM issues\", or audit Django code for performance problems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[873,874,877,878],{"name":813,"slug":807,"type":16},{"name":875,"slug":876,"type":16},"Database","database",{"name":860,"slug":861,"type":16},{"name":816,"slug":817,"type":16},"2026-05-15T06:16:24.832813",88,{"items":882,"total":923},[883,888,894,901,907,914,918],{"slug":749,"name":749,"fn":750,"description":751,"org":884,"tags":885,"stars":24,"repoUrl":25,"updatedAt":758},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[886,887],{"name":14,"slug":15,"type":16},{"name":756,"slug":757,"type":16},{"slug":760,"name":760,"fn":761,"description":762,"org":889,"tags":890,"stars":24,"repoUrl":25,"updatedAt":772},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[891,892,893],{"name":766,"slug":767,"type":16},{"name":9,"slug":8,"type":16},{"name":770,"slug":771,"type":16},{"slug":774,"name":774,"fn":775,"description":776,"org":895,"tags":896,"stars":24,"repoUrl":25,"updatedAt":789},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[897,898,899,900],{"name":780,"slug":781,"type":16},{"name":783,"slug":784,"type":16},{"name":9,"slug":8,"type":16},{"name":787,"slug":788,"type":16},{"slug":791,"name":791,"fn":792,"description":793,"org":902,"tags":903,"stars":24,"repoUrl":25,"updatedAt":805},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[904,905,906],{"name":797,"slug":798,"type":16},{"name":800,"slug":801,"type":16},{"name":803,"slug":804,"type":16},{"slug":807,"name":807,"fn":808,"description":809,"org":908,"tags":909,"stars":24,"repoUrl":25,"updatedAt":819},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[910,911,912,913],{"name":813,"slug":807,"type":16},{"name":756,"slug":757,"type":16},{"name":816,"slug":817,"type":16},{"name":803,"slug":804,"type":16},{"slug":821,"name":821,"fn":822,"description":823,"org":915,"tags":916,"stars":24,"repoUrl":25,"updatedAt":827},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[917],{"name":22,"slug":23,"type":16},{"slug":829,"name":829,"fn":830,"description":831,"org":919,"tags":920,"stars":24,"repoUrl":25,"updatedAt":838},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[921,922],{"name":835,"slug":836,"type":16},{"name":9,"slug":8,"type":16},28]