Review MCP Servers Before They Go Live
An upstream MCP server is arbitrary remote code your agents will call with your callers' context. Registering one and publishing it to your gateways are separate acts in AISIX: every MCP server carries an approval_status, and only an approved server is sent to the gateways.
A server that is waiting for review, or that was rejected, has no presence on the gateway at all. It does not appear in tools/list and its tools cannot be called, even by a caller API key that explicitly allowlists them. Approval, not the enabled flag, is what makes a server reachable.
This gives you two things. A member can propose an MCP server without being able to publish it. And an approval is tied to the exact upstream, name, credential, and exposure that was reviewed.
Review States
approval_status | On the gateways | Reached by |
|---|---|---|
pending_review | Not projected | Submitting a server, or changing something material about an approved one. |
approved | Projected to allowed_environments | Approving it, or registering it with POST /mcp_servers. |
rejected | Not projected | Rejecting it. |
Servers registered before this feature was introduced are approved, so upgrading does not interrupt tool traffic.
Who Can Approve
Approving is the same permission as registering: write on mcp_servers. Owners and admins hold it. Anyone who can publish a server directly gains nothing from reviewing one, so tying the two together costs no control. It also lets an organization with a single administrator approve its own submissions.
The separable half is a second permission, write on mcp_server_submissions. A custom role that holds it — and not write on mcp_servers — can propose servers and revise proposals that are not approved yet, but cannot publish anything. That is the role to give a team that integrates its own tooling.
Propose a Server
The examples on this page use the AISIX Cloud Admin API. Follow the On-Premises AISIX Cloud Quickstart to run the stack, create an admin token with write scope, and create an environment, then set:
export AISIX_CP="http://localhost:8080/api"
export AISIX_TOKEN="aisix_pat_YOUR_ADMIN_TOKEN"
export ENV_ID="YOUR_ENVIRONMENT_ID"
Submit the server with POST /mcp_server_submissions. The body is the same as POST /mcp_servers:
curl -sS -X POST "$AISIX_CP/mcp_server_submissions" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "runbooks",
"url": "https://mcp.example.com/runbooks",
"auth_type": "none",
"allowed_environments": ["'"$ENV_ID"'"]
}'
The server is registered but unpublished:
{
"mcp_server": {
"id": "6f64f080-17d7-44d9-b995-6a353e71f6bc",
"name": "runbooks",
"url": "https://mcp.example.com/runbooks",
"enabled": true,
"allowed_environments": ["YOUR_ENVIRONMENT_ID"],
"approval_status": "pending_review",
"submitted_at": "2026-07-29T09:30:00Z"
}
}
Note that enabled is true and allowed_environments names an environment, yet no gateway can reach the server. Those fields describe the configuration that will take effect once the server is approved.
In the dashboard, the MCP servers page shows a Pending review badge on the row and a band at the top of the page listing how many servers are waiting.
Approve or Reject
List what is waiting by reading approval_status from GET /mcp_servers:
curl -sS "$AISIX_CP/mcp_servers" \
-H "Authorization: Bearer $AISIX_TOKEN" \
| jq '.data[] | select(.approval_status == "pending_review") | {id, name, url}'
Check the upstream before approving. Confirm that the URL is the one you expect. Check that the name is not a near-copy of an existing server's name, and that the credential belongs to the account you intend the gateway to act as.
Approve it, and it is published to the environments in allowed_environments:
export MCP_SERVER_ID="YOUR_MCP_SERVER_ID"
curl -sS -X POST "$AISIX_CP/mcp_servers/$MCP_SERVER_ID/approve" \
-H "Authorization: Bearer $AISIX_TOKEN"
Reject it with a reason instead. The note is returned on the server, so whoever proposed it can see what to change:
curl -sS -X POST "$AISIX_CP/mcp_servers/$MCP_SERVER_ID/reject" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"review_notes": "Not on the trusted registry. Use the internal mirror."}'
Approving an already-approved server, or rejecting an already-rejected one, returns 400.
Revise a Rejected Proposal
PATCH /mcp_server_submissions/{id} corrects a submission and puts it back in the queue:
curl -sS -X PATCH "$AISIX_CP/mcp_server_submissions/$MCP_SERVER_ID" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "https://mcp.internal.example.com/runbooks"}'
This route only reaches servers that are not approved. Patching an approved server through it returns 403: publishing and editing live configuration both require write on mcp_servers.
Changing an Approved Server
A change to what was reviewed returns the server to pending_review and withdraws it from every environment until it is approved again. Otherwise an approval granted to a benign upstream could be inherited by a different one.
These fields are material:
name— the namespace agents address the tools by, and the field a look-alike name would targeturl— the upstream itselftransportauth_type,secret,client_id,token_url,scopes— the credential and where it is presentedallowed_environments— where the server is exposed
These are not, and keep the approval:
enabledtimeout_ms
Rotating an upstream credential is a material change, so plan for the approval step: the server stops serving tool calls between the patch and the re-approval. Use enabled for a pause that does not need a review.
The dashboard warns before you save an edit that will take the server offline.
Revoke an Approval
Rejecting a server that is currently approved revokes it: it is withdrawn from every environment it was serving, and callers lose its tools. Use it when an upstream stops being trustworthy.
curl -sS -X POST "$AISIX_CP/mcp_servers/$MCP_SERVER_ID/reject" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"review_notes": "Upstream credential compromised."}'
Revocation takes effect on the gateways within seconds and needs no gateway restart. To remove the registry entry entirely, delete the server instead.
Audit Trail
Every step is recorded in the organization audit log with the acting user, the time, and the before and after state of the server:
| Action | Recorded when |
|---|---|
submit | A server is proposed for review. |
create | A server is registered directly and published. |
approve | A server is approved. |
reject | A server is rejected, or an approval is revoked. |
update | A server's configuration is changed. |
delete | A server is removed. |
Read them from the audit log in the dashboard, or with GET /audit_events?resource_type=mcp_server.
Related Reading
- MCP Gateway Overview — registering servers and connecting a client
- Control tool access — which tools a caller API key may call
- Manage access with policies — environment and team level tool grants