Build the admission gate of an MCP gateway
RolesAgent Systems EngineeringCustomer AI Deployment
The question
Take-home. Build the admission module of an MCP gateway. It takes the tools/list results of several servers and produces an admitted set, a rejected set and an audit trail. It must page through every list completely, pin each descriptor, qualify names before it merges, reject a descriptor that mirrors a sensitive parameter into a header or places x-mcp-header anywhere but a direct top-level property, validate structured results against output schemas on the invoke path, and emit header audit events that carry names and never values. Describe your design, the tests you ship, and the evidence a reviewer should expect.
Answer it first, aloud or on paper. Then open the answer.
The answer
Show short answer
Run the five gates in order and keep them separate. Discover with an opaque cursor loop that stops only when nextCursor is absent or null; admit each descriptor against a canonical whole-descriptor digest, a full schema-tree walk for x-mcp-header, and a sensitive-name policy; qualify every tool as <server>.<tool> before the merge; and validate structuredContent against the published output schema before anything reaches the model. Tests prove that one rejected descriptor does not block valid tools, that an empty-string cursor continues, that an annotation under oneOf, items or $ref is rejected, and that a changed inputSchema fails the pin. The evidence is the page trace, the admitted and rejected sets, the digest ledger and redacted audit events.
Show full answer
Modules
| Module | Responsibility |
|---|---|
discover | opaque cursor loop per server; records every request as a page trace |
admit | canonical digest, schema walk, header policy, scanner tripwire |
namespace | qualified names, backend mapping, collision policy |
consume | argument validation before invoke, result validation before model use |
audit | header names, rejection categories, digests; no values |
Admission rules
- Canonicalize the descriptor with sorted keys and compact separators, hash it, and store the digest under the qualified name with publisher evidence and approval time. An unknown key is quarantined. A changed digest is a rug pull and is quarantined until re-approved.
- Walk the entire
inputSchematree.x-mcp-headeris legal only on a direct member ofinputSchema.properties, with type string, integer or boolean, a non-empty token header name unique without regard to case, and integer values inside the JavaScript safe range. Reject it under a nested object, aoneOfbranch,items, a$reftarget or any output schema. - Reject a descriptor whose mirrored property is named
password,secret,token,api_keyorauthorization. - Run the static scanner for role tags, instruction overrides, concealment, secret access and obscured destinations. Treat a hit as review evidence, not as a verdict.
- A descriptor without an input schema object is rejected; a tool with no arguments declares
{"type": "object", "additionalProperties": false}. - Store the verified publisher namespace and the live
server/discoveridentity with the approved record. On a later refresh, quarantine a server whose namespace or live identity differs, even when every descriptor digest still matches.
Namespacing and merge
Qualified names are the public gateway names. Record the backend mapping separately. Sort before merging so local order does not depend on process timing. Never let discovery order decide a collision.
The invoke path
Validate arguments before the call. Encode mirrored header values as plain visible ASCII or the exact base64 UTF-8 sentinel. Validate structuredContent as any JSON value against the output schema, including results with isError: true. Audit the header name and the encoding decision, never the value.
Tests to ship
- A two-page list whose first page returns
""as the cursor produces two requests. - One unsafe descriptor is rejected and every valid descriptor on the same server is admitted.
- An annotation under
oneOf,items, a nestedpropertiesor a$refdefinition is rejected even when that branch is never used. - Changing
inputSchemawithout changing the description fails the pin. - An unchanged descriptor behind a changed
server/discoveridentity is quarantined. - Two servers that both expose
searchbecome two qualified names and two digests. - An array result passes an array output schema and an object fails it.
- Header audit events contain names and no values, checked by a test that greps for the value.
Evidence for the reviewer
The capstone grades admission evidence: verified publisher namespace and live server/discover identity, deterministic and complete paginated discovery, descriptor validation before model exposure, validated structured output, and routing metadata that preserves authorization boundaries. Capture the descriptor, the page trace, the admitted set, the rejected set with a reason per descriptor, the digest ledger and one validated result. Do not claim gateway compatibility from a successful tools/call alone.
Follow-ups
L3SeniorWhy not stop pagination when a page's
toolsarray is empty?Show expected answer
emptiness is the server's choice; continue while
nextCursoris present and notnull.L4StaffTwo servers return byte-identical descriptors under different names. What does the ledger hold?
Show expected answer
two qualified keys with two digests; the same digest under different keys is fine, and a different digest under the same key is the rug pull.
L4StaffThe scanner flags a legitimate warning sentence in a description. What happens?
Show expected answer
review, not automatic rejection; the scanner is a tripwire and a human approves the full descriptor.
L4StaffA descriptor passes every check and is poisoned from day one. What catches it?
Show expected answer
nothing automatic; the design needs a review queue before the first pin, because hash equality proves stability and not safety.
Wrong answers
Show the wrong answers that interviewers hear
“Hash the description; schemas rarely change.”
Schema and annotation changes are the rug pull; pin the whole canonical descriptor.
“Trust the server's deterministic order and skip the sort.”
Local registry order must not depend on process startup timing; sort before you merge.
“Log header values to make debugging easier.”
A mirrored value can be routing data or, in a bad descriptor, a secret; audit names and rejection categories only.
Taught in
The tool poisoning lesson gives the pin and the scanner, the tool contracts lesson gives the header and cursor rules, and the capstone names the admission evidence.