From 91be3358062301139c7eabaec3430f76e8d0c317 Mon Sep 17 00:00:00 2001 From: Cesar Garcia <128240629+Chesars@users.noreply.github.com> Date: Wed, 20 May 2026 21:03:11 -0300 Subject: [PATCH 1/8] docs(blog): make /blog responsive (#51) * docs(blog): make /blog responsive - Add mobile styles to swizzled BlogListPage (hero, marquee, posts, pagination). - Fix horizontal overflow caused by the marquee's white-space: nowrap propagating width up the flex chain. Break it with min-width: 0 on .page and #__docusaurus > *, plus defensive overflow-x: clip. - Respect prefers-reduced-motion (stop marquee animation). * Remove global overflow prevention styles Removed global styles to prevent horizontal overflow on mobile. --- src/css/custom.css | 15 +++++ src/theme/BlogListPage/styles.module.css | 78 ++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/src/css/custom.css b/src/css/custom.css index 764ef2f13..d71ab92a3 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -34,6 +34,21 @@ --ifm-container-width-xl: 1560px; } + +html, +body, +#__docusaurus { + overflow-x: hidden; + overflow-x: clip; +} + +/* Break the flex-item min-width:auto chain that lets nowrap children + (marquees, long pre/code blocks) push wrappers wider than the viewport. */ +#__docusaurus, +#__docusaurus > * { + min-width: 0; +} + /* ========================================= DARK MODE Variables ========================================= */ diff --git a/src/theme/BlogListPage/styles.module.css b/src/theme/BlogListPage/styles.module.css index 49a24d5f9..72d798873 100644 --- a/src/theme/BlogListPage/styles.module.css +++ b/src/theme/BlogListPage/styles.module.css @@ -1,8 +1,15 @@ /* ── Page shell ───────────────────────────────────────────────────────── */ .page { + width: 100%; max-width: 860px; margin: 0 auto; padding: 0 2rem; + /* Required: as a flex item of .main-wrapper, default min-width: auto + would let the marquee's intrinsic content size push the page wider + than the viewport. */ + min-width: 0; + overflow-x: hidden; + overflow-x: clip; } /* ── Hero ─────────────────────────────────────────────────────────────── */ @@ -259,6 +266,77 @@ color: #0ea5e9; } +/* ── Mobile (≤640px) ──────────────────────────────────────────────────── */ +@media (max-width: 640px) { + .page { + padding: 0 1.25rem; + } + + .hero { + padding: 2rem 0 0; + } + + .heroTitle { + font-size: 2rem; + } + + .heroSub { + font-size: 0.9rem; + } + + .hiringBtn { + padding: 0.6rem 1rem; + font-size: 0.85rem; + } + + .marqueeWrap { + margin: 2rem 0 0; + padding: 1rem 0; + } + + .fadeLeft, + .fadeRight { + width: 2.5rem; + } + + .marqueeItem { + padding: 0 0.9rem; + font-size: 0.78rem; + } + + .marqueeSep { + margin-left: 0.8rem; + } + + .post { + padding: 1.5rem 0; + } + + .title { + font-size: 1.2rem; + } + + .desc { + font-size: 0.85rem; + } + + .pagination { + padding: 1rem 0 3rem; + gap: 1rem; + } + + .pageLink { + font-size: 0.8rem; + } +} + +/* ── Reduced motion ──────────────────────────────────────────────────── */ +@media (prefers-reduced-motion: reduce) { + .marqueeTrack { + animation: none; + } +} + /* ── Dark mode ────────────────────────────────────────────────────────── */ [data-theme='dark'] .heroTitle, [data-theme='dark'] .title { From 2311885feca2856902e09aa18807b13b2a6d448f Mon Sep 17 00:00:00 2001 From: harish-berri Date: Wed, 20 May 2026 17:05:17 -0700 Subject: [PATCH 2/8] docs(proxy): add Grafana Cloud Pyroscope user and API token configuration options (#52) --- docs/proxy/config_settings.md | 2 ++ docs/proxy/pyroscope_profiling.md | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/docs/proxy/config_settings.md b/docs/proxy/config_settings.md index 032731b87..1a1c5ab4a 100644 --- a/docs/proxy/config_settings.md +++ b/docs/proxy/config_settings.md @@ -906,6 +906,8 @@ router_settings: | PYROSCOPE_APP_NAME | Application name reported to Pyroscope. Required when LITELLM_ENABLE_PYROSCOPE is true. No default. | PYROSCOPE_SERVER_ADDRESS | Pyroscope server URL to send profiles to. Required when LITELLM_ENABLE_PYROSCOPE is true. No default. | PYROSCOPE_SAMPLE_RATE | Optional. Sample rate for Pyroscope profiling (integer). No default; when unset, the pyroscope-io library default is used. +| PYROSCOPE_GRAFANA_USER | Optional. Grafana Cloud Pyroscope user/tenant ID for basic auth. Required when PYROSCOPE_GRAFANA_API_TOKEN is set. +| PYROSCOPE_GRAFANA_API_TOKEN | Optional. Grafana Cloud API/access policy token for Pyroscope basic auth. Required when PYROSCOPE_GRAFANA_USER is set. | LITELLM_MASTER_KEY | Master key for proxy authentication | LITELLM_MAX_BUDGET_PER_SESSION_TTL | TTL in seconds for session budget counters used by the max-budget-per-session limiter. Default is 3600 (1 hour) | LITELLM_MAX_ITERATIONS_TTL | TTL in seconds for session iteration counters used by the max-iterations limiter. Default is 3600 (1 hour) diff --git a/docs/proxy/pyroscope_profiling.md b/docs/proxy/pyroscope_profiling.md index 19d12ba24..ab6367903 100644 --- a/docs/proxy/pyroscope_profiling.md +++ b/docs/proxy/pyroscope_profiling.md @@ -24,6 +24,8 @@ LiteLLM proxy can send continuous CPU profiles to [Grafana Pyroscope](https://gr | `PYROSCOPE_APP_NAME` | Yes (when enabled) | Application name shown in the Pyroscope UI. | | `PYROSCOPE_SERVER_ADDRESS` | Yes (when enabled) | Pyroscope server URL (e.g. `http://localhost:4040`). | | `PYROSCOPE_SAMPLE_RATE` | No | Sample rate (integer). If unset, the pyroscope-io library default is used. | + | `PYROSCOPE_GRAFANA_USER` | No | Grafana Cloud Pyroscope user/tenant ID. Required when `PYROSCOPE_GRAFANA_API_TOKEN` is set. | + | `PYROSCOPE_GRAFANA_API_TOKEN` | No | Grafana Cloud API/access policy token. Used as the Pyroscope basic auth password. | 3. **Start the proxy**; profiling will begin automatically when the proxy starts. @@ -34,6 +36,18 @@ LiteLLM proxy can send continuous CPU profiles to [Grafana Pyroscope](https://gr litellm --config config.yaml ``` + For Grafana Cloud Pyroscope, use the Profiles endpoint as `PYROSCOPE_SERVER_ADDRESS` + and set the Grafana Cloud credentials: + + ```bash + export LITELLM_ENABLE_PYROSCOPE=true + export PYROSCOPE_APP_NAME=litellm-proxy + export PYROSCOPE_SERVER_ADDRESS=https://profiles-prod-.grafana.net + export PYROSCOPE_GRAFANA_USER= + export PYROSCOPE_GRAFANA_API_TOKEN= + litellm --config config.yaml + ``` + 4. **View profiles** in the Pyroscope (or Grafana) UI and select your `PYROSCOPE_APP_NAME`. ## Notes From 644a2b50d41d0e4026cbfa887914032c132f5732 Mon Sep 17 00:00:00 2001 From: milan-berri Date: Thu, 21 May 2026 03:08:36 +0300 Subject: [PATCH 3/8] docs(auth): document scope and wildcard support for JWT routing overrides (#31) Backfills the `BerriAI/litellm-docs` site with the changes that originally shipped under `docs/my-website/` in BerriAI/litellm#25939 / #26325. After the docs source was migrated to this repo, those edits could no longer be carried in the code PR and were dropped on rebase. - proxy/token_auth.md: expand "Matching behavior" with AND semantics, the new optional `scope` selector, list/string forms, shell-style wildcards (`*`, `?`, case-sensitive), and the scope-only space-split rule (iss/aud/client_id are never split on spaces). Adds a worked example combining `scope` with a wildcard `client_id`. - proxy/oauth2.md: cross-reference the new wildcard/scope behavior and point readers to token_auth.md for full details. Code change is in BerriAI/litellm#26325 (litellm_internal_staging). --- docs/proxy/oauth2.md | 4 +++- docs/proxy/token_auth.md | 24 ++++++++++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/docs/proxy/oauth2.md b/docs/proxy/oauth2.md index 9b94a017c..0f24332da 100644 --- a/docs/proxy/oauth2.md +++ b/docs/proxy/oauth2.md @@ -83,5 +83,7 @@ general_settings: path: "oauth2" ``` -For full `routing_overrides` behavior and list-based selectors, see [`/proxy/token_auth`](./token_auth.md#route-jwt-shaped-machine-tokens-to-oauth2). +Selectors support shell-style wildcards (`*`, `?`, case-sensitive) and accept either a single string or a list of strings. + +For full `routing_overrides` behavior — supported selectors, wildcard and list semantics, and matching rules — see [`/proxy/token_auth`](./token_auth.md#route-jwt-shaped-machine-tokens-to-oauth2). diff --git a/docs/proxy/token_auth.md b/docs/proxy/token_auth.md index 4d49a2445..f7c04682a 100644 --- a/docs/proxy/token_auth.md +++ b/docs/proxy/token_auth.md @@ -814,10 +814,26 @@ general_settings: ### Matching behavior -- A rule matches when all configured selectors match token claims -- Supported selectors: `iss` (required), `client_id` (optional), `aud` (optional) -- Selector values support both string and list forms -- If no rule matches, LiteLLM continues with standard JWT validation +- A rule matches when **all** configured selectors match the corresponding token claims (AND semantics). +- Supported selectors: `iss` (required), `client_id` (optional), `scope` (optional), `aud` (optional). +- Selector values can be a single string or a list of strings (the claim must match at least one entry, using the rules below). +- **Wildcards:** selectors may use shell-style `*` and `?`. Matching is **case-sensitive**—use the same casing your IdP emits in JWT claims. +- **`scope` claim as a space-delimited string:** OAuth/OIDC often sends `scope` as one string (e.g. `openid profile App:LiteLLM`). LiteLLM splits that string **only when matching the `scope` selector**, so a configured value like `App:LiteLLM` can match. **`iss`, `aud`, and `client_id` are never split on spaces**; the full claim string is used (routing uses unverified claims only for path selection; final auth still validates the token). +- If no rule matches, LiteLLM continues with standard JWT validation. + +### Example: `scope` and wildcard `client_id` + +```yaml title="config.yaml" +general_settings: + enable_jwt_auth: true + enable_oauth2_auth: false + litellm_jwtauth: + routing_overrides: + - iss: "machine-issuer.example.com" + scope: "App:LiteLLM" + client_id: "*MID_LITELLM" + path: "oauth2" +``` ### List-based override example From b943f475f41fa2e3361e8f4af30653d914518a88 Mon Sep 17 00:00:00 2001 From: mubashir1osmani Date: Wed, 20 May 2026 20:08:55 -0400 Subject: [PATCH 4/8] docs(mcp,a2a): code-verified auth reference fixes + overview page (#156) (#184) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs(mcp,a2a): code-verified auth reference fixes + overview page (#156) * docs(mcp): complete auth_type table, OAuth config reference, RBAC intersection model, hub-vs-public-internet distinction * docs(a2a): document x-litellm-api-key, trace-id enforcement, sub-agent propagation, agent access groups and full intersection model * docs(bedrock_agentcore): add LiteLLM A2A Gateway section — fixes broken anchor from a2a.md, documents dual JWT/SigV4 auth modes and full credential chain * docs: add AuthN/AuthZ overview page side-by-siding MCP and A2A gateways * docs(fixup): corrections from code-review pass — verified against current LiteLLM source * make changes --------- Co-authored-by: michelligabriele --- docs/a2a.md | 36 ++++++- docs/a2a_agent_permissions.md | 45 +++++++- docs/auth_overview.md | 161 ++++++++++++++++++++++++++++ docs/mcp.md | 12 ++- docs/mcp_control.md | 127 +++++++++++++++++++++- docs/mcp_oauth.md | 15 ++- docs/mcp_public_internet.md | 27 +++++ docs/providers/bedrock_agentcore.md | 146 +++++++++++++++++++++++++ sidebars.js | 1 + 9 files changed, 554 insertions(+), 16 deletions(-) create mode 100644 docs/auth_overview.md diff --git a/docs/a2a.md b/docs/a2a.md index 9c86d0de3..bd29f5629 100644 --- a/docs/a2a.md +++ b/docs/a2a.md @@ -211,12 +211,46 @@ POST /a2a/{agent_name}/message/send ### Authentication -Include your LiteLLM Virtual Key in the `Authorization` header: +Include your LiteLLM Virtual Key in either of two headers — `x-litellm-api-key` is preferred when the inbound `Authorization` header may carry a token destined for the backend agent (e.g. when using the [convention-based passthrough](./a2a_agent_headers#method-3--convention-based-forwarding) to forward the caller's identity). ``` Authorization: Bearer sk-your-litellm-key +# or +x-litellm-api-key: Bearer sk-your-litellm-key ``` +#### Per-agent permission check + +After the virtual key is authenticated, LiteLLM checks whether the calling key (and its team) is allowed to invoke the requested agent. If not, the response is HTTP 403. See [Agent Permission Management](./a2a_agent_permissions) for the full intersection model and access groups. + +#### Trace ID enforcement (optional, per-agent) + +An agent can require every inbound request to carry a trace ID for cross-system audit threading. Set `require_trace_id_on_calls_to_agent: true` in the agent's `litellm_params`. When set, requests missing `x-litellm-trace-id` (or `x-litellm-session-id`) are rejected with HTTP 400. + +```bash title="Register an agent that requires inbound trace IDs" showLineNumbers +curl -X POST http://localhost:4000/v1/agents \ + -H "Authorization: Bearer sk-master-key" \ + -H "Content-Type: application/json" \ + -d '{ + "agent_name": "audit-critical-agent", + "agent_card_params": { ... }, + "litellm_params": { + "require_trace_id_on_calls_to_agent": true + } + }' +``` + +The reverse direction — enforcing trace ID on **outbound** calls made by a key owned by an agent — is controlled by `require_trace_id_on_calls_by_agent` on the same `litellm_params` block. + +#### Sub-agent identity propagation + +When the backend agent itself calls LiteLLM (for chat completions or to invoke a sub-agent), LiteLLM forwards two headers to maintain trace continuity: + +- `X-LiteLLM-Trace-Id` — links all calls in the chain to a single trace +- `X-LiteLLM-Agent-Id` — attributes spend to the originating agent + +The caller's **virtual key** and **end-user ID** are not automatically forwarded. If the downstream agent needs the user's identity, propagate it explicitly via [`extra_headers` or the `x-a2a-{agent_name_or_id}-{header}` convention](./a2a_agent_headers). + ### Request Format LiteLLM follows the [A2A JSON-RPC 2.0 specification](https://github.com/google/A2A): diff --git a/docs/a2a_agent_permissions.md b/docs/a2a_agent_permissions.md index 93f367f43..ef721955e 100644 --- a/docs/a2a_agent_permissions.md +++ b/docs/a2a_agent_permissions.md @@ -208,6 +208,39 @@ curl -X POST "http://localhost:4000/a2a/agent-456" \ -d '{"message": {"role": "user", "parts": [{"type": "text", "text": "Hello"}]}}' ``` +## Agent Access Groups + +Granting individual agents to every key or team gets unwieldy as the agent catalog grows. **Agent access groups** let you tag agents with logical labels in the dashboard, then grant the **group** to a key or team — adding a new agent to the group automatically makes it available to every key/team that holds the group. + +### 1. Tag the agent with one or more groups + +In the LiteLLM dashboard: + +1. Go to **Agents**. +2. Create or edit an agent. +3. Under **Access Groups**, type a group name (e.g. `clinical-tools`) and press Enter. + +:::note +Tagging an agent with access groups is currently a dashboard-only operation. The `POST /v1/agents` body schema does not expose `agent_access_groups` as a top-level field; the group tags persist via the underlying DB column and are consumed during permission resolution. +::: + +### 2. Grant a key or team the group + +```bash title="Key with access to two agent groups" showLineNumbers +curl -X POST "http://localhost:4000/key/generate" \ + -H "Authorization: Bearer sk-master-key" \ + -H "Content-Type: application/json" \ + -d '{ + "object_permission": { + "agent_access_groups": ["clinical-tools", "research-tools"] + } + }' +``` + +The key now has access to every agent tagged with either group — no per-agent enumeration required. The same `agent_access_groups` field is also valid on a team's `object_permission`. + +When a key has **both** a direct `agents` list and `agent_access_groups`, the union is computed (any agent reached by either path is allowed), and then the team-level intersection is applied as described below. + ## How It Works ```mermaid @@ -215,28 +248,32 @@ flowchart TD A[Request to invoke agent] --> B{LiteLLM Virtual Key has agent restrictions?} B -->|Yes| C{LiteLLM Team has agent restrictions?} B -->|No| D{LiteLLM Team has agent restrictions?} - + C -->|Yes| E[Use intersection of key + team permissions] C -->|No| F[Use key permissions only] - + D -->|Yes| G[Inherit team permissions] D -->|No| H[Allow ALL agents] - + E --> I{Agent in allowed list?} F --> I G --> I H --> J[Allow request] - + I -->|Yes| J I -->|No| K[Return 403 Forbidden] ``` +A2A permission resolution operates over two levels: Key and Team. (MCP's [permission hierarchy](./mcp_control#permission-hierarchy) extends to End-user / Agent / Org additionally — agent permissions are a narrower model today.) + | Key Permissions | Team Permissions | Result | Notes | |-----------------|------------------|--------|-------| | None | None | Key can access **all** agents | Open access by default when no restrictions are set | | `["agent-1", "agent-2"]` | None | Key can access `agent-1` and `agent-2` | Key uses its own permissions | | None | `["agent-1", "agent-3"]` | Key can access `agent-1` and `agent-3` | Key inherits team's permissions | | `["agent-1", "agent-2"]` | `["agent-1", "agent-3"]` | Key can access `agent-1` only | Intersection of both lists (most restrictive wins) | +| `agent_access_groups: ["clinical"]` | None | Key can access every agent tagged `clinical` | Access groups resolved to concrete agent IDs | +| `agent_access_groups: ["clinical"]` | `agents: ["agent-1"]` | Intersection of (every agent tagged `clinical`) and `["agent-1"]` | Mixing direct and group grants is supported | ## Viewing Permissions diff --git a/docs/auth_overview.md b/docs/auth_overview.md new file mode 100644 index 000000000..f0128e40e --- /dev/null +++ b/docs/auth_overview.md @@ -0,0 +1,161 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Gateway Auth Reference + +LiteLLM exposes two gateway surfaces that share most authentication and authorization primitives but diverge in a few important places. This page is the side-by-side reference: which header does what, where the two surfaces are symmetric, and where they're not. Each section links out to the dedicated page for the deep dive. + +| Surface | Endpoints | Dedicated docs | +|---|---|---| +| **MCP Gateway** | `/mcp`, `/{server}/mcp`, `/toolset/{name}/mcp`, `/sse`, `/v1/mcp/...`, `/mcp-rest/...` | [MCP Overview](./mcp) | +| **A2A Agent Gateway** | `/a2a/{agent_id}`, `/a2a/{agent_id}/message/send`, `/v1/agents/...` | [A2A Overview](./a2a) | + +--- + +## 1. Client → LiteLLM (authenticating the caller) + +Both surfaces accept the same LiteLLM Virtual Key headers and the same identification headers. The one place they diverge: the MCP **ASGI** routes (the streamable MCP endpoints at `/mcp`, `/{name}/mcp`, `/toolset/{name}/mcp`, `/sse`) bypass the standard FastAPI auth dependency and do not parse the vendor-specific auth aliases (`API-Key`, `x-api-key`, `x-goog-api-key`, `Ocp-Apim-Subscription-Key`) or `x-litellm-tags`. The MCP **REST/management** routes (`/v1/mcp/...`, `/mcp-rest/...`) and **all** A2A routes accept the full header set. + +| Header | Purpose | MCP ASGI | MCP REST + A2A | +|---|---|---|---| +| `x-litellm-api-key: Bearer sk-...` | Preferred LiteLLM Virtual Key header. Use whenever the inbound `Authorization` header may carry a different token (OAuth passthrough, OBO, A2A per-user forwarding). | ✓ | ✓ | +| `Authorization: Bearer sk-...` | Standard fallback. Stripped of the `Bearer ` prefix before lookup. | ✓ | ✓ | +| `API-Key`, `x-api-key`, `x-goog-api-key`, `Ocp-Apim-Subscription-Key` | Vendor-specific aliases (Azure, Anthropic, Google AI Studio, Azure APIM). | — | ✓ | +| `x-litellm-end-user-id` | End-user identification. Layers per-end-user budgets, MCP access intersection, and audit log entries on top of the key. `x-litellm-customer-id` is an accepted alias. | ✓ | ✓ | +| `x-litellm-trace-id` | Cross-request correlation ID. Falls back to `x-litellm-session-id` or any matching `x--session-id` header. | ✓ | ✓ | +| `x-litellm-session-id` | Session grouping. Same parse path as trace-id, lower priority. | ✓ | ✓ | +| `x-litellm-tags` | Comma-separated tags for spend-log labeling and tag-based routing. Body field `tags` takes precedence. | — (not parsed on MCP ASGI) | ✓ | +| `x-litellm-mcp-debug: true` | Returns masked diagnostic response headers (`x-mcp-debug-*`). See [MCP OAuth — Debugging](./mcp_oauth#debugging-oauth). | ✓ | — | +| `x-mcp-servers` | Scope a request to specific MCP servers (comma-separated). | ✓ | — | + +--- + +## 2. LiteLLM → Backend (authenticating the gateway to the agent or MCP server) + +This is the section where MCP and A2A diverge most. MCP has a first-class `auth_type` field on each server registration. **A2A has no `auth_type` field at all** — the outbound auth mode is inferred from what's present in `litellm_params`. + +### MCP — `auth_type` enum + +Nine values. The MCP server's outbound `Authorization` header (or per-request SigV4 signature) is determined by `auth_type`. See [MCP Overview — Add HTTP MCP Server](./mcp#add-http-mcp-server) for the full table. + +| `auth_type` | Mechanism | Dedicated docs | +|---|---|---| +| `none` | No auth header added | — | +| `api_key` / `bearer_token` / `basic` / `authorization` / `token` | Static header, sent verbatim per call | [MCP Overview](./mcp) | +| `oauth2` | PKCE (interactive) or M2M `client_credentials`. Discriminated by `oauth2_flow`. | [MCP OAuth](./mcp_oauth) | +| `oauth2_token_exchange` | RFC 8693 On-Behalf-Of (OBO) — exchange the caller's bearer token for a scoped MCP token | [MCP OBO Auth](./mcp_obo_auth) | +| `aws_sigv4` | Per-request SigV4 signature using a dedicated MCP-side credential chain | [MCP AWS SigV4](./mcp_aws_sigv4) | + +### A2A — auth mode inferred from `litellm_params` + +There is no `auth_type` field on an agent. The provider handler picks the auth mechanism from the contents of `litellm_params`: + +| Mode | When it fires | Send to backend | +|---|---|---| +| **Bearer / JWT** | `litellm_params.api_key` is set | `Authorization: Bearer ` | +| **SigV4** (AgentCore only) | `litellm_params.api_key` is unset | Per-request SigV4 via the full AWS credential chain. See [Bedrock AgentCore — A2A Gateway Authentication](./providers/bedrock_agentcore#a2a-gateway-authentication). | +| **Provider-native** | `litellm_params.custom_llm_provider` matches a non-Bedrock provider (Vertex AI Agent Engine, LangGraph, Azure AI Foundry, Pydantic AI) | The provider's normal auth path | + +The dual JWT-vs-SigV4 mode is specific to AgentCore. Other A2A providers (Vertex, LangGraph, Azure Foundry) use the provider's own credential conventions — see the relevant provider page under [Providers](./providers). + +### Zero-trust add-on (MCP only) + +If the MCP server needs to **cryptographically verify** the request came through LiteLLM, layer the [MCP JWT Signer](./mcp_zero_trust) guardrail on top. It signs every outbound tool call with a short-lived RS256 JWT and publishes a JWKS endpoint the MCP server can verify against. This is a guardrail (`guardrail: mcp_jwt_signer`, `mode: pre_mcp_call`), not an `auth_type` — it composes with any `auth_type`. + +--- + +## 3. Per-user header passthrough + +Both surfaces let clients forward credentials destined for a specific backend server/agent without admin pre-configuration. The conventions look symmetric but parse differently — be precise when copy-pasting. + +| Surface | Prefix | Parse rule | Match against | Example | +|---|---|---|---|---| +| **MCP** | `x-mcp-` | Format: `x-mcp-{server_alias}-{header_name}` | Server's `alias`, then `server_name` (case-insensitive) | `x-mcp-github-authorization: Bearer ghp_...` → server `github`, header `Authorization` | +| **A2A** | `x-a2a-` | Format: `x-a2a-{agent_name_or_id}-{header_name}`; matched against agent's UUID and human-readable name (both tried) | Agent's UUID **and** human-readable name (both tried) | `x-a2a-my-agent-x-api-key: secret` → agent `my-agent`, header `x-api-key` | + +Both surfaces also support admin-controlled alternatives that compose with the user passthrough: + +| Mechanism | MCP | A2A | Notes | +|---|---|---|---| +| `static_headers: {K: V}` | ✓ | ✓ | Always sent. **Wins over user passthrough** on key conflicts. | +| `extra_headers: [name, name, ...]` | ✓ | ✓ | Admin-allowlist of client header names to forward verbatim. | +| `x---
` convention | ✓ (`x-mcp-`) | ✓ (`x-a2a-`) | Client-driven, no admin config needed. | + +See [MCP Overview — Forwarding Custom Headers](./mcp#forwarding-custom-headers-to-mcp-servers) and [A2A Agent Authentication Headers](./a2a_agent_headers) for the full mechanics. + +--- + +## 4. Authorization — RBAC and access groups + +Both surfaces use the `object_permission` model with intersection-style resolution, but at different depths today. MCP resolves across five levels; A2A across two. The detailed flowcharts and tables live on the dedicated pages: + +- [MCP Permission Hierarchy](./mcp_control#permission-hierarchy) +- [A2A Agent Permission Management — How It Works](./a2a_agent_permissions#how-it-works) + +| Level | MCP field | A2A field | +|---|---|---| +| **Key** | `object_permission.mcp_servers`, `object_permission.mcp_access_groups`, `object_permission.mcp_tool_permissions` | `object_permission.agents`, `object_permission.agent_access_groups` | +| **Team** | Same | Same (inheritance-first: if the key has no list, it inherits the team's) | +| **End user** | Same (via `x-litellm-end-user-id`) | — not resolved today | +| **Agent** | Same (via `x-litellm-agent-id`) | — not applicable (the agent is the target) | +| **Org** | Same — acts as a **ceiling** | — not resolved today | + +| Concern | MCP | A2A | +|---|---|---| +| Per-server / per-agent allowlist | `object_permission.mcp_servers` | `object_permission.agents` | +| Access groups (tag-based grants) | `object_permission.mcp_access_groups` | `object_permission.agent_access_groups` | +| Per-server tool-level allowlist | `object_permission.mcp_tool_permissions: {server_id: [tool, ...]}` | n/a (tools live inside the agent) | +| Server-registration allowlist (admin-static) | `allowed_tools` / `disallowed_tools` on the MCP server | n/a | +| Param-level allowlist | `allowed_params: {tool_name: [param, ...]}` on the MCP server | n/a | +| Reject behaviour | `list_tools` filters out hidden servers; `call_tool` returns error | `GET /v1/agents` filters; `POST /a2a/{agent_id}` returns HTTP **403** | + +--- + +## 5. Trace IDs and identity propagation + +`x-litellm-trace-id` is **accepted** on every request and threaded through logging on both surfaces. A few A2A-specific extras: + +| Setting | Scope | Behaviour | +|---|---|---| +| `require_trace_id_on_calls_to_agent: true` | Per-agent, on the agent's `litellm_params` | Reject inbound `/a2a/{agent_id}` calls missing `x-litellm-trace-id` (or `x-litellm-session-id` fallback) with **HTTP 400**. See [A2A Overview — Trace ID enforcement](./a2a#trace-id-enforcement-optional-per-agent). | +| `require_trace_id_on_calls_by_agent: true` | Per-agent, on the agent's `litellm_params` | Reverse direction — when a key **owned by** that agent makes outbound calls, require a trace ID on those. | + +**Sub-agent identity propagation** — when LiteLLM dispatches a downstream call as part of an A2A invocation, it forwards `X-LiteLLM-Trace-Id` and `X-LiteLLM-Agent-Id` to maintain trace continuity and spend attribution. The original virtual key and end-user identity are **not** auto-forwarded. Use `extra_headers` or the `x-a2a-{agent_name_or_id}-{header}` convention to thread identity explicitly. See [A2A Overview — Sub-agent identity propagation](./a2a#sub-agent-identity-propagation). + +--- + +## 6. Guardrails on the gateway path + +| Concern | MCP | A2A | +|---|---|---| +| Pre-call input guardrails (Presidio, Bedrock, Lakera, Aporia, etc.) | `mode: pre_mcp_call` | Standard chat-completion guardrails apply to the underlying LLM calls the agent makes | +| During-call intervention | `mode: during_mcp_call` | — | +| Zero-trust JWT signing | [`mcp_jwt_signer` guardrail](./mcp_zero_trust) | — (not applicable to A2A today) | +| Documentation | [MCP Guardrails](./mcp_guardrail), [MCP Zero Trust](./mcp_zero_trust) | Standard [guardrails docs](./proxy/guardrails) apply via the agent's underlying model calls | + +--- + +## 7. Cheatsheet — what header does what + +For copy-paste, the high-frequency request headers across both surfaces: + +```http +# Always (LiteLLM-side auth and identification) +x-litellm-api-key: Bearer sk-... +# or +Authorization: Bearer sk-... + +x-litellm-end-user-id: user-42 +x-litellm-trace-id: 8f4a-2b1c-d3e5-... + +# MCP — server scoping / per-user passthrough +x-mcp-servers: github,zapier +x-mcp-github-authorization: Bearer ghp_ # user passthrough to github_mcp +x-litellm-mcp-debug: true # diagnostic response headers + +# A2A — per-user passthrough +x-a2a-my-agent-authorization: Bearer # caller's token to my-agent +x-a2a-my-agent-x-api-key: # additional per-agent header +``` + +For the deep dives, follow the cross-links above into the dedicated pages. diff --git a/docs/mcp.md b/docs/mcp.md index f6fe01ac2..af9b0f502 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -226,13 +226,19 @@ mcp_servers: - **Description**: Optional description for the server - **Auth Type**: Optional authentication type. Supported values: - | Value | Header sent | + | Value | Header sent (managed SSE/HTTP transport) | |-------|-------------| + | `none` | No auth header added | | `api_key` | `X-API-Key: ` | | `bearer_token` | `Authorization: Bearer ` | | `basic` | `Authorization: Basic ` | - | `authorization` | `Authorization: ` | - | `aws_sigv4` | Per-request AWS SigV4 signature ([details](./mcp_aws_sigv4.md)) | + | `authorization` | `Authorization: ` (verbatim, no prefix) | + | `token` | `Authorization: token ` (GitHub-style) | + | `oauth2` | `Authorization: Bearer ` — PKCE or M2M `client_credentials`. See [MCP OAuth](./mcp_oauth.md) | + | `oauth2_token_exchange` | `Authorization: Bearer ` — RFC 8693 On-Behalf-Of. See [MCP OBO Auth](./mcp_obo_auth.md) | + | `aws_sigv4` | Per-request AWS SigV4 signature. See [MCP AWS SigV4](./mcp_aws_sigv4.md) | + + Note: the header table above describes the managed SSE/HTTP transport path. The OpenAPI-tool path emits `Authorization: ApiKey ` instead of `X-API-Key` for `auth_type: api_key`; the deprecated `x-mcp-auth` broadcast header also uses the `ApiKey` form. - **Extra Headers**: Optional list of additional header names that should be forwarded from client to the MCP server - **Static Headers**: Optional map of header key/value pairs to include every request to the MCP server. diff --git a/docs/mcp_control.md b/docs/mcp_control.md index ccaa37f94..07c60a58d 100644 --- a/docs/mcp_control.md +++ b/docs/mcp_control.md @@ -35,6 +35,50 @@ When Creating a Key, Team, or Organization, you can select the allowed MCP Serve style={{width: '80%', display: 'block', margin: '0'}} /> +## Permission Hierarchy + +Permissions can be set at five distinct levels. When more than one level applies to a request, LiteLLM **intersects** the lists (most-restrictive wins) — except for the organization level, which acts as a **ceiling**. + +| Level | Source | How it composes | +|---|---|---| +| **Key** | `object_permission.mcp_servers` / `object_permission.mcp_access_groups` on the virtual key | If the key has an explicit list, it's used. | +| **Team** | Same fields on the team | If both key and team have lists, the result is the **intersection** (only servers in both). If only the team has a list, the key inherits it. | +| **End user** | Same fields on the `LiteLLM_EndUserTable` row matching `x-litellm-end-user-id` | Intersected with the running result. Skipped if no end-user-id is present on the request. | +| **Agent** | Same fields on the agent identified by `x-litellm-agent-id` | Intersected with the running result. Skipped if no agent-id is present. | +| **Organization** | Same fields on the org owning the key/team | Acts as a **ceiling** — the final allowed-server set is intersected with the org's list. If the org has no list, no additional restriction. | + +If no level has a list, the request can access **every** MCP server (open by default). + +```mermaid +flowchart TD + A[Inbound MCP request] --> B{Key has mcp_servers list?} + B -->|Yes| C[Start with key's list] + B -->|No| D[Start with: all servers] + C --> E{Team has list?} + D --> E + E -->|Yes, key also had list| F[Intersect with team's list] + E -->|Yes, key had no list| G[Use team's list] + E -->|No| H[Keep current] + F --> I + G --> I + H --> I + I[Running set] --> J{end-user-id present and end-user has list?} + J -->|Yes| K[Intersect with end-user list] + J -->|No| L[Keep current] + K --> M + L --> M + M{agent-id present and agent has list?} + M -->|Yes| N[Intersect with agent list] + M -->|No| O[Keep current] + N --> P + O --> P + P{Org has list?} + P -->|Yes| Q[Cap final set to org's list] + P -->|No| R[Final set] + Q --> R +``` + +The same intersection model applies to the per-server tool-level dict `mcp_tool_permissions` (see [Per-entity Tool-Level Permissions](#per-entity-tool-level-permissions) below). ## Allow/Disallow MCP Tools @@ -625,16 +669,93 @@ When creating API keys, you can assign them to specific access groups for permis /> - -## Set Allowed Tools for a Key, Team, or Organization +## Per-entity Tool-Level Permissions {#per-entity-tool-level-permissions} Control which tools different teams can access from the same MCP server. For example, give your Engineering team access to `list_repositories`, `create_issue`, and `search_code`, while Sales only gets `search_code` and `close_issue`. - This video shows how to set allowed tools for a Key, Team, or Organization. +### `mcp_tool_permissions` API + +`object_permission.mcp_tool_permissions` is a `Dict[server_id, List[tool_name]]` on the key, team, end-user, agent, or organization. It's evaluated **after** server-level access has been resolved (see [Permission Hierarchy](#permission-hierarchy) above) and applies the same five-level intersection — most-restrictive wins, organization acts as a ceiling. + +This is distinct from the server-registration-level `allowed_tools` / `disallowed_tools` (which apply to **every** caller of the server). `mcp_tool_permissions` lets you carve out per-team subsets without changing the server config. + + + + +```bash title="Engineering key — full GitHub access" showLineNumbers +curl -X POST "http://localhost:4000/key/generate" \ + -H "Authorization: Bearer sk-master-key" \ + -H "Content-Type: application/json" \ + -d '{ + "object_permission": { + "mcp_servers": ["github_mcp"], + "mcp_tool_permissions": { + "github_mcp": ["list_repositories", "create_issue", "search_code"] + } + } + }' +``` + +```bash title="Sales key — read-only on the same server" showLineNumbers +curl -X POST "http://localhost:4000/key/generate" \ + -H "Authorization: Bearer sk-master-key" \ + -H "Content-Type: application/json" \ + -d '{ + "object_permission": { + "mcp_servers": ["github_mcp"], + "mcp_tool_permissions": { + "github_mcp": ["search_code", "close_issue"] + } + } + }' +``` + + + + +```bash title="Team-wide tool subset (all keys inherit)" showLineNumbers +curl -X POST "http://localhost:4000/team/new" \ + -H "Authorization: Bearer sk-master-key" \ + -H "Content-Type: application/json" \ + -d '{ + "team_alias": "engineering", + "object_permission": { + "mcp_servers": ["github_mcp", "deepwiki_mcp"], + "mcp_tool_permissions": { + "github_mcp": ["list_repositories", "create_issue", "search_code"] + } + } + }' +``` + +When the key also sets `mcp_tool_permissions` for `github_mcp`, the resulting tool list is the **intersection** of the two. + + + + +When an agent (identified by `x-litellm-agent-id`) calls MCP tools, the agent's own `mcp_tool_permissions` participate in the intersection. Useful for capping what an autonomous agent can do regardless of which key originally invoked it. + +```bash showLineNumbers +curl -X PATCH "http://localhost:4000/v1/agents/{agent_id}" \ + -H "Authorization: Bearer sk-master-key" \ + -H "Content-Type: application/json" \ + -d '{ + "object_permission": { + "mcp_servers": ["github_mcp"], + "mcp_tool_permissions": { + "github_mcp": ["search_code"] + } + } + }' +``` + + + + ## Dashboard View Modes diff --git a/docs/mcp_oauth.md b/docs/mcp_oauth.md index e077f7def..894c9d143 100644 --- a/docs/mcp_oauth.md +++ b/docs/mcp_oauth.md @@ -296,11 +296,16 @@ curl http://localhost:4000/mcp-rest/tools/call \ | Field | Required | Description | |-------|----------|-------------| -| `auth_type` | Yes | Must be `oauth2` | -| `client_id` | Yes | OAuth2 client ID. Supports `os.environ/VAR_NAME` | -| `client_secret` | Yes | OAuth2 client secret. Supports `os.environ/VAR_NAME` | -| `token_url` | Yes | Token endpoint URL | -| `scopes` | No | List of scopes to request | +| `auth_type` | Yes | Must be `oauth2`. For RFC 8693 On-Behalf-Of, use `oauth2_token_exchange` instead — see [MCP OBO Auth](./mcp_obo_auth.md). | +| `oauth2_flow` | No | Explicit flow selector. One of `"client_credentials"` (M2M) or `"authorization_code"` (interactive PKCE). If omitted, LiteLLM infers from the other fields: `authorization_url` present → interactive; only `token_url` + `client_id` + `client_secret` → client credentials. Set explicitly when in doubt — for example, when a legacy DB row has both `authorization_url` and `token_url` but you want M2M. | +| `client_id` | Yes for M2M, optional for interactive | OAuth2 client ID. Required for `client_credentials`. For interactive flows, can be obtained via Dynamic Client Registration (RFC 7591) at `POST /{server_name}/register` if the upstream supports it. Supports `os.environ/VAR_NAME`. | +| `client_secret` | Yes for M2M, optional for interactive | OAuth2 client secret. Same applicability as `client_id`. Supports `os.environ/VAR_NAME`. | +| `token_url` | Yes for M2M, optional for interactive | Token endpoint URL. LiteLLM POSTs to this for `client_credentials` and for the authorization-code exchange. | +| `authorization_url` | Interactive only | Upstream authorization endpoint. When present, LiteLLM treats the server as interactive PKCE and proxies `GET /{server_name}/authorize` to this URL. | +| `registration_url` | Optional | Upstream Dynamic Client Registration endpoint (RFC 7591). When present, `POST /{server_name}/register` proxies through to this URL. | +| `scopes` | No | List of scopes to request. For M2M, joined into the `scope` parameter on the token request. For interactive, forwarded on the authorize request. | +| `token_validation` | No | Dict of key-value rules checked against the OAuth token response after the `/token` exchange. Any rule mismatch fails the exchange with `token_validation_failed`. Useful for asserting a tenant claim like `{"team.enterprise_id": "T12345"}`. | +| `token_storage_ttl_seconds` | No | Override the TTL for the per-user token cache (interactive flow). If unset, LiteLLM uses `expires_in - buffer` from the token response. | ## Debugging OAuth diff --git a/docs/mcp_public_internet.md b/docs/mcp_public_internet.md index c3ea5ccc6..ca9605587 100644 --- a/docs/mcp_public_internet.md +++ b/docs/mcp_public_internet.md @@ -255,3 +255,30 @@ general_settings: ``` When empty, the standard private ranges are used (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `127.0.0.0/8`). + +--- + +## Public Internet vs MCP Hub Visibility + +`available_on_public_internet` and the **MCP Hub** (`GET /public/mcp_hub`) are two separate mechanisms that are easy to confuse: + +| Concern | Controlled by | Default | +|---|---|---| +| Can an external (non-private-CIDR) caller see this server at the MCP tool endpoints (list/call)? | `available_on_public_internet` on the server | `True` (visible by default; toggle to `false` to restrict to private CIDRs) | +| Does this server appear in the unauthenticated `GET /public/mcp_hub` advertisement? | `litellm.public_mcp_servers` list, gated by `litellm.public_mcp_hub_strict_whitelist` | Hub strict whitelist is **on** by default — only servers explicitly listed in `public_mcp_servers` are advertised | + +In the **default strict-whitelist mode**, `available_on_public_internet: true` (the default) does not make a server appear in the hub. To advertise a server on the hub you also need to add it to `public_mcp_servers`: + +```yaml title="Server on the hub AND visible to external callers (the default)" showLineNumbers +litellm_settings: + public_mcp_servers: + - deepwiki + # public_mcp_hub_strict_whitelist defaults to true + +mcp_servers: + deepwiki: + url: https://mcp.deepwiki.com/mcp + # available_on_public_internet defaults to true +``` + +If you set `litellm.public_mcp_hub_strict_whitelist: false`, the hub falls back to advertising every server that has `available_on_public_internet: true` — but the IP-based access filter on this page still applies independently to the actual tool endpoints. diff --git a/docs/providers/bedrock_agentcore.md b/docs/providers/bedrock_agentcore.md index 7802624fc..17a362b13 100644 --- a/docs/providers/bedrock_agentcore.md +++ b/docs/providers/bedrock_agentcore.md @@ -245,8 +245,154 @@ model_list: | `qualifier` | string | Optional runtime qualifier/version to invoke a specific version of the agent runtime | | `runtimeSessionId` | string | Optional custom session ID (must be 33+ characters). If not provided, LiteLLM generates one automatically | +## LiteLLM A2A Gateway {#litellm-a2a-gateway} + +Register a Bedrock AgentCore runtime as a first-class A2A agent on the LiteLLM [Agent Gateway](../a2a). This gives you per-agent RBAC, access groups, trace-ID enforcement, and the `x-a2a-{agent_name_or_id}-{header}` per-user passthrough convention — same surface as any other A2A provider. + +This path is distinct from the chat-completions invocation above. Pick one based on your client: + +| You want to call AgentCore via... | Use this path | +|---|---| +| `/v1/chat/completions` with `model: bedrock/agentcore/` | Chat completions (covered above) | +| `POST /a2a/{agent_id}` with A2A JSON-RPC 2.0 (`message/send` or `message/stream`) | A2A Gateway (this section) | + +### 1. Register the agent + + + + +1. Go to **Agents** → **Add Agent**. +2. Select **Bedrock AgentCore** as the provider. +3. Paste the AgentCore Runtime ARN as the agent URL. +4. Configure AWS credentials (or leave blank to use the proxy's ambient credential chain — see [Authentication](#a2a-gateway-authentication) below). + + + + +```bash showLineNumbers +curl -X POST http://localhost:4000/v1/agents \ + -H "Authorization: Bearer sk-admin" \ + -H "Content-Type: application/json" \ + -d '{ + "agent_name": "my-agentcore-runtime", + "agent_card_params": { + "name": "my-agentcore-runtime", + "description": "Internal research agent", + "url": "bedrock/agentcore/arn:aws:bedrock-agentcore:us-east-1:123456789012:runtime/my-runtime" + }, + "litellm_params": { + "custom_llm_provider": "bedrock", + "aws_role_name": "arn:aws:iam::123456789012:role/LiteLLMAgentCoreInvoker", + "aws_region_name": "us-east-1" + } + }' +``` + + + + +### 2. Invoke via A2A + +```bash showLineNumbers +curl -X POST http://localhost:4000/a2a/my-agentcore-runtime/message/send \ + -H "x-litellm-api-key: Bearer sk-client-key" \ + -H "Content-Type: application/json" \ + -d '{ + "jsonrpc": "2.0", + "id": "1", + "method": "message/send", + "params": { + "message": { + "role": "user", + "parts": [{"kind": "text", "text": "Summarize the latest clinical trial results"}], + "messageId": "msg-1" + } + } + }' +``` + +### Authentication {#a2a-gateway-authentication} + +The AgentCore A2A path supports **two distinct outbound auth modes**, picked automatically based on what's in `litellm_params`: + +| Mode | When it fires | What's sent to AgentCore | +|---|---|---| +| **Bearer / JWT** | `litellm_params.api_key` is set (any value) | `Authorization: Bearer ` — SigV4 is bypassed entirely | +| **SigV4** | `litellm_params.api_key` is **not** set | Per-request SigV4 signature using the full AWS credential chain (below) | + +#### SigV4 credential resolution + +When SigV4 mode is active, credentials are resolved in this priority order: + +1. **`aws_web_identity_token` + `aws_role_name` + `aws_session_name`** → `sts:AssumeRoleWithWebIdentity`. Cross-account IRSA path. +2. **`aws_role_name` alone** → `sts:AssumeRole`. The proxy's ambient credentials (instance profile, IRSA, env vars) are the source identity. Session name auto-generated if omitted. +3. **`aws_profile_name`** → resolved via the boto3 profile loader (`~/.aws/credentials`). +4. **`aws_access_key_id` + `aws_secret_access_key` + `aws_session_token`** → explicit temporary credentials. +5. **`aws_access_key_id` + `aws_secret_access_key` + `aws_region_name`** → explicit long-lived credentials. All three must be set; without `aws_region_name` this branch is skipped. +6. **No credentials configured** → boto3 default chain (env vars, IRSA via `AWS_WEB_IDENTITY_TOKEN_FILE` + `AWS_ROLE_ARN`, instance metadata). + +Recognized fields on `litellm_params` for SigV4: + +| Field | Description | +|---|---| +| `aws_role_name` | IAM role ARN to assume via STS | +| `aws_session_name` | Session name for the AssumeRole call (auto-generated if omitted) | +| `aws_external_id` | ExternalId passed to `sts:AssumeRole` for cross-account trust policies | +| `aws_web_identity_token` | OIDC token for `AssumeRoleWithWebIdentity` (set explicitly or via `AWS_WEB_IDENTITY_TOKEN_FILE` env) | +| `aws_profile_name` | AWS CLI profile name | +| `aws_sts_endpoint` | Custom STS endpoint (VPC endpoints, FIPS endpoints) | +| `aws_access_key_id` / `aws_secret_access_key` / `aws_session_token` | Explicit credentials | +| `aws_region_name` | AWS region. If omitted, detected from the runtime ARN in `agent_card_params.url`. | + +#### IRSA on EKS + +For Kubernetes deployments using [IAM Roles for Service Accounts](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html), no explicit credential configuration is needed — boto3's default chain picks up `AWS_WEB_IDENTITY_TOKEN_FILE` and `AWS_ROLE_ARN` from the pod environment automatically. + +If you want the invocation to assume a **second** role (e.g. separate the pod's identity from the agent-invocation identity for CloudTrail attribution), combine IRSA with `aws_role_name`: + +```bash showLineNumbers +curl -X POST http://localhost:4000/v1/agents \ + -H "Authorization: Bearer sk-admin" \ + -H "Content-Type: application/json" \ + -d '{ + "agent_name": "production-runtime", + "agent_card_params": { + "name": "production-runtime", + "url": "bedrock/agentcore/arn:aws:bedrock-agentcore:us-east-1:123456789012:runtime/prod" + }, + "litellm_params": { + "custom_llm_provider": "bedrock", + "aws_role_name": "arn:aws:iam::123456789012:role/AgentCoreInvocationRole", + "aws_session_name": "litellm-prod" + } + }' +``` + +The proxy pod's IRSA role serves as the source identity for the AssumeRole call; the assumed role's CloudTrail entries reflect the agent invocation. + +### Per-user header passthrough + +The standard A2A header forwarding mechanisms apply — see [A2A Agent Authentication Headers](../a2a_agent_headers) for the full reference. All three methods work with AgentCore: + +- **`static_headers`** — always sent to AgentCore (e.g. a custom `X-Tenant-Id`) +- **`extra_headers`** — admin-configured allowlist of client headers to forward +- **`x-a2a-{agent_name_or_id}-{header}` convention** — caller-driven forwarding without admin config + +Note that the SigV4 / Bearer auth handled by `litellm_params` is **separate** from the agent-level header forwarding above. Auth headers are computed per-request by the AWS signer; user passthrough headers are merged into the request after signing. + +### RBAC and trace IDs + +All standard A2A controls apply: +- **Per-agent RBAC** — [Agent Permission Management](../a2a_agent_permissions). Returns HTTP 403 when the calling key/team isn't authorized for the AgentCore agent. +- **Access groups** — tag the agent with one or more access groups in the LiteLLM dashboard, then grant the group to a team or key via `object_permission.agent_access_groups`. See [Agent Access Groups](../a2a_agent_permissions#agent-access-groups). +- **Trace ID enforcement** — set `require_trace_id_on_calls_to_agent: true` on `litellm_params` to require `x-litellm-trace-id` on every inbound call. See [A2A Overview — Trace ID enforcement](../a2a#trace-id-enforcement-optional-per-agent). + ## Further Reading - [AWS Bedrock AgentCore Documentation](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_agentcore_InvokeAgentRuntime.html) - [LiteLLM Authentication to Bedrock](https://docs.litellm.ai/docs/providers/bedrock#boto3---authentication) +- [LiteLLM A2A Gateway Overview](../a2a) +- [A2A Agent Authentication Headers](../a2a_agent_headers) +- [A2A Agent Permission Management](../a2a_agent_permissions) +- [MCP AWS SigV4](../mcp_aws_sigv4) — for the AgentCore-hosted MCP servers path (separate from the agent runtimes path) diff --git a/sidebars.js b/sidebars.js index faec856fa..dd06140ed 100644 --- a/sidebars.js +++ b/sidebars.js @@ -356,6 +356,7 @@ const sidebars = { "skills_gateway", ], }, + { type: "doc", id: "auth_overview", label: "Gateway Auth Reference" }, ], }, { From 906c3dad32f9d580dbada2fc05546701d77113f2 Mon Sep 17 00:00:00 2001 From: agent-shin <279878236+agent-shin@users.noreply.github.com> Date: Wed, 20 May 2026 21:57:40 -0700 Subject: [PATCH 5/8] Update Claude Code compatibility matrix (#175) litellm_version: v1.83.14-stable claude_code_version: 2.1.126 generated_at: 2026-05-20T06:09:45Z Co-authored-by: litellm-compat-matrix-bot --- src/data/compatibility-matrix.json | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/data/compatibility-matrix.json b/src/data/compatibility-matrix.json index 8d6fba87d..3faed6fc1 100644 --- a/src/data/compatibility-matrix.json +++ b/src/data/compatibility-matrix.json @@ -1,6 +1,6 @@ { "schema_version": "1", - "generated_at": "2026-05-19T06:18:22Z", + "generated_at": "2026-05-20T06:09:45Z", "litellm_version": "v1.83.14-stable", "claude_code_version": "2.1.126", "providers": [ @@ -23,7 +23,7 @@ }, "bedrock_converse": { "status": "fail", - "error": "[claude-haiku-4-5-bedrock-converse] claude returned empty assistant text" + "error": "[claude-haiku-4-5-bedrock-converse] claude CLI failed: exit=1; text=API Error: Content block is not a text block" }, "vertex_ai": { "status": "pass" @@ -44,8 +44,7 @@ "status": "pass" }, "bedrock_converse": { - "status": "fail", - "error": "[claude-haiku-4-5-bedrock-converse] claude CLI failed: exit=1; text=API Error: Content block is not a text block" + "status": "pass" }, "vertex_ai": { "status": "pass" @@ -134,7 +133,7 @@ }, "bedrock_converse": { "status": "fail", - "error": "[claude-sonnet-4-6-bedrock-converse] claude CLI failed: exit=1; text=API Error: Content block is not a text block" + "error": "[claude-opus-4-7-bedrock-converse] no `thinking` content block observed in stream-json events" }, "vertex_ai": { "status": "fail", @@ -222,8 +221,7 @@ "status": "pass" }, "bedrock_converse": { - "status": "fail", - "error": "[claude-haiku-4-5-bedrock-converse] claude CLI failed: exit=1; text=API Error: Content block is not a text block" + "status": "pass" }, "vertex_ai": { "status": "pass" @@ -267,7 +265,7 @@ }, "bedrock_converse": { "status": "fail", - "error": "[claude-haiku-4-5-bedrock-converse] claude CLI failed: exit=1; text=API Error: Content block is not a text block" + "error": "[claude-opus-4-7-bedrock-converse] claude CLI failed: exit=1; api_status=400; text=API Error: 400 {\"error\":{\"message\":\"litellm.BadRequestError: BedrockException - {\\\"message\\\":\\\"The text field in the ContentBlock object at messages.1.content.0 is blank. Add text to the text field, and try again.\\\"}. Received Model Group=claude-opus-4-7-bedrock-converse\\nAvailable Model Group Fallbacks=None\",\"type\":null,\"param\":null,\"code\":\"400\"}}" }, "vertex_ai": { "status": "pass" @@ -333,7 +331,8 @@ "error": "[claude-opus-4-7-bedrock-invoke] claude CLI failed: exit=1; text=API Error: Claude Code is unable to respond to this request, which appears to violate our Usage Policy (https://www.anthropic.com/legal/aup). Try rephrasing the request or attempting a different approach. If you are seeing this refusal repeatedly, try running /model claude-sonnet-4-20250514 to switch models." }, "bedrock_converse": { - "status": "pass" + "status": "fail", + "error": "[claude-opus-4-7-bedrock-converse] claude returned empty assistant text" }, "vertex_ai": { "status": "pass" From 081c3e323e5ed389db6e1539d2c1bb94041e088b Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Thu, 21 May 2026 12:05:11 +0530 Subject: [PATCH 6/8] docs(mcp): pass guardrails via extra_body in OpenAI SDK example (#188) The OpenAI Python SDK rejects guardrails as a top-level argument; use extra_body to send LiteLLM-specific params to the proxy. Co-authored-by: Cursor --- docs/mcp_guardrail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/mcp_guardrail.md b/docs/mcp_guardrail.md index c1f2fbec0..fae9699c4 100644 --- a/docs/mcp_guardrail.md +++ b/docs/mcp_guardrail.md @@ -73,7 +73,7 @@ response = client.chat.completions.create( {"role": "user", "content": "Send an email to 555-123-4567 with my SSN 123-45-6789"} ], tools=[{"type": "mcp", "server_label": "litellm", "server_url": "litellm_proxy"}], - guardrails=["mcp-input-validation"] + extra_body={"guardrails": ["mcp-input-validation"]}, ) ``` From 2a38292e743b74cb8ff9d098b4478cfd59dc56dc Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Thu, 21 May 2026 13:20:06 -0700 Subject: [PATCH 7/8] docs(release_notes): add v1.84.1 and v1.85.1 patch release notes (#191) Patch releases on top of v1.84.0 and v1.85.0, each shipping the same three PRs: Gemini 3.5 Flash day-0 support (#28268), a Vertex AI tool-calling fix for Gemini 3.5+ HTTP 400 errors (#28324), and a cross-pod spend-counter seeding fix (#27854). Adds release_notes/v1.84.1/ and release_notes/v1.85.1/ pages and updates the release_notes overview (Latest Release block + table). --- release_notes/index.md | 8 ++-- release_notes/v1.84.1/index.md | 72 ++++++++++++++++++++++++++++++++++ release_notes/v1.85.1/index.md | 72 ++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 release_notes/v1.84.1/index.md create mode 100644 release_notes/v1.85.1/index.md diff --git a/release_notes/index.md b/release_notes/index.md index 01377498c..3643a00e3 100644 --- a/release_notes/index.md +++ b/release_notes/index.md @@ -10,11 +10,11 @@ LiteLLM ships new releases regularly with new provider support, performance impr ## Latest Release -### [v1.85.0 — Realtime GA, MCP Gateway Expansion & Hardened Multi-Tenancy](/release_notes/v1.85.0/v1-85-0) +### [v1.85.1 — Gemini 3.5 Flash & Reliability Fixes](/release_notes/v1.85.1/v1-85-1) -_May 16, 2026_ +_May 20, 2026_ -OpenAI Realtime GA (with `gpt-realtime-2` pricing and `/openai/v1/realtime` logging), a broad multi-tenancy hardening sweep across keys, projects, batches, files, and MCP servers, MCP Gateway expansion (org-level permissions, OBO auth, `delegate_auth_to_upstream` PKCE passthrough), an observability overhaul (Prometheus label/cardinality fixes + OTEL handler isolation), and new models including xAI `grok-4.3`, OpenAI `gpt-realtime-2`, OpenRouter Qwen 3.6 Plus, SambaNova MiniMax-M2.7, and Bedrock Z.AI GLM-5. +A patch release on top of [v1.85.0](/release_notes/v1.85.0/v1-85-0): day-0 support for Gemini 3.5 Flash on Google AI Studio and Vertex AI, a Vertex AI tool-calling fix that resolves Gemini 3.5+ HTTP 400 `Unknown name "id"` errors, and a cross-pod spend-counter fix that stops team budgets from double-counting after a Redis cache miss. The same fixes are also shipped for the 1.84 line as [v1.84.1](/release_notes/v1.84.1/v1-84-1). --- @@ -22,6 +22,8 @@ OpenAI Realtime GA (with `gpt-realtime-2` pricing and `/openai/v1/realtime` logg | Version | Date | Highlights | | ----------------------------------- | ------------ | ---------------------------------------------------------- | +| [v1.85.1](/release_notes/v1.85.1/v1-85-1) | May 20, 2026 | Patch — Gemini 3.5 Flash day-0 + cross-pod spend fix | +| [v1.84.1](/release_notes/v1.84.1/v1-84-1) | May 20, 2026 | Patch — Gemini 3.5 Flash day-0 + cross-pod spend fix | | [v1.85.0](/release_notes/v1.85.0/v1-85-0) | May 16, 2026 | Realtime GA, MCP Gateway expansion & hardened multi-tenancy | | [v1.84.0](/release_notes/v1.84.0/v1-84-0) | May 14, 2026 | Reliability hardening + multi-pod budget accuracy | | [v1.83.14](/release_notes/v1.83.14/v1-83-14) | Apr 27, 2026 | GPT-5.5, Prompt Compression & Memory API | diff --git a/release_notes/v1.84.1/index.md b/release_notes/v1.84.1/index.md new file mode 100644 index 000000000..60fe78ff7 --- /dev/null +++ b/release_notes/v1.84.1/index.md @@ -0,0 +1,72 @@ +--- +title: "v1.84.1 - Gemini 3.5 Flash & Reliability Fixes" +slug: "v1-84-1" +date: 2026-05-20T00:00:00 +authors: + - name: Krrish Dholakia + title: CEO, LiteLLM + url: https://www.linkedin.com/in/krish-d/ + image_url: https://pbs.twimg.com/profile_images/1298587542745358340/DZv3Oj-h_400x400.jpg + - name: Ishaan Jaff + title: CTO, LiteLLM + url: https://www.linkedin.com/in/reffajnaahsi/ + image_url: https://pbs.twimg.com/profile_images/1613813310264340481/lz54oEiB_400x400.jpg + - name: Yuneng Jiang + title: Senior Full Stack Engineer, LiteLLM + url: https://www.linkedin.com/in/yuneng-david-jiang-455676139/ + image_url: https://avatars.githubusercontent.com/u/171294688?v=4 +hide_table_of_contents: false +--- + +## Deploy this version + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + +```bash +docker run \ +-e STORE_MODEL_IN_DB=True \ +-p 4000:4000 \ +docker.litellm.ai/berriai/litellm:1.84.1 +``` + + + + +```bash +pip install litellm==1.84.1 +``` + + + + +`v1.84.1` is a patch release on top of [`v1.84.0`](/release_notes/v1.84.0/v1-84-0). It adds day-0 support for Gemini 3.5 Flash and ships two reliability fixes — cross-pod spend accuracy and Vertex AI tool calling. + +## New Models / Updated Models + +#### New Model Support (1 new model) + +| Provider | Model | Context Window | Input ($/1M tokens) | Output ($/1M tokens) | Features | +| --- | --- | --- | --- | --- | --- | +| Gemini / Vertex AI | `gemini/gemini-3.5-flash`, `vertex_ai/gemini-3.5-flash` | 1M | $1.50 | $9.00 | Reasoning, vision, audio input, PDF input, prompt caching, web search, function calling, response schema | + +#### Features + +- **[Gemini](../../docs/providers/gemini)** / **[Vertex AI](../../docs/providers/vertex)** + - Day-0 support for Gemini 3.5 Flash on both Google AI Studio and Vertex AI - [PR #28268](https://github.com/BerriAI/litellm/pull/28268) + +### Bug Fixes + +- **[Vertex AI](../../docs/providers/vertex)** + - Omit the `function_call` / `function_response` `id` on Vertex Gemini 3.5+ tool turns, fixing HTTP 400 `Unknown name "id"` errors. Google AI Studio (`gemini` provider) still forwards the `id` on Gemini 3.5+ for strict tool-call matching - [PR #28324](https://github.com/BerriAI/litellm/pull/28324) + +## Spend Tracking, Budgets and Rate Limiting + +- Seed the Redis spend counter via `SET NX` instead of `INCRBYFLOAT` to prevent cross-pod double-seeding. On multi-pod deployments this previously caused team `spend` to jump to ~Nx the pod count after a Redis cache miss / TTL expiry, triggering false "Budget Crossed" alerts - [PR #27854](https://github.com/BerriAI/litellm/pull/27854) + +## Full Changelog + +https://github.com/BerriAI/litellm/compare/v1.84.0...v1.84.1 diff --git a/release_notes/v1.85.1/index.md b/release_notes/v1.85.1/index.md new file mode 100644 index 000000000..04ead06dd --- /dev/null +++ b/release_notes/v1.85.1/index.md @@ -0,0 +1,72 @@ +--- +title: "v1.85.1 - Gemini 3.5 Flash & Reliability Fixes" +slug: "v1-85-1" +date: 2026-05-20T00:00:00 +authors: + - name: Krrish Dholakia + title: CEO, LiteLLM + url: https://www.linkedin.com/in/krish-d/ + image_url: https://pbs.twimg.com/profile_images/1298587542745358340/DZv3Oj-h_400x400.jpg + - name: Ishaan Jaff + title: CTO, LiteLLM + url: https://www.linkedin.com/in/reffajnaahsi/ + image_url: https://pbs.twimg.com/profile_images/1613813310264340481/lz54oEiB_400x400.jpg + - name: Yuneng Jiang + title: Senior Full Stack Engineer, LiteLLM + url: https://www.linkedin.com/in/yuneng-david-jiang-455676139/ + image_url: https://avatars.githubusercontent.com/u/171294688?v=4 +hide_table_of_contents: false +--- + +## Deploy this version + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + +```bash +docker run \ +-e STORE_MODEL_IN_DB=True \ +-p 4000:4000 \ +docker.litellm.ai/berriai/litellm:1.85.1 +``` + + + + +```bash +pip install litellm==1.85.1 +``` + + + + +`v1.85.1` is a patch release on top of [`v1.85.0`](/release_notes/v1.85.0/v1-85-0). It adds day-0 support for Gemini 3.5 Flash and ships two reliability fixes — cross-pod spend accuracy and Vertex AI tool calling. + +## New Models / Updated Models + +#### New Model Support (1 new model) + +| Provider | Model | Context Window | Input ($/1M tokens) | Output ($/1M tokens) | Features | +| --- | --- | --- | --- | --- | --- | +| Gemini / Vertex AI | `gemini/gemini-3.5-flash`, `vertex_ai/gemini-3.5-flash` | 1M | $1.50 | $9.00 | Reasoning, vision, audio input, PDF input, prompt caching, web search, function calling, response schema | + +#### Features + +- **[Gemini](../../docs/providers/gemini)** / **[Vertex AI](../../docs/providers/vertex)** + - Day-0 support for Gemini 3.5 Flash on both Google AI Studio and Vertex AI - [PR #28268](https://github.com/BerriAI/litellm/pull/28268) + +### Bug Fixes + +- **[Vertex AI](../../docs/providers/vertex)** + - Omit the `function_call` / `function_response` `id` on Vertex Gemini 3.5+ tool turns, fixing HTTP 400 `Unknown name "id"` errors. Google AI Studio (`gemini` provider) still forwards the `id` on Gemini 3.5+ for strict tool-call matching - [PR #28324](https://github.com/BerriAI/litellm/pull/28324) + +## Spend Tracking, Budgets and Rate Limiting + +- Seed the Redis spend counter via `SET NX` instead of `INCRBYFLOAT` to prevent cross-pod double-seeding. On multi-pod deployments this previously caused team `spend` to jump to ~Nx the pod count after a Redis cache miss / TTL expiry, triggering false "Budget Crossed" alerts - [PR #27854](https://github.com/BerriAI/litellm/pull/27854) + +## Full Changelog + +https://github.com/BerriAI/litellm/compare/v1.85.0...v1.85.1 From 19ec76c632f9c73bee07c585b5097472d73489d7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 21 May 2026 22:33:04 +0000 Subject: [PATCH 8/8] docs: replace slow Inkeep search with offline @easyops-cn/docusaurus-search-local Inkeep search was reported as slow and exhibited focus / Cmd+K bugs (cursor in the wrong place, page preventing repeated searches). Swap the navbar SearchBar over to @easyops-cn/docusaurus-search-local, which builds a static lunr index at build time and renders results instantly on the client. - Add @easyops-cn/docusaurus-search-local theme with both docs and release_notes routes indexed. - Keep stop words and stems (technical docs frequently search short tokens) and enable highlight-on-target-page. - Drop the SearchBar config from @inkeep/cxkit-docusaurus so it only provides the floating Ask AI chat button (still useful for AI Q&A). Co-authored-by: Mateo Wang --- docusaurus.config.js | 35 ++- package-lock.json | 530 +++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 3 files changed, 562 insertions(+), 4 deletions(-) diff --git a/docusaurus.config.js b/docusaurus.config.js index 6e2d19ece..abc84e240 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -68,9 +68,10 @@ const config = { [ '@inkeep/cxkit-docusaurus', { - SearchBar: { - ...inkeepConfig, - }, + // Note: Inkeep's hosted search was reported as slow with focus/Cmd+K + // bugs. The navbar search is now powered by the fast offline index + // from @easyops-cn/docusaurus-search-local (see `themes` below). + // Inkeep is still used for the floating "Ask AI" chat button. ChatButton: { ...inkeepConfig, }, @@ -242,7 +243,33 @@ const config = { ], ], - themes: ['@docusaurus/theme-mermaid'], + themes: [ + '@docusaurus/theme-mermaid', + [ + // Fast, offline full-text search index. Replaces the slow hosted Inkeep + // search bar. Uses lunr under the hood and indexes both the main docs + // and the release_notes docs plugin instance. + require.resolve('@easyops-cn/docusaurus-search-local'), + /** @type {import('@easyops-cn/docusaurus-search-local').PluginOptions} */ + ({ + hashed: true, + docsRouteBasePath: ['docs', 'release_notes'], + docsDir: ['docs', 'release_notes'], + indexBlog: true, + indexPages: false, + language: ['en'], + // Programming/CLI docs frequently rely on short tokens (e.g. "is", + // "or", "if", "as") as searchable terms — keep stop words indexed. + removeDefaultStopWordFilter: true, + highlightSearchTermsOnTargetPage: true, + searchResultLimits: 8, + searchResultContextMaxLength: 50, + searchBarShortcut: true, + searchBarShortcutHint: true, + searchBarShortcutKeymap: 'mod+k', + }), + ], + ], markdown: { mermaid: true, }, diff --git a/package-lock.json b/package-lock.json index d14ca96cf..1c5fe6d29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@docusaurus/plugin-ideal-image": "3.8.1", "@docusaurus/preset-classic": "3.8.1", "@docusaurus/theme-mermaid": "3.8.1", + "@easyops-cn/docusaurus-search-local": "^0.55.1", "@inkeep/cxkit-docusaurus": "0.5.107", "@mdx-js/react": "3.1.1", "clsx": "1.2.1", @@ -4252,6 +4253,165 @@ "node": ">=18.0" } }, + "node_modules/@easyops-cn/autocomplete.js": { + "version": "0.38.1", + "resolved": "https://registry.npmjs.org/@easyops-cn/autocomplete.js/-/autocomplete.js-0.38.1.tgz", + "integrity": "sha512-drg76jS6syilOUmVNkyo1c7ZEBPcPuK+aJA7AksM5ZIIbV57DMHCywiCr+uHyv8BE5jUTU98j/H7gVrkHrWW3Q==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "immediate": "^3.2.3" + } + }, + "node_modules/@easyops-cn/docusaurus-search-local": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@easyops-cn/docusaurus-search-local/-/docusaurus-search-local-0.55.1.tgz", + "integrity": "sha512-jmBKj1J+tajqNrCvECwKCQYTWwHVZDGApy8lLOYEPe+Dm0/f3Ccdw8BP5/OHNpltr7WDNY2roQXn+TWn2f1kig==", + "license": "MIT", + "dependencies": { + "@docusaurus/plugin-content-docs": "^2 || ^3", + "@docusaurus/theme-translations": "^2 || ^3", + "@docusaurus/utils": "^2 || ^3", + "@docusaurus/utils-common": "^2 || ^3", + "@docusaurus/utils-validation": "^2 || ^3", + "@easyops-cn/autocomplete.js": "^0.38.1", + "@node-rs/jieba": "^1.6.0", + "cheerio": "^1.0.0", + "clsx": "^2.1.1", + "comlink": "^4.4.2", + "debug": "^4.2.0", + "fs-extra": "^10.0.0", + "klaw-sync": "^6.0.0", + "lunr": "^2.3.9", + "lunr-languages": "^1.4.0", + "mark.js": "^8.11.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "@docusaurus/theme-common": "^2 || ^3", + "open-ask-ai": "^0.7.3", + "react": "^16.14.0 || ^17 || ^18 || ^19", + "react-dom": "^16.14.0 || 17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "open-ask-ai": { + "optional": true + } + } + }, + "node_modules/@easyops-cn/docusaurus-search-local/node_modules/cheerio": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz", + "integrity": "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==", + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "encoding-sniffer": "^0.2.1", + "htmlparser2": "^10.1.0", + "parse5": "^7.3.0", + "parse5-htmlparser2-tree-adapter": "^7.1.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^7.19.0", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=20.18.1" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/@easyops-cn/docusaurus-search-local/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@easyops-cn/docusaurus-search-local/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@easyops-cn/docusaurus-search-local/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@easyops-cn/docusaurus-search-local/node_modules/htmlparser2": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@floating-ui/core": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.4.tgz", @@ -4633,6 +4793,18 @@ "langium": "3.3.1" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, "node_modules/@noble/hashes": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", @@ -4645,6 +4817,259 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@node-rs/jieba": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba/-/jieba-1.10.4.tgz", + "integrity": "sha512-GvDgi8MnBiyWd6tksojej8anIx18244NmIOc1ovEw8WKNUejcccLfyu8vj66LWSuoZuKILVtNsOy4jvg3aoxIw==", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@node-rs/jieba-android-arm-eabi": "1.10.4", + "@node-rs/jieba-android-arm64": "1.10.4", + "@node-rs/jieba-darwin-arm64": "1.10.4", + "@node-rs/jieba-darwin-x64": "1.10.4", + "@node-rs/jieba-freebsd-x64": "1.10.4", + "@node-rs/jieba-linux-arm-gnueabihf": "1.10.4", + "@node-rs/jieba-linux-arm64-gnu": "1.10.4", + "@node-rs/jieba-linux-arm64-musl": "1.10.4", + "@node-rs/jieba-linux-x64-gnu": "1.10.4", + "@node-rs/jieba-linux-x64-musl": "1.10.4", + "@node-rs/jieba-wasm32-wasi": "1.10.4", + "@node-rs/jieba-win32-arm64-msvc": "1.10.4", + "@node-rs/jieba-win32-ia32-msvc": "1.10.4", + "@node-rs/jieba-win32-x64-msvc": "1.10.4" + } + }, + "node_modules/@node-rs/jieba-android-arm-eabi": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-android-arm-eabi/-/jieba-android-arm-eabi-1.10.4.tgz", + "integrity": "sha512-MhyvW5N3Fwcp385d0rxbCWH42kqDBatQTyP8XbnYbju2+0BO/eTeCCLYj7Agws4pwxn2LtdldXRSKavT7WdzNA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-android-arm64": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-android-arm64/-/jieba-android-arm64-1.10.4.tgz", + "integrity": "sha512-XyDwq5+rQ+Tk55A+FGi6PtJbzf974oqnpyCcCPzwU3QVXJCa2Rr4Lci+fx8oOpU4plT3GuD+chXMYLsXipMgJA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-darwin-arm64": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-darwin-arm64/-/jieba-darwin-arm64-1.10.4.tgz", + "integrity": "sha512-G++RYEJ2jo0rxF9626KUy90wp06TRUjAsvY/BrIzEOX/ingQYV/HjwQzNPRR1P1o32a6/U8RGo7zEBhfdybL6w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-darwin-x64": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-darwin-x64/-/jieba-darwin-x64-1.10.4.tgz", + "integrity": "sha512-MmDNeOb2TXIZCPyWCi2upQnZpPjAxw5ZGEj6R8kNsPXVFALHIKMa6ZZ15LCOkSTsKXVC17j2t4h+hSuyYb6qfQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-freebsd-x64": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-freebsd-x64/-/jieba-freebsd-x64-1.10.4.tgz", + "integrity": "sha512-/x7aVQ8nqUWhpXU92RZqd333cq639i/olNpd9Z5hdlyyV5/B65LLy+Je2B2bfs62PVVm5QXRpeBcZqaHelp/bg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-linux-arm-gnueabihf": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-linux-arm-gnueabihf/-/jieba-linux-arm-gnueabihf-1.10.4.tgz", + "integrity": "sha512-crd2M35oJBRLkoESs0O6QO3BBbhpv+tqXuKsqhIG94B1d02RVxtRIvSDwO33QurxqSdvN9IeSnVpHbDGkuXm3g==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-linux-arm64-gnu": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-linux-arm64-gnu/-/jieba-linux-arm64-gnu-1.10.4.tgz", + "integrity": "sha512-omIzNX1psUzPcsdnUhGU6oHeOaTCuCjUgOA/v/DGkvWC1jLcnfXe4vdYbtXMh4XOCuIgS1UCcvZEc8vQLXFbXQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-linux-arm64-musl": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-linux-arm64-musl/-/jieba-linux-arm64-musl-1.10.4.tgz", + "integrity": "sha512-Y/tiJ1+HeS5nnmLbZOE+66LbsPOHZ/PUckAYVeLlQfpygLEpLYdlh0aPpS5uiaWMjAXYZYdFkpZHhxDmSLpwpw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-linux-x64-gnu": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-linux-x64-gnu/-/jieba-linux-x64-gnu-1.10.4.tgz", + "integrity": "sha512-WZO8ykRJpWGE9MHuZpy1lu3nJluPoeB+fIJJn5CWZ9YTVhNDWoCF4i/7nxz1ntulINYGQ8VVuCU9LD86Mek97g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-linux-x64-musl": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-linux-x64-musl/-/jieba-linux-x64-musl-1.10.4.tgz", + "integrity": "sha512-uBBD4S1rGKcgCyAk6VCKatEVQb6EDD5I40v/DxODi5CuZVCANi9m5oee/MQbAoaX7RydA2f0OSCE9/tcwXEwUg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-wasm32-wasi": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-wasm32-wasi/-/jieba-wasm32-wasi-1.10.4.tgz", + "integrity": "sha512-Y2umiKHjuIJy0uulNDz9SDYHdfq5Hmy7jY5nORO99B4pySKkcrMjpeVrmWXJLIsEKLJwcCXHxz8tjwU5/uhz0A==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@node-rs/jieba-win32-arm64-msvc": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-win32-arm64-msvc/-/jieba-win32-arm64-msvc-1.10.4.tgz", + "integrity": "sha512-nwMtViFm4hjqhz1it/juQnxpXgqlGltCuWJ02bw70YUDMDlbyTy3grCJPpQQpueeETcALUnTxda8pZuVrLRcBA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-win32-ia32-msvc": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-win32-ia32-msvc/-/jieba-win32-ia32-msvc-1.10.4.tgz", + "integrity": "sha512-DCAvLx7Z+W4z5oKS+7vUowAJr0uw9JBw8x1Y23Xs/xMA4Em+OOSiaF5/tCJqZUCJ8uC4QeImmgDFiBqGNwxlyA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@node-rs/jieba-win32-x64-msvc": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@node-rs/jieba-win32-x64-msvc/-/jieba-win32-x64-msvc-1.10.4.tgz", + "integrity": "sha512-+sqemSfS1jjb+Tt7InNbNzrRh1Ua3vProVvC4BZRPg010/leCbGFFiQHpzcPRfpxAXZrzG5Y0YBTsPzN/I4yHQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -7449,6 +7874,16 @@ "tslib": "^2.6.2" } }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/body-parser": { "version": "1.19.6", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", @@ -9734,6 +10169,12 @@ "node": ">= 0.8" } }, + "node_modules/comlink": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/comlink/-/comlink-4.4.2.tgz", + "integrity": "sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==", + "license": "Apache-2.0" + }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", @@ -11504,6 +11945,19 @@ "node": ">= 0.8" } }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", + "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, "node_modules/end-of-stream": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", @@ -13379,6 +13833,12 @@ "node": ">=16.x" } }, + "node_modules/immediate": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", + "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==", + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -14013,6 +14473,15 @@ "node": ">=0.10.0" } }, + "node_modules/klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.11" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -14252,6 +14721,24 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "license": "MIT" + }, + "node_modules/lunr-languages": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lunr-languages/-/lunr-languages-1.20.0.tgz", + "integrity": "sha512-3LVgE7ekWXt04NBci/hjm+NXJxXZeRXuyClL0kA0HONyBOjxhP3ZQkuWIM4Ok3pbeptUW/rj3XcJcJuJVPwPYA==", + "license": "MPL-1.1" + }, + "node_modules/mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "license": "MIT" + }, "node_modules/markdown-extensions": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", @@ -17433,6 +17920,18 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/parse5/node_modules/entities": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", @@ -21736,6 +22235,15 @@ "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", "license": "MIT" }, + "node_modules/undici": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", + "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", @@ -22735,6 +23243,28 @@ "node": ">=0.8.0" } }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", diff --git a/package.json b/package.json index 73ff62dcb..701c8bfc4 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@docusaurus/plugin-ideal-image": "3.8.1", "@docusaurus/preset-classic": "3.8.1", "@docusaurus/theme-mermaid": "3.8.1", + "@easyops-cn/docusaurus-search-local": "^0.55.1", "@inkeep/cxkit-docusaurus": "0.5.107", "@mdx-js/react": "3.1.1", "clsx": "1.2.1",