Skip to content

feat(mcp): McpServer admin resource (registration + CRUD) - #664

Merged
moonming merged 2 commits into
mainfrom
feat/mcp-server-resource
Jun 29, 2026
Merged

feat(mcp): McpServer admin resource (registration + CRUD)#664
moonming merged 2 commits into
mainfrom
feat/mcp-server-resource

Conversation

@moonming

@moonming moonming commented Jun 29, 2026

Copy link
Copy Markdown
Member

What

Adds McpServer as a first-class Admin resource (parallel to ProviderKey): an upstream MCP server registration that the MCP gateway endpoint will source its upstreams from, instead of the explicit list it takes today. This is the control-plane prerequisite for wiring the gateway behind the governance pipeline.

Fields (customer-facing, rendered into the Admin API reference): display_name (unique; namespace prefix for the server's tools, so __ is rejected), url (Streamable HTTP endpoint), transport (enum, streamable_http), auth_type (none/bearer) + secret (gateway-held), timeout_ms, enabled.

How (mirrors the existing resource scaffolding)

  • aisix-coreMcpServer struct + McpTransport/McpAuthType enums + Resource impl (kind = "mcp_servers"); schema validator built through the same struct_root_schema producer the other resources use, so the published schema == the enforced schema; registered in dump-schema; new mcp_servers snapshot table.
  • aisix-etcd — loader decode arm + supervisor merge / present-check / delete / clone dispatch for the new kind.
  • aisix-adminConfigStore get/put/list/delete on both InMemoryStore and EtcdConfigStore (etcd subkey mcp_servers); /admin/v1/mcp_servers[/:id] handlers (schema-validate, dup-name 409, reject reserved __, uuid on POST, revision bump on PUT) + routes.

Test plan

  • Resource unit tests (defaults, bearer auth, unknown-field/enum rejection, Resource trait, round-trip).
  • mcp_servers_round_trip_through_real_etcd — full Admin CRUD over a real etcd.
  • loader_picks_up_every_admin_write extended to seed an mcp_servers row and assert the loader accepts it — this catches subkey-constant drift between EtcdConfigStore and aisix_etcd::loader (the most likely wiring bug).
  • cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings, cargo test -p aisix-core -p aisix-etcd -p aisix-admin all green; schemas/resources/mcp_server.schema.json regenerated via dump-schema.

Scope / explicitly deferred

  • OpenAPI reference for the two new routes is deferred to Document /admin/v1/mcp_servers in the Admin API OpenAPI #663. Verified that no test or CI check enforces it (both OpenAPI coverage tests use hardcoded path/schema lists, not the live router; the generate admin openapi CI step validates structure, not a committed snapshot). The routes are fully functional; only the generated OpenAPI omits them. Called out here per the merge gate so it's a tracked gap, not a silent one.
  • Wiring the gateway endpoint to source upstreams from this snapshot + per-tool ACL / quota / guardrail reuse is the next step (depends on this).

Refs AISIX-Cloud#894

Summary by CodeRabbit

  • New Features

    • Added support for managing MCP server resources in the admin UI and API, including create, view, update, delete, and list actions.
    • Introduced MCP server configuration fields for connection, authentication, enablement, and timeout settings.
    • Added MCP server schema support and snapshot/loader handling so these resources are included in exports and imports.
  • Bug Fixes

    • Enforced unique display names and rejected invalid names with reserved separators to prevent conflicts.
    • Improved persistence and watch handling so MCP server changes are consistently stored and reflected.

Adds `McpServer` as a first-class Admin resource, parallel to ProviderKey:
an upstream MCP server registration (name, url, Streamable HTTP transport,
gateway-held auth, timeout, enabled) that the MCP gateway endpoint will
source upstreams from.

- aisix-core: `McpServer` struct + `McpTransport`/`McpAuthType` enums +
  Resource impl (`kind = "mcp_servers"`); schema validator wired through the
  same `struct_root_schema` producer as the other resources (published ==
  enforced), registered in dump-schema; new `mcp_servers` snapshot table.
- aisix-etcd: loader decode arm + supervisor merge / present / delete /
  clone dispatch for the new kind.
- aisix-admin: `ConfigStore` get/put/list/delete + InMemoryStore +
  EtcdConfigStore (subkey `mcp_servers`); `/admin/v1/mcp_servers[/:id]`
  handlers (validate, dup-name 409, reject the reserved `__` separator in
  display_name, uuid on POST, revision bump on PUT) + routes.
- tests: resource unit tests, an etcd CRUD round-trip, and the new kind
  added to `loader_picks_up_every_admin_write` (asserts the Admin →
  EtcdConfigStore → loader path agrees on the subkey constant).

The committed `schemas/resources/mcp_server.schema.json` is regenerated via
`cargo run -p aisix-core --bin dump-schema`.

OpenAPI reference for the new routes is deferred to #663 (no functionality
depends on it; the routes work, only the generated OpenAPI omits them).

Refs AISIX-Cloud#894
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@moonming, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1ad28507-9222-4532-bac2-272edbbc23d0

📥 Commits

Reviewing files that changed from the base of the PR and between 0a55fd8 and cb81cab.

📒 Files selected for processing (1)
  • crates/aisix-admin/src/mcp_servers_handlers.rs
📝 Walkthrough

Walkthrough

Adds McpServer as a new gateway resource kind end-to-end: a Rust model with McpTransport/McpAuthType enums and JSON schema, admin HTTP CRUD handlers at /admin/v1/mcp_servers with display_name uniqueness and separator validation, etcd and in-memory store implementations, snapshot table inclusion, and etcd loader/supervisor dispatch for watch events.

Changes

McpServer Resource End-to-End

Layer / File(s) Summary
McpServer model, enums, schema, and snapshot
crates/aisix-core/src/models/mcp_server.rs, crates/aisix-core/src/models/schema.rs, crates/aisix-core/src/models/snapshot.rs, crates/aisix-core/src/models/mod.rs, crates/aisix-core/src/lib.rs, crates/aisix-core/src/bin/dump-schema.rs, schemas/resources/mcp_server.schema.json
Defines McpServer struct, McpTransport/McpAuthType enums, Resource trait impl, JSON schema validator/generator, re-exports, mcp_servers field in AisixSnapshot, and schema dump output.
ConfigStore trait and storage backends
crates/aisix-admin/src/store.rs, crates/aisix-admin/src/etcd_store.rs
Extends ConfigStore trait with four MCP server CRUD methods; implements them for InMemoryStore (new DashMap field) and EtcdConfigStore (new MCP_SERVERS_SUBKEY).
Admin HTTP handlers and routing
crates/aisix-admin/src/mcp_servers_handlers.rs, crates/aisix-admin/src/lib.rs
Adds list/get/create/update/delete handlers with schema validation, display_name uniqueness and __ separator enforcement, UUID generation, and revision management; mounts routes in build_router.
Etcd loader and supervisor wiring
crates/aisix-etcd/src/loader.rs, crates/aisix-etcd/src/supervisor.rs
Extends build_snapshot to parse mcp_servers etcd entries; updates apply_put, apply_delete, and clone_snapshot to propagate mcp_servers through incremental watch events.
Integration tests
crates/aisix-admin/tests/etcd_integration.rs
Adds an etcd-backed CRUD round-trip test for /admin/v1/mcp_servers; updates loader compatibility test to seed one MCP server and assert stats.accepted == 7 and snap.mcp_servers.len() == 1.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • api7/aisix#630: Modifies the same clone_snapshot, apply_put, and apply_delete functions in supervisor.rs using the identical pattern for a different resource kind (guardrail_attachments).
  • api7/aisix#638: Modifies the same models/schema.rs and dump-schema.rs pipeline that this PR extends with the mcp_server schema/validator.

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Security Check ❌ Error McpServer.secret is returned in CRUD responses and persisted verbatim to etcd/in-memory with no redaction or encryption. Make secret write-only/redacted in API DTOs and encrypt or secret-reference it before storage; add tests to ensure no response echoes it.
E2e Test Quality Review ⚠️ Warning Real-etcd coverage is only a happy-path POST→list→DELETE smoke; MCP GET/PUT, 404/409, invalid payloads, and __ rejection aren't exercised end-to-end. Add MCP route E2E cases for GET/PUT/delete-after-delete, duplicate display_name, reserved __, and malformed payloads; keep the loader test as a separate smoke.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the new McpServer admin resource and its registration/CRUD scope.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mcp-server-resource

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Audit (CLAUDE.md §8) on #664 returned APPROVE with two LOW suggestions,
both folded in:

- LOW: `auth_type=bearer` with no/empty `secret` was accepted and would
  send an empty `Authorization: Bearer ` upstream. `decode()` now rejects
  it (400), alongside the existing reserved-`__` check.
- LOW: the `__`-rejection (net-new logic) had no coverage. Added unit tests
  for the decode guards: rejects `__` in display_name, rejects bearer
  without secret, accepts a valid server.

Refs AISIX-Cloud#894
@moonming

Copy link
Copy Markdown
Member Author

Independent audit (CLAUDE.md §8): APPROVE

A fresh audit agent checked out the branch, regenerated the schema, ran build/tests/clippy, and grepped every provider_keys/rate_limit_policies seam for an mcp_servers analog.

Verified complete & correct:

  • All four supervisor dispatch sites present (initial-merge, present-check, delete, clone_snapshot — the silent-drop-on-RCU trap), loader decode arm, snapshot table + total_entries, ConfigStore + both stores.
  • etcd subkey "mcp_servers" is string-exact across MCP_SERVERS_SUBKEY / loader match arm / Resource::kind().
  • No schema drift (dump-schema byte-identical to committed JSON); aisix-server builds; clippy -D warnings clean; 300 + 6 tests pass.
  • Subkey-drift regression is test-guarded: loader_picks_up_every_admin_write asserts accepted == 7 and mcp_servers.len() == 1.
  • Deferred-OpenAPI (Document /admin/v1/mcp_servers in the Admin API OpenAPI #663) claim verified: OpenAPI tests parse the static doc, never the live router; no CI enforces parity. Not merge-blocking.

Two LOW findings, both folded in (cb81cab):

Sev Finding Resolution
LOW auth_type=bearer with no/empty secret accepted → would send empty Authorization: Bearer decode() now rejects it (400)
LOW __-rejection (net-new logic) untested Added unit tests for the decode guards (reject __, reject bearer-without-secret, accept valid)

Note (not actioned): secret is returned plaintext on get/list — mirrors ProviderKey exactly (established posture, not logged). If secrets are ever redacted on read-back, do it uniformly across both resources, not here.

Verdict: no HIGH/MEDIUM. fmt + clippy --workspace --all-targets -D warnings + unit tests green.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
crates/aisix-admin/tests/etcd_integration.rs (1)

199-218: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the MCP-specific payload, not just the id.

This helper only checks status, id, and list size, so this new test still passes if url, auth_type, secret, or MCP defaults serialize incorrectly. A small response-body/GET assertion here, plus one 400/409 case for the new validation rules, would give this path real coverage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/aisix-admin/tests/etcd_integration.rs` around lines 199 - 218, The new
mcp_servers_round_trip_through_real_etcd test only validates status, id, and
list size, so it can miss MCP field serialization bugs. Update this test to
assert the MCP-specific response/body fields using the mcp_servers endpoint and
the returned resource, verifying url, auth_type, secret handling, and any MCP
defaults. Also add one negative case covering the new validation rules with a
400 or 409 response so the MCP path is actually exercised beyond the shared
admin_crud_round_trip helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/aisix-admin/src/mcp_servers_handlers.rs`:
- Around line 69-80: update_mcp_server currently does a read-modify-write using
existing.revision + 1 and an unconditional put_mcp_server, which allows
concurrent overwrites. Update the store API used by
mcp_servers_handlers::update_mcp_server and ResourceEntry::new to perform a
compare-and-swap on the persisted revision (using the revision read from
get_mcp_server), return a conflict/error when the revision no longer matches,
and have the successful write return the committed revision back to the handler.
- Around line 54-59: The `add_mcp_server` and related write path currently
enforce `display_name` uniqueness by scanning with `list_mcp_servers` and then
calling `assert_unique_display_name` before `put_mcp_server`, which is not safe
under concurrency. Move the uniqueness check into the backing store layer as an
atomic operation, ideally via a transaction or compare-and-swap style write in
the store implementation used by `state.store.put_mcp_server`, so both insert
paths cannot race and persist the same `display_name`. Update the handler to
rely on that atomic store guarantee rather than doing pre-checks in
`mcp_servers_handlers`.

In `@crates/aisix-core/src/models/mcp_server.rs`:
- Around line 41-46: The current MCP server validation in the `McpServer` model
only documents the `auth_type`/`secret` rule, so invalid combinations can still
pass through the shared validator. Add an explicit cross-field validation in the
`McpServer`/`Validator` path that enforces `secret` is required when `auth_type`
is `bearer` and must be absent when `auth_type` is `none`, so both admin writes
and loader ingestion reject bad states before they reach the live snapshot.
- Around line 18-23: The `McpServer::display_name` schema currently enforces
only `min_length`, but the contract also forbids the reserved `__` separator.
Update the canonical validation used by `validate_mcp_server`/loader paths in
`mcp_server.rs` to reject any `display_name` containing `__`, and make sure the
schema reflects that constraint so direct etcd writes cannot persist ambiguous
tool namespaces.

---

Nitpick comments:
In `@crates/aisix-admin/tests/etcd_integration.rs`:
- Around line 199-218: The new mcp_servers_round_trip_through_real_etcd test
only validates status, id, and list size, so it can miss MCP field serialization
bugs. Update this test to assert the MCP-specific response/body fields using the
mcp_servers endpoint and the returned resource, verifying url, auth_type, secret
handling, and any MCP defaults. Also add one negative case covering the new
validation rules with a 400 or 409 response so the MCP path is actually
exercised beyond the shared admin_crud_round_trip helper.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c66a94b2-7cec-408d-9ed1-555ee0ed7f43

📥 Commits

Reviewing files that changed from the base of the PR and between 2ed22fd and 0a55fd8.

📒 Files selected for processing (14)
  • crates/aisix-admin/src/etcd_store.rs
  • crates/aisix-admin/src/lib.rs
  • crates/aisix-admin/src/mcp_servers_handlers.rs
  • crates/aisix-admin/src/store.rs
  • crates/aisix-admin/tests/etcd_integration.rs
  • crates/aisix-core/src/bin/dump-schema.rs
  • crates/aisix-core/src/lib.rs
  • crates/aisix-core/src/models/mcp_server.rs
  • crates/aisix-core/src/models/mod.rs
  • crates/aisix-core/src/models/schema.rs
  • crates/aisix-core/src/models/snapshot.rs
  • crates/aisix-etcd/src/loader.rs
  • crates/aisix-etcd/src/supervisor.rs
  • schemas/resources/mcp_server.schema.json

Comment on lines +54 to +59
let all = state.store.list_mcp_servers().await?;
assert_unique_display_name(&all, &mcp_server.display_name, None)?;

let id = Uuid::new_v4().to_string();
let entry = ResourceEntry::new(&id, mcp_server, STARTING_REVISION);
state.store.put_mcp_server(entry.clone()).await?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Enforce display_name uniqueness atomically.

Both write paths do list_mcp_serversassert_unique_display_nameput_mcp_server. With only blind writes underneath, two concurrent requests can both pass the scan and persist the same display_name, which breaks the downstream <display_name>__<tool> namespace contract. This needs to move to an atomic store operation / etcd transaction instead of staying in the handler.

Also applies to: 76-80

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/aisix-admin/src/mcp_servers_handlers.rs` around lines 54 - 59, The
`add_mcp_server` and related write path currently enforce `display_name`
uniqueness by scanning with `list_mcp_servers` and then calling
`assert_unique_display_name` before `put_mcp_server`, which is not safe under
concurrency. Move the uniqueness check into the backing store layer as an atomic
operation, ideally via a transaction or compare-and-swap style write in the
store implementation used by `state.store.put_mcp_server`, so both insert paths
cannot race and persist the same `display_name`. Update the handler to rely on
that atomic store guarantee rather than doing pre-checks in
`mcp_servers_handlers`.

Comment on lines +69 to +80
let existing = state
.store
.get_mcp_server(&id)
.await?
.ok_or(AdminError::NotFound)?;
let mcp_server = decode(&raw)?;

let all = state.store.list_mcp_servers().await?;
assert_unique_display_name(&all, &mcp_server.display_name, Some(&id))?;

let entry = ResourceEntry::new(&id, mcp_server, existing.revision + 1);
state.store.put_mcp_server(entry.clone()).await?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Guard PUT with optimistic concurrency.

update_mcp_server reads the current row, bumps the revision locally, and then calls an unconditional put_mcp_server. Two concurrent updates to the same id will both succeed and one silently overwrites the other. The store layer needs a compare-and-swap on the persisted revision and should return the committed revision to the handler.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/aisix-admin/src/mcp_servers_handlers.rs` around lines 69 - 80,
update_mcp_server currently does a read-modify-write using existing.revision + 1
and an unconditional put_mcp_server, which allows concurrent overwrites. Update
the store API used by mcp_servers_handlers::update_mcp_server and
ResourceEntry::new to perform a compare-and-swap on the persisted revision
(using the revision read from get_mcp_server), return a conflict/error when the
revision no longer matches, and have the successful write return the committed
revision back to the handler.

Comment on lines +18 to +23
/// Operator-facing label, unique within the gateway. It is used as the
/// namespace prefix for this server's tools, which are exposed to clients as
/// `<display_name>__<tool>`, so it must not contain the reserved separator
/// `__`.
#[schemars(length(min = 1))]
pub display_name: String,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Enforce the reserved __ separator in the canonical schema.

This field documents a hard contract, but the model only enforces minLength. Because validate_mcp_server is also what the loader uses for etcd rows, a direct write can still persist display_name values containing __, which makes the <display_name>__<tool> namespace ambiguous at runtime.

Suggested fix
-    #[schemars(length(min = 1))]
+    #[schemars(regex(pattern = "^(?!.*__).+$"), length(min = 1))]
     pub display_name: String,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// Operator-facing label, unique within the gateway. It is used as the
/// namespace prefix for this server's tools, which are exposed to clients as
/// `<display_name>__<tool>`, so it must not contain the reserved separator
/// `__`.
#[schemars(length(min = 1))]
pub display_name: String,
/// Operator-facing label, unique within the gateway. It is used as the
/// namespace prefix for this server's tools, which are exposed to clients as
/// `<display_name>__<tool>`, so it must not contain the reserved separator
/// `__`.
#[schemars(regex(pattern = "^(?!.*__).+$"), length(min = 1))]
pub display_name: String,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/aisix-core/src/models/mcp_server.rs` around lines 18 - 23, The
`McpServer::display_name` schema currently enforces only `min_length`, but the
contract also forbids the reserved `__` separator. Update the canonical
validation used by `validate_mcp_server`/loader paths in `mcp_server.rs` to
reject any `display_name` containing `__`, and make sure the schema reflects
that constraint so direct etcd writes cannot persist ambiguous tool namespaces.

Comment on lines +41 to +46
/// Authentication credential for the upstream server. Required when
/// `auth_type` is `bearer`, where it is sent as `Authorization: Bearer
/// <secret>` on every upstream request. Leave unset when `auth_type` is
/// `none`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub secret: Option<String>,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Encode the auth_type/secret dependency instead of only documenting it.

Right now the shared validator still accepts both {"auth_type":"bearer"} and {"auth_type":"none","secret":"..."}. Since admin writes and loader ingestion both go through that validator, those invalid combinations can be stored and propagated into the live snapshot.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/aisix-core/src/models/mcp_server.rs` around lines 41 - 46, The current
MCP server validation in the `McpServer` model only documents the
`auth_type`/`secret` rule, so invalid combinations can still pass through the
shared validator. Add an explicit cross-field validation in the
`McpServer`/`Validator` path that enforces `secret` is required when `auth_type`
is `bearer` and must be absent when `auth_type` is `none`, so both admin writes
and loader ingestion reject bad states before they reach the live snapshot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant