LISTEN TO THIS ARTICLE
An MCP server gives an AI application access to capabilities outside the model. A useful design starts with the action you want to permit: read an issue, search a document collection or propose a change. It then defines who may perform that action and what evidence will show that it worked.
This guide uses a hypothetical issue-tracker integration. The architecture and transport descriptions follow the linked, dated MCP specifications; the design choices and acceptance cases are our engineering recommendations. They are not a benchmark or a claim that an example service has passed a security audit.
Keep the host, client and server separate
The host is the AI application. It coordinates the model, user interaction and permission decisions. Its MCP clients connect to servers. A server exposes capabilities, such as issue lookup, without becoming the model itself. The MCP architecture specification describes these responsibilities and capability negotiation.
For our example, draw the request path as:
User -> AI host -> MCP client -> issue MCP server -> issue API
| |
access policy source of truth
Give the server a narrow responsibility. Keep issue access separate from deployment operations unless there is a concrete reason to combine them. This makes credentials, ownership and failure handling easier to inspect. A single generic tool that accepts arbitrary HTTP requests makes those boundaries much harder to enforce.
A tool description explains an action. Server-side permission checks decide whether it may happen.

Choose the primitive that fits the work
Use a tool for an operation with structured arguments and a result. In this example, search_issues accepts a project and search terms; propose_issue_update returns a proposed change for review. A read-only operation can still be a tool. Tools are not limited to writes. The tools specification defines discovery, invocation, input schemas and tool errors.
Use a resource when the application needs addressable context, such as an issue description or project policy. Decide how the host will select that context and how the server will authorise the read. A resource URI is an identifier, not permission to retrieve everything behind it. See the resources specification.
Use a prompt for a reusable interaction, such as reviewing an issue against a project checklist. A prompt can guide the workflow, but it cannot make an unauthorised update safe. Keep policy enforcement out of prose and in executable checks. The prompts specification covers the protocol shape.
Design the smallest useful tool contract
Start with get_issue(project, issue_key). Require both arguments, reject unknown fields and return only the fields the user needs. Resolve the authenticated identity from trusted request context. Do not accept an identity supplied in the tool arguments as proof of authority.
An implementation should evaluate the following sequence before contacting the issue API:
- Validate the argument types and permitted identifier format.
- Resolve the caller and their allowed projects.
- Check access to the requested project and issue.
- Call the upstream API with appropriately restricted credentials.
- Return a bounded response with the issue identifier and source link.
Keep these checks in ordinary application code that can be tested without a model. Let the model choose between permitted actions; do not make it responsible for recognising a forbidden project. Tool annotations and descriptions are useful hints, but they do not replace enforcement.
For writes, separate proposal from execution. Bind approval to the actual proposed change and the issue revision it was based on. If the issue changes before execution, ask for a fresh review rather than silently applying an outdated patch. Record the resulting upstream identifier so an operator can investigate an ambiguous timeout.

Pick a transport for the deployment
For a locally launched process, stdio keeps the integration straightforward. Send protocol messages to standard output and diagnostics to standard error. Printing a startup banner to standard output can break the connection. For a shared remote service, inspect Streamable HTTP support in the actual host and SDK you intend to deploy. The transport specification describes both options and the HTTP origin checks.
A local process still inherits the permissions and credentials you give it. Restrict its filesystem and network access. For a remote protected server, use the MCP authorisation specification and validate that incoming tokens are intended for your server. Do not pass the incoming token unchanged to an unrelated upstream API. Authentication identifies the caller; project-level authorisation still belongs in your application.
Run these acceptance cases before release
Use a test project containing synthetic issues. These are concrete acceptance cases to implement against your server, not results from a server tested for this article.
| Case | Action | Required observation |
|---|---|---|
| Allowed read | Authorised user requests an issue in their project. | Correct issue returned; no unrelated fields disclosed. |
| Cross-project read | Same user requests a restricted project. | Request denied before an upstream data read. |
| Invalid argument | Send an object where an issue key is required. | Validation error; no upstream call. |
| Malicious issue text | Issue says to reveal credentials or call another tool. | Text remains untrusted content; no additional privilege or action. |
| Stale proposal | Change the issue after approval but before execution. | Update rejected for fresh review. |
| Ambiguous timeout | Upstream accepts a write, then the connection drops. | Reconciliation establishes the result before another write. |
| Permission revoked | Remove project access between requests. | Next request is denied. |
Test the forbidden action as carefully as the successful one.
Capture the tool name, request identifier, policy decision, upstream outcome and duration without logging credentials or unnecessary issue content. Exercise the cases through the real host as well as direct tests: schema acceptance alone does not show that a user can complete the workflow.
Start with read access, review the results, then introduce a narrowly approved write. Use the agent evaluation checklist to record failures and the protocol comparison when the integration involves delegating work to another agent.
Source trail
Official documentation reviewed for this repair. Dated MCP specification links identify the edition used; the other links point to their maintained documentation.