MCP Servers: Architecture, OpenAPI Readiness, and Security
Understand MCP server roles and transports, assess OpenAPI tool readiness, and test permissions, credentials, and launch blockers.
An MCP server can turn a model request into an action in another system. A safe launch review must test the server's own decisions, not rely on a model to choose the right tool. The official MCP security guidance covers token passthrough, confused-deputy behavior, server-side request forgery, and other trust-boundary failures. HTTP authorization is specified separately from local stdio credential handling.
Start with one approved server, a fixed tool inventory, synthetic accounts, and an owner-controlled environment. Record the server version and the exact tools under review. Do not connect production credentials to a test that only needs fake data.
What is an MCP server?
MCP means Model Context Protocol. An MCP server exposes capabilities to an AI application; it is neither a model nor a replacement for the underlying API. The host application coordinates the model, user interaction, and permissions. Each MCP client inside that host communicates with one server. A server can wrap an existing API or provide another focused capability.
| Server primitive | Purpose | Example |
|---|---|---|
| Tool | Execute a callable operation | Look up invoice status |
| Resource | Supply addressable context or data | Read a reference document |
| Prompt | Supply a reusable prompt template | Prepare a review request |
The dated architecture specification describes a stateless JSON-RPC protocol. Requests carry version and capability metadata. Clients can use server/discover for capabilities, tools/list for the available catalog, and tools/call for execution. Earlier revisions used an initialize handshake; do not copy that lifecycle into a current implementation without checking compatibility.
The standard transports are stdio for a client-launched process and Streamable HTTP for an endpoint. HTTP replies can use JSON or a request-scoped SSE stream. Local process credentials and remote HTTP authorization need different handling. Generating tool definitions and hosting a reachable service are separate jobs.
Build an OpenAPI readiness report
For each selected operation, map its operationId to a stable tool name, parameters and body to input fields, responses to result expectations, effective security requirements to authorization checks, and server URL to the upstream destination. Descriptions, tags, and schema annotations are review inputs, not permission grants. Prefer a bounded task over a broad manageAccount tool. State units, required fields, errors, retry limits, and timeouts.
For example, getInvoiceStatus(invoiceId) can wrap an authorized invoice lookup and return only status, not expose the whole billing API. For long-running tools, define progress and cancellation behavior without treating a closed connection as proof that upstream work stopped.
Count ordinary reads, sensitive reads, writes, and destructive actions from actual handler behavior. Initially exclude key reads, member inventories, exports, billing records, admin actions, and mutations unless separately approved. An HTTP GET is not proof of low sensitivity.
| Readiness issue | Hold until |
|---|---|
| Missing IDs, collisions, vague descriptions | Names and effects are reviewed |
| No permitted default tools | Owner approves a useful bounded set |
| Missing endpoint auth or unfinished OAuth | Identity, consent, callbacks, and token lifecycle work |
| Wrong host, insecure credential transport, unsafe redirects | Network and transport policy is verified |
| Query-string secrets or unclear credential source | Exposure and credential handling are resolved |
For a public remote launch, verify that the intended client can reach the protected HTTPS endpoint. A private upstream is not inherently invalid: it needs an explicit internal network policy, not accidental public exposure or unrestricted fetching.
Save the report as a review artifact with the spec revision, server build, operation-to-tool map, category counts, enabled list, blocker owner, and test evidence. Reconcile it with registered handlers and the caller-visible catalog. Do not mark readiness from a clean parse or transport connection alone.
Worked tool decision
For a fictional getInvoiceStatus(invoiceId) tool, the reviewer can record: caller = tenant A member; resource = invoice owned by tenant A; action = status read only; upstream credential = service-side read grant; decision = allow only after current membership and invoice ownership checks. The positive test reads A's synthetic invoice and returns status without billing details. The denial test asks the same tool for tenant B's invoice and confirms that the handler makes no upstream request and returns no invoice data. Record the server build, policy version, request IDs, and redacted decisions. If the upstream call occurs before the ownership check, hold this tool even if the model's final answer hides the response.
Tool permission worksheet
| Field | Record for each tool |
|---|---|
| Caller | User, service, and tenant identity that the server verifies |
| Action | Read, sensitive read, write, or destructive action |
| Resource | Exact object type and owner lookup used by the handler |
| Credential | Upstream credential source and minimum scope |
| Confirmation | Server-enforced approval for high-impact actions |
| Evidence | One permitted call and one denied call with redacted logs |
A tool description is not an authorization rule. The server must validate the caller, arguments, and target resource on every call. For a list tool, include tenant and scope checks before returning rows. For a write tool, verify the requested object belongs to the caller before changing it. Make the check independent of model text and user-supplied tenant IDs.
Test the boundaries
Use a small negative-test matrix. First, call a read tool as an authorized synthetic user and confirm the expected record appears. Next, try another tenant's synthetic record and require a denial with no record content. Repeat with a missing identity, an altered object ID, an unexpected argument field, and a tool that the caller cannot use. A successful HTTP response with an error string inside it is not enough; record the actual server decision and side effects.
Treat tool results and retrieved documents as untrusted data. Put an instruction-like string in a synthetic result and check that it cannot change the next tool's arguments or expand permission. Do not treat a prompt telling the model to ignore the string as the control. Use a server-side tool allowlist and validation before execution.
Keep credentials out of tool arguments, model context, and logs. If an HTTP MCP client presents a token, validate that token for this MCP resource. Do not pass a client token straight through to an unrelated upstream API. Constrain outbound destinations and redirects so a tool cannot fetch private hosts or metadata endpoints. Redact call records while retaining actor, tool, target class, decision, and time.
Release only after a reviewer can trace each permitted action to a permission rule and each rejected case to a test. A clean tool list is not proof that every handler is safe. If scope or ownership evidence is missing, hold that tool out of the launch.
Know what your agents can reach
Map data access, tool actions and approval boundaries before testing. Use the scope guide, or discuss a review of your AI application.
Sources
- MCP Security Best PracticesAccessed
- MCP Authorization SpecificationAccessed
- OWASP Practical Guide for Secure MCP Server DevelopmentAccessed
- MCP Architecture, 2026-07-28Accessed
- MCP Transport Overview, 2026-07-28Accessed
- MCP Tools Specification, 2026-07-28Accessed