ms-ai-architect/skills/ms-ai-advisor/references/copilot-extensibility/copilot-api-rate-limiting-resilience.md
Kjell Tore Guttormsen 712a143e58 fix(ms-ai-architect): RX-KB1 strip stale plain-Verified pipe-tails (87) + audit-deteksjon [skip-docs]
De 87 referansefilene bar en plain-text `| Verified: <dato>`-hale på **Last updated:**-linjen
i 500B-header-vinduet — usynlig for den bold-only kontrakt-stacken (kb-headers.mjs / audit
RE_VERIFIED), og claimet en verifisering judgen aldri gjorde (samme poison-klasse som de 14
bold **Verified:** MCP Spor 1 fjernet). Uhåndtert springer den også dual-Verified-fellen: R7s
insertVerifiedFields ville stemplet en bold-verdi ved siden av den plain → to motstridende
provenance-claims per fil.

- ny driver strip-stale-verified-pipe.mjs: frosset 87-manifest (18 advisor + 45 eng + 8 gov +
  16 sec), pure verdi-bevarende strip (kun ` | Verified: …`-halen; **Last updated:**-dato
  byte-eksakt), hard per-fil-invariant (linjeantall uendret, body byte-identisk, dato bevart),
  idempotent, atomicWriteSync (RX-OPS2 recovery-kontrakt).
- audit-corpus-headers.mjs: ny plain-Verified-deteksjon (RE_PLAIN_VERIFIED + plainVerifiedPipe)
  — gjør M4-blindheten synlig så en stale plain-hale ikke kan gjenoppstå stille (non-advisor scope).
- 87 filer strippet; plain Verified i vinduet 0/389; live-audit plainVerifiedPipe 0.

Mekanisme: +15 tester (12 strip + 3 audit). Suite 875→890 exit 0. validate-plugin.sh 250/0.

Utsatt → RX-KB1b: footer-dato-avvik + label-whitelist (annen dialekt, flag-to-human).
2026-07-16 20:04:52 +02:00

497 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# API Rate Limiting and Resilience Patterns
**Last updated:** 2026-06-19
**Status:** GA
**Category:** Copilot Extensibility & Integration
---
## Introduksjon
Rate limiting og resilience patterns er kritiske for å bygge robuste Microsoft AI-applikasjoner som håndterer transiente feil, throttling og kapasitetsbegrensninger på en elegant måte. Microsofts AI-plattformer (Azure OpenAI, Copilot Studio, M365 Copilot) implementerer throttling for å beskytte infrastruktur og sikre rettferdig ressursfordeling. Effektiv håndtering av disse begrensningene skiller en prototyp fra en produksjonsklar løsning.
Denne referansen dekker:
- **Retry patterns** med exponential backoff for transiente feil
- **Rate limiting patterns** for å unngå throttling
- **Circuit breaker patterns** for varige feil
- **Plattformspesifikke kvotegrenser** (Azure OpenAI, Copilot Studio)
- **Implementeringsmønstre** med kodeeksempler
**Confidence:** Verified (Microsoft Learn MCP, januar 2026) | Re-verified: MCP 2026-06-19
---
## Kjernekomponenter / Nøkkelegenskaper
### 1. Retry Pattern (Retry etter transiente feil)
**Formål:** Håndtere kortvarige feil (nettverkstap, midlertidig utilgjengelighet, timeouts) ved automatisk å prøve operasjonen på nytt etter en passende forsinkelse.
**Nøkkelstrategier:**
- **Cancel:** Avbryt hvis feilen ikke er transient eller sannsynligvis vil gjenta seg
- **Retry immediately:** For sjeldne feil (f.eks. korrupt nettverkspakke)
- **Retry after delay:** For vanlige connectivity/busy-feil — vent før retry (anbefalt)
**Exponential backoff:** Vent 2s → 4s → 8s → 16s mellom forsøk for å unngå å overbelaste en allerede busy service.
**Viktighetsgrad:**
- Innebygd retry i mange Microsoft-biblioteker (Entity Framework, Azure SDK)
- Logg tidlige feil som informasjon, kun siste forsøk som error
- Idempotens-krav: operasjonen må være trygg å kjøre flere ganger
**Verified:** [Retry pattern - Azure Architecture Center](https://learn.microsoft.com/en-us/azure/architecture/patterns/retry)
### 2. Rate Limiting Pattern (Kontrollert trafikk)
**Formål:** Redusere throttling-feil ved å kontrollere antall requests sendt til en service over tid, innenfor servicens kapasitetsgrenser.
**Problem som løses:**
En naiv "retry on error"-tilnærming kan sende 3x mer trafikk enn nødvendig (eksempel: 10 000 records, 2 000 RU/s kapasitet = 30 000 forsøk i stedet for 10 000).
**Løsning:**
1. **Bruk durable messaging** (Azure Service Bus, Event Hubs, Queue Storage) som buffer
2. **Dequeue i kontrollert tempo** (f.eks. 20 requests hvert 200ms i stedet for 100/sekund) — sender et jevnere dataflyt og forhindrer bottlenecks ved bursts
3. **Distributed lease management** for multiple prosesser (Azure Blob lease eller Zookeeper/Redis/etcd/Consul)
4. **Logisk partisjonering:** Deler kapasiteten i partisjoner (f.eks. 20 partisjoner à 25 req/s for 500 req/s totalt) — prosesser konkurrerer om eksklusive blob-leases per partisjon
**Fordeler:**
- Redusert trafikk og færre feil
- Forutsigbar throughput
- Lavere minneforbruk (dequeue kun når kapasitet er tilgjengelig)
**Verified:** [Rate Limiting pattern - Azure Architecture Center](https://learn.microsoft.com/en-us/azure/architecture/patterns/rate-limiting-pattern)
**Eksempel (Cosmos DB med 20 000 RU/s kapasitet, 10 000 records à 10 RU):**
- Naiv retry: 30 000 requests sendt (10K + 8K + 6K + 4K + 2K) med 20 000 feil
- Med rate limiting: 10 000 requests, ingen throttling-feil, forutsigbar gjennomstrømningstid
### 3. Circuit Breaker Pattern (Beskyttelse mot varige feil)
**Formål:** Forhindre at applikasjonen spammer en service som er nede eller ikke responderer, ved å "åpne kretsen" etter N feilede forsøk.
**Tilstander:**
- **Closed:** Normal drift, requests går gjennom
- **Open:** Etter X feil — blokkerer alle requests
- **Half-Open:** Periodisk tillat én prøve-request for å sjekke om service er tilbake
**Forskjell fra Retry:**
- Retry forventer at feilen løser seg raskt
- Circuit Breaker forventer langvarig feil og beskytter mot waste
**Kombinasjon:** Bruk Retry pattern med Circuit Breaker for optimal resilience.
**Verified:** [Circuit Breaker pattern - Azure Architecture Center](https://learn.microsoft.com/en-us/dotnet/architecture/cloud-native/application-resiliency-patterns#circuit-breaker-pattern)
### 4. HTTP Response Headers for Rate Limiting
**Standard headers** (RateLimit-* eller X-RateLimit-*):
- `RateLimit-Remaining`: Antall requests igjen i nåværende window
- `RateLimit-Reset`: Tidspunkt når grensen resettes
- `Retry-After`: Antall sekunder å vente før neste request (ved 429 Too Many Requests)
**Status codes:**
- **429 Too Many Requests:** Rate limit overskredet
- **503 Service Unavailable:** Midlertidig overbelastet (retry etter `Retry-After`)
**Best practice:**
- Sjekk `RateLimit-Remaining` og throttle *før* du når 0
- Respekter `Retry-After` header ved 429-feil
**Verified:** [What is rate limiting? - Microsoft Cloud Dev](https://learn.microsoft.com/en-us/microsoft-cloud/dev/dev-proxy/concepts/what-is-rate-limiting)
---
## Arkitekturmønstre
### Mønster 1: Retry med Exponential Backoff (C#)
```csharp
using Microsoft.Azure.WebJobs;
[FunctionName("EventHubTrigger")]
[ExponentialBackoffRetry(5, "00:00:04", "00:15:00")]
public static async Task Run([EventHubTrigger("myHub", Connection = "EventHubConnection")] EventData[] events, ILogger log)
{
// Function logic her
// Retries automatisk 5 ganger med 4s min, 15 min max delay
}
```
**Forklaring:**
- 5 retry-forsøk
- Initial delay: 4 sekunder
- Max delay: 15 minutter
- Eksponentiell økning mellom forsøk
**Use case:** Azure Functions, Event Hubs triggers, Cosmos DB triggers.
**Verified:** [Retry policies - Azure Functions](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-error-pages)
### Mønster 2: Custom Retry Logic med Transient Fault Handling (Teams Bot)
```csharp
// Definer retry-strategi
var exponentialBackoffRetryStrategy = new ExponentialBackoffRetryStrategy(
3, // 3 forsøk
TimeSpan.FromSeconds(2), // Min backoff
TimeSpan.FromSeconds(20), // Max backoff
TimeSpan.FromSeconds(1) // Jitter delta (+/- 20%)
);
// Definer retry policy
var retryPolicy = new RetryPolicy(new BotSdkTransientExceptionDetectionStrategy(), exponentialBackoffRetryStrategy);
// Utfør bot-operasjon med retry
await retryPolicy.ExecuteAsync(() => connector.Conversations.ReplyToActivityAsync((Activity)reply)).ConfigureAwait(false);
```
**Transient Exception Detection (429 rate limit):**
```csharp
public class BotSdkTransientExceptionDetectionStrategy : ITransientErrorDetectionStrategy
{
List<int> transientErrorStatusCodes = new List<int>() { 429 };
public bool IsTransient(Exception ex)
{
if (ex.Message.Contains("429"))
return true;
HttpResponseMessageWrapper? response = null;
if (ex is HttpOperationException httpOperationException)
response = httpOperationException.Response;
return response != null && transientErrorStatusCodes.Contains((int)response.StatusCode);
}
}
```
**Forklaring:**
- Sjekker om feil er HTTP 429 (rate limit)
- Retry kun for transiente feil
- Jitter (+/- 20%) sprer load fra multiple klienter
**Use case:** Teams bots, Power Virtual Agents, Copilot Studio bots.
**Verified:** [Optimize bot with rate limiting in Teams](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/rate-limit)
### Mønster 3: Rate Limiting med Queue + Lease-basert Kapasitetsstyring
```text
[API] → [Queue A / Queue B] → [Job Processor] → [Rate-limited Service]
[Blob Lease Partitions]
```
**Workflow:**
1. API enqueuer records til durable queue (Azure Service Bus/Event Hubs)
2. Job Processor forsøker å lease blob-partitions (Azure Blob Storage)
3. For hver leaset partition → X requests/sekund kapasitet
4. Processor dequeuer kun det som kan prosesseres innenfor kapasitet
5. Lease expires → processor må re-lease eller redusere throughput
**Eksempel:**
- Service tillater 500 req/s
- Oppretter 20 partitions × 25 req/s
- Process A leaser 4 partitions → 100 req/s
- Process B leaser 2 partitions → 50 req/s
**Fordeler:**
- Multiple unkoordinerte prosesser kan dele kapasitet
- Redusert minnebruk (dequeue kun ved kapasitet)
- Færre throttling-feil
**Verified:** [Rate Limiting pattern - Azure Architecture Center](https://learn.microsoft.com/en-us/azure/architecture/patterns/rate-limiting-pattern)
### Mønster 4: Batch Job Queueing med Exponential Backoff (Azure OpenAI)
```python
import time
from openai import BadRequestError
max_retries = 10
retries = 0
initial_delay = 5
delay = initial_delay
while True:
try:
batch_response = client.batches.create(
input_file_id=file_id,
endpoint="/chat/completions",
completion_window="24h",
)
batch_id = batch_response.id
print(f"✅ Batch created successfully after {retries} retries")
break
except BadRequestError as e:
if 'token_limit_exceeded' in str(e):
retries += 1
if retries >= max_retries:
raise
print(f"⏳ Token limit exceeded. Waiting {delay}s (retry {retries}/{max_retries})")
time.sleep(delay)
delay *= 2 # Exponential backoff
else:
raise
```
**Forklaring:**
- Håndterer token_limit_exceeded for Azure OpenAI batch jobs
- Fail-fast hvis token quota nådd (nytt i 2025)
- Exponential backoff: 5s → 10s → 20s → 40s...
**Use case:** Store batch-operasjoner (Azure OpenAI, Microsoft Foundry).
**Verified:** [Batch deployments - Azure OpenAI](https://learn.microsoft.com/en-us/azure/foundry/openai/how-to/batch)
---
## Beslutningsveiledning
### Når bruke hvilken pattern?
| Pattern | Use Case | Eksempel |
|---------|----------|----------|
| **Retry (immediate)** | Sjeldne, transiente feil | Korrupt nettverkspakke |
| **Retry (exponential backoff)** | Vanlige transiente feil (connectivity, busy) | Azure OpenAI 429, Cosmos DB throttling |
| **Rate Limiting** | Forutsigbar throttling-grense | Azure OpenAI TPM/RPM quotas, Copilot Studio generative AI limits |
| **Circuit Breaker** | Langvarige feil (service nede) | Avhengighet på ekstern API som er nede i minutter |
| **Kombiner Retry + Circuit Breaker** | Kritiske applikasjoner | E-handel checkout, helsesektorsystemer |
### Sjekkliste før implementering
**1. Er operasjonen idempotent?**
- Ja → Trygt å retry
- Nei → Implementer idempotency token eller accept duplicate risk
**2. Hva er tjenestens throttling-grense?**
- Sjekk dokumentasjon for TPM (tokens per minute), RPM (requests per minute)
- Eksempler: Azure OpenAI Standard tier = 150K TPM (gpt-4o), Copilot Studio = per Dataverse-environment RPM/RPH (generative AI: 50 RPM / 1 000 RPH med 1-10 message packs, skalerer til 100 RPM / 2 000 RPH; 8 000 RPM for meldinger til en agent)
**3. Har applikasjonen multiple workstreams?**
- Ja → Bruk shared rate limiter eller separate capacity pools
- Nei → Simpel retry policy holder
**4. Er feilen transient eller varig?**
- Transient (429, 503) → Retry
- Varig (500 Internal Server Error gjentatte ganger) → Circuit Breaker
---
## Integrasjon med Microsoft-stakken
### Azure OpenAI Service
**Quota limits (per deployment):**
- **gpt-4o** (Global Standard, Default tier): 450K TPM, 2.7K RPM
- **gpt-4o-mini** (Default tier): 2M TPM, 12K RPM
- **o1-preview** (Default tier): 300K TPM, 50 RPM
**429 Error Scenarios:**
1. **Rate Limit Exceeded:** TPM/RPM quota overskredet → retry etter `Retry-After`
2. **High System Demand:** System under load → retry etter suggested time
**Best practice:**
- Sett `max_tokens` til minimum nødvendig (reduserer TPM-forbruk)
- Bruk quota management for å øke TPM på high-traffic deployments
- Implementer retry logic med exponential backoff
- Unngå skarpe workload-endringer (gradvis økning)
**Verified:** [Quotas and limits - Azure OpenAI](https://learn.microsoft.com/en-us/azure/foundry/openai/quotas-limits), [Manage quota - Azure OpenAI](https://learn.microsoft.com/en-us/azure/foundry-classic/openai/how-to/quota)
### Copilot Studio
**Throttling error codes:**
- **GenAISearchandSummarizeRateLimitReached:** Søk/summarize quota nådd (per hour/minute per Dataverse environment)
- **GenAIToolPlannerRateLimitReached:** Generative orchestration quota nådd
- **OpenAIRateLimitReached:** Max generative answers reached
**Løsninger:**
1. **Licensing:** Kjøp flere capacity packs eller bytt til pay-as-you-go
2. **Request rate limit increase:** Kontakt Microsoft Support (kun for pay-as-you-go environments)
3. **Optimize bot:** Bruk express mode, cache retrieved info, bruk direct connector calls i stedet for Power Automate flows
**Flow timeout:** Max 100 sekunder før timeout → optimaliser flow logic, flytt non-critical logic etter 'Return value(s)' step.
**Verified:** [Resolve throttling errors in agents](https://learn.microsoft.com/en-us/troubleshoot/power-platform/copilot-studio/licensing/throttling-errors-agents), [Error codes - Copilot Studio](https://learn.microsoft.com/en-us/troubleshoot/power-platform/copilot-studio/authoring/error-codes)
### Power Automate (Cloud Flows)
**Throttling limits:**
- API request limits per 24 timer (avhenger av lisens)
- Service protection API limits (Dataverse): 429 med `Retry-After` header
**Best practice:**
- Følg `Retry-After` interval (lengre delay hvis du sender demanding requests)
- Start med lavt request-volum, øk gradvis til du treffer limit
- Cache data i variabler i stedet for multiple API calls
- Bruk direct connector calls (raskere enn flows fra Copilot Studio)
**Verified:** [Retry operations - Dynamics 365](https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/data-entities/service-protection-retry-operations)
### Microsoft Teams Bots
**Rate limits:**
- Per bot per thread limit
- Per bot global limit
**Best practice:**
- Detect transient exceptions (429 status code)
- Implement exponential backoff med jitter
- Unngå overdreven polling
**Verified:** [Rate limiting in Teams](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/rate-limit)
---
## Offentlig sektor (Norge)
### Compliance og logging
**GDPR/Personvern:**
- Logg kun feilinformasjon (status codes, timestamps), ikke persondata
- Tidlige retry-feil = INFO, kun siste forsøk = ERROR (unngå flooding av PII i logs)
**Sporbarhet:**
- Implementer correlation IDs for å spore requests gjennom retry-forsøk
- Aggreger feilstatistikk for å identifisere underliggende problemer (f.eks. persistent throttling = kapasitetsøkning nødvendig)
### Kostnadskontroll
**Rate limiting reduserer kostnader:**
- Færre unødvendige API-kall (Azure OpenAI, Copilot Studio)
- Lavere TPM-forbruk = mindre behov for capacity packs
**Example:**
- Naiv retry (10K records, 30K requests sent) vs. rate limiting (10K records, 10K requests sent) = 66% redusert API-forbruk
### Tilgjengelighet og SLA
**SLA-krav:**
- Standard tier (Azure OpenAI) har *ingen latency SLA* og variabel latency ved high load
- For kritiske tjenester: vurder **Provisioned Throughput** (Premium tier) for forutsigbar ytelse
- Circuit Breaker beskytter mot cascade failures i multi-tjeneste-arkitekturer
---
## Kostnad og lisensiering
### Azure OpenAI
**Quota management (gratis):**
- Juster TPM/RPM per deployment (ingen ekstra kostnad)
**Provisioned Throughput (PTU):**
- Fast monthly cost per PTU
- Bedre forutsigbarhet og lavere latency
- Egnet for prod-workloads med strenge SLA-krav
### Copilot Studio
**Capacity packs:**
- Kjøp ekstra capacity for å øke quotas (generative AI messages)
**Pay-as-you-go:**
- Betale per bruk (Copilot credits)
- Overage enforcement: "Agent unavailable" når quota nådd (tilbake online innen 5 min etter capacity økning)
### Power Automate
**API request limits:**
- Inkludert i lisens (varierer per plan: F1, P1, P2, etc.)
- Overskridelse = throttling (429 errors)
**Verified:** [Copilot Studio quotas](https://learn.microsoft.com/en-us/microsoft-copilot-studio/requirements-quotas), [Power Platform API limits](https://learn.microsoft.com/en-us/power-platform/admin/api-request-limits-allocations)
---
## For arkitekten (Cosmo)
### Når anbefale hvilken løsning?
**Prototyping/POC:**
- Start med innebygd retry (Azure SDK, Entity Framework)
- Acceptable å treffe 429-feil under testing
**Production-ready:**
- **Implementer alle 3:** Retry + Rate Limiting + Circuit Breaker
- Bruk durable messaging (Event Hubs, Service Bus) som buffer
- Monitorér `RateLimit-Remaining` headers proaktivt
**Kritiske tjenester (helse, finans, offentlig sektor):**
- Azure OpenAI Provisioned Throughput (PTU) for SLA
- Distributed rate limiting for multi-instance apps
- Correlation IDs for full observability
- Graceful degradation ved circuit breaker open (vis cached/fallback data)
### Red Flags (når å eskalere til PTU/Premium)
1. **Hyppig 429-feil til tross for retry logic** → Kapasitet for lav, vurder PTU
2. **Variabel latency påvirker brukeropplevelse** → Standard tier har ingen latency SLA
3. **Multiple apps konkurrerer om samme deployment** → Separer deployments eller bruk distributed lease
4. **Batch jobs tar timer lenger enn forventet** → Rate limiting med queue kan halvere tid
### Implementeringsrekkefølge (anbefalt)
**Fase 1: Basic Resilience**
1. Implementer retry med exponential backoff (Azure SDK default eller custom policy)
2. Logg 429-feil og analyser frekvens
**Fase 2: Proactive Rate Limiting**
3. Bruk `RateLimit-Remaining` header for å throttle *før* 429
4. Implementer queue-basert rate limiting hvis batch-operasjoner
**Fase 3: Advanced Resilience**
5. Legg til Circuit Breaker for kritiske avhengigheter
6. Implementer distributed lease for multi-instance apps
7. Monitorér og tune retry/backoff-parametere basert på prod-data
### Spørsmål å stille kunden
1. **Hva er forventet request-volum?** (beregn TPM/RPM-behov)
2. **Hva er SLA-krav for latency?** (Standard vs. PTU)
3. **Har dere multiple applikasjoner som deler samme Azure OpenAI deployment?** (distributed rate limiting)
4. **Er operasjonene batch-orienterte eller real-time?** (queue vs. direct retry)
5. **Hva er akseptabel feilrate?** (0.1% = streng, 1% = moderat)
### Testing og Validering
**Load testing:**
- Simuler 2x forventet load for å verifisere retry logic
- Sjekk at app håndterer 429-feil uten crash
- Verifiser at circuit breaker åpner/lukker korrekt
**Chaos engineering:**
- Simuler service downtime (503 errors) for å teste circuit breaker
- Sjekk at app degrader gracefully (fallback, cached data)
**Metrics å monitorere:**
- 429 error rate (mål: < 1% av requests)
- Retry success rate (mål: > 95%)
- P95/P99 latency (inkludert retry delays)
- Circuit breaker state transitions (Open/Closed/Half-Open)
---
## Kilder og verifisering
**Alle kilder verifisert via Microsoft Learn MCP (januar 2026):**
1. [Retry pattern - Azure Architecture Center](https://learn.microsoft.com/en-us/azure/architecture/patterns/retry)
2. [Rate Limiting pattern - Azure Architecture Center](https://learn.microsoft.com/en-us/azure/architecture/patterns/rate-limiting-pattern)
3. [Circuit Breaker pattern - Cloud-Native .NET](https://learn.microsoft.com/en-us/dotnet/architecture/cloud-native/application-resiliency-patterns)
4. [What is rate limiting? - Microsoft Cloud Dev](https://learn.microsoft.com/en-us/microsoft-cloud/dev/dev-proxy/concepts/what-is-rate-limiting)
5. [How to handle API throttling - Microsoft Cloud Dev](https://learn.microsoft.com/en-us/microsoft-cloud/dev/dev-proxy/concepts/how-to-handle-api-throttling)
6. [Azure OpenAI quotas and limits](https://learn.microsoft.com/en-us/azure/foundry/openai/quotas-limits)
7. [Manage Azure OpenAI quota](https://learn.microsoft.com/en-us/azure/foundry-classic/openai/how-to/quota)
8. [Batch deployments - Azure OpenAI](https://learn.microsoft.com/en-us/azure/foundry/openai/how-to/batch)
9. [Resolve throttling errors in Copilot Studio agents](https://learn.microsoft.com/en-us/troubleshoot/power-platform/copilot-studio/licensing/throttling-errors-agents)
10. [Error codes - Copilot Studio](https://learn.microsoft.com/en-us/troubleshoot/power-platform/copilot-studio/authoring/error-codes)
11. [Optimize bot with rate limiting in Teams](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/rate-limit)
12. [Retry operations - Dynamics 365](https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/data-entities/service-protection-retry-operations)
13. [Retry policies - Azure Functions](https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-error-pages)
**MCP Calls:** 6 (3 searches, 2 fetches, 1 code sample search)
**Unique URLs:** 13 sources