DEV ZONE

How to Build a Secure MCP Server: Tools, Permissions, and Human Approval

To build a secure MCP server, treat every tool call as a controlled product surface: typed inputs, scoped permissions, observable execution, and human approval for actions that can change money, data, infrastructure, or customer trust.

Bright AI developer workflow illustration showing a secure MCP server connecting agents to tools with an approval checkpoint
SJ

Written by

Singularity Journey Editorial Team

Practical AI systems research and implementation guidance for builders, operators, and future-focused teams.

Reviewed for clarity, source quality, and practical usefulness for Singularity Journey readers.

If you want to build a secure MCP server, the goal is not merely to make an AI assistant call your API. The real goal is to expose useful capabilities to an agent without turning that agent into an unbounded operator with access to everything your backend can do. MCP, short for Model Context Protocol, gives developers a standard way to connect AI applications to tools, resources, and workflows. That standardization is powerful, but it also makes your design decisions more important: a bad tool boundary can scale risk as quickly as a good one scales productivity.

The official MCP documentation describes MCP as an open-source standard for connecting AI applications to external systems, and the specification frames servers as providers of resources, prompts, and tools. In practical developer terms, an MCP server is the integration layer where your AI client discovers capabilities and asks for work to be done. That makes it a control point. You can use it to simplify integrations, reduce one-off connector code, and let agents operate across files, databases, tickets, calendars, dashboards, or internal services. You can also accidentally create a path where a model sees a vaguely described tool, invents risky arguments, and triggers an action you never meant to automate.

Developer verdict: start with a small, boring, default-deny MCP server. Add tools only when you can define their input schema, permission boundary, error behavior, logging, and approval policy. A secure MCP server is not a wrapper around your entire backend; it is a curated interface designed for agent use.

What a Secure MCP Server Actually Does

An MCP server is easiest to understand as a capability boundary. The AI host might be Claude Desktop, ChatGPT, VS Code, Cursor, or another MCP-capable application. Inside the host, an MCP client connects to your server. Your server then exposes capabilities such as tools, resources, and prompts. Resources are things the model can read or inspect, such as documents, schemas, issue records, knowledge-base pages, or structured context. Prompts are reusable workflow templates. Tools are callable functions that can do something: search, create a ticket, send a message, update a row, deploy a job, query a database, or trigger a workflow.

Security starts with accepting that these categories do not have equal risk. A resource that returns a sanitized product manual is not the same as a tool that can delete a repository. A prompt that guides a support workflow is not the same as a tool that sends customer email. The MCP specification itself warns that tools can represent arbitrary code execution and should be treated with caution. That means the server has to enforce rules deterministically; it cannot depend on the model to remember every policy instruction under pressure.

A secure MCP server therefore does five jobs. First, it exposes a small set of clear capabilities. Second, it validates every input before action. Third, it maps the current user, client, tenant, or workspace to a specific permission set. Fourth, it records what happened in a way humans can audit. Fifth, it pauses or refuses risky actions instead of letting the model improvise. If you already read Singularity Journey’s guide on how to build AI agents in 2026, think of the MCP server as the tool boundary inside that larger agent architecture.

The Architecture: Host, Client, Server, Tools, and Resources

The basic MCP flow is simple. A host application starts a client connection. The client discovers what your server can provide. The server advertises tools, resources, and prompts using structured metadata. When the user asks for a task, the host may decide to call a tool. Your server receives the call, validates the request, performs the backend work, and returns a structured response. This sounds similar to an API, but the calling party is not just a normal application screen. It is an AI-assisted environment that may generate tool arguments dynamically.

That distinction changes how you design. Traditional API endpoints often assume the caller is another piece of code written by your team. MCP tools may be called because a model inferred they were relevant. Even when the host requires user approval, the proposed action may still be shaped by model output. For that reason, the server should treat each call as untrusted until it has passed schema validation, permission checks, business rules, rate limits, and risk classification.

MCP conceptWhat it meansSecurity question
HostThe AI application where the user interacts, such as an assistant, IDE, or agent workspace.Can the user see and approve risky actions clearly?
ClientThe connector inside the host that communicates with your MCP server.Does it preserve identity, session boundaries, and user intent?
ServerYour integration layer that exposes tools, resources, and prompts.Does it enforce policy outside the model?
ResourceReadable context such as files, documents, schemas, or records.Is sensitive data filtered, scoped, and logged?
ToolA callable action or function.Is the action typed, permissioned, rate-limited, and reversible or approved?
PromptA reusable workflow or instruction template.Could it accidentally encourage unsafe behavior or leak context?

The architecture also raises a transport decision. Local stdio servers are common for personal developer workflows because they are easy to run near the user’s machine. Remote servers are more appropriate when multiple users, centralized credentials, audit logs, or enterprise deployment are required. Remote servers usually need stronger attention to authentication, authorization, session isolation, and operational monitoring. Community discussions repeatedly show developers asking whether they should use stdio, SSE, or Streamable HTTP for multi-user deployments. The safe answer is not “always use one transport.” The safe answer is to choose the simplest transport that still lets you enforce identity, auditability, and least privilege for your real deployment.

Workflow illustration showing stages for defining MCP tools, validating inputs, enforcing permissions, logging calls, and requiring approval

Design the Tool Surface Before Writing Code

The most common MCP server mistake is exposing backend power too directly. A tool named run_query looks flexible, but it may become a SQL injection, data exposure, or policy bypass problem. A tool named execute_command looks convenient, but it can become arbitrary code execution. A tool named send_email looks useful, but it can create reputational damage if the model sends the wrong message to the wrong audience. The secure design pattern is to expose specific business capabilities, not raw superpowers.

Instead of run_salesforce_query(query), prefer get_account_health_summary(account_id). Instead of update_database(table, row, values), prefer request_customer_status_change(customer_id, proposed_status, reason). Instead of send_slack_message(channel, text), prefer draft_team_update(channel_id, summary, links) with a separate approval step for sending. The model can still help users work faster, but your server remains the place where allowed actions are encoded.

Good MCP tool design

  • One tool maps to one user-understandable capability.
  • Inputs use strict JSON schema and clear enums.
  • The server validates identity, tenant, and permissions.
  • Dangerous work starts as dry-run, draft, or request.
  • Outputs are structured and do not mix instructions with data.

Risky MCP tool design

  • Generic tools expose raw queries, shell commands, or unrestricted APIs.
  • Tool descriptions promise safety but the backend does not enforce it.
  • Any connected client can use every tool.
  • Writes happen without confirmation, idempotency, or rollback.
  • Logs omit user, arguments, result, and approval state.

This is also where internal linking across your agent architecture matters. A secure MCP server depends on the same ideas covered in Singularity Journey’s articles on AI agent tools, AI agent control planes, and human approval for AI agents. The MCP server is not a magic safety layer by itself. It becomes safe when it is part of a system that understands identity, permissions, review queues, evaluation, and operational reliability.

Implementation Pattern and Pseudocode

The exact SDK and runtime will depend on your stack, but the implementation pattern is stable. Start with a minimal server. Define one read-only resource, one safe read tool, and one write-like tool that only creates a draft or dry-run result. Then add authentication, permissions, structured logging, and approval routing before exposing destructive actions. If you cannot explain how a tool is authorized, logged, and reversed, do not ship it as a direct action.

// Pseudocode: secure MCP tool registration pattern server.tool("create_support_ticket_draft", { inputSchema: { customer_id: "string", issue_summary: "string", priority: ["low", "normal", "high"], requested_action: "string" }, risk: "write_draft", approval: "required_before_send" }, async (ctx, input) => { const user = authenticate(ctx); authorize(user, "support.ticket.draft", input.customer_id); const clean = validateAndNormalize(input); const draft = await tickets.createDraft(clean); auditLog({user:user.id, tool:"create_support_ticket_draft", input:clean, result:draft.id}); return {status:"draft_created", draft_id:draft.id, next_step:"human_review"}; });

The key is not the syntax. The key is the policy placement. The tool description tells the model and client what the tool does, but the server still authenticates the session, authorizes the action, validates the input, logs the call, and returns a bounded result. If the user asks the agent to do something outside scope, the server should return a policy error with a useful explanation rather than silently doing a more dangerous variation.

For a first production candidate, choose a tool that is useful but naturally limited: create a draft ticket, summarize a record the user can already access, produce a dry-run plan, validate a configuration file, or generate a proposed change set. Avoid first shipping tools that transfer money, rotate secrets, delete data, send external communications, or run arbitrary commands. Those can come later, behind stricter policies and explicit human approval.

Interactive Approval Decision Helper

Use this lightweight decision helper while designing a tool. It does not replace a threat model, but it turns the most important approval questions into a repeatable review habit.

MCP tool approval helper

Select every statement that applies to your proposed tool.

Default recommendation: start as read-only, draft-only, or dry-run until the risk score is reviewed.

Security, Validation, and Tool Poisoning Defenses

OWASP’s secure MCP guidance emphasizes strong architecture, authentication, authorization, strict validation, session isolation, and hardened deployment. OWASP also describes MCP tool poisoning as a form of indirect prompt injection where a malicious server or tool response inserts instructions into the model context. The practical lesson for developers is simple: do not trust tool descriptions or tool outputs just because they arrived through a protocol. Treat external tool metadata and responses as untrusted input.

Tool poisoning matters because agents often combine information from many places. A malicious or compromised tool might return normal-looking data plus hidden instructions that try to override the user’s goal or cause calls to more privileged tools. Your server cannot solve every client-side risk, but it can reduce damage by returning structured outputs, keeping privileged actions isolated, enforcing policy server-side, requiring confirmation for sensitive operations, and avoiding broad trust between unrelated tools.

RiskHow it appearsServer-side mitigation
Overbroad toolA generic query, command, or API wrapper lets the model invent high-risk operations.Replace generic power tools with narrow business tools and enums.
Prompt injection through tool outputReturned text contains instructions aimed at the model.Prefer structured JSON, sanitize free text, and isolate privileged tools.
Permission confusionThe MCP server uses an integration token broader than the user’s real access.Propagate identity or enforce per-user/per-tenant authorization at the server layer.
Silent destructive actionA tool deletes, sends, charges, or deploys without a human checkpoint.Use dry-runs, drafts, approval queues, risk tiers, and rollback plans.
Audit gapNo one can reconstruct who asked for what, which tool ran, or what changed.Log user, client, tool, arguments, decision, result, latency, error, and approval ID.

Input validation deserves special attention. Use strict schemas and reject unexpected fields. Normalize IDs. Limit free-form text length. Convert ambiguous values into enums. Validate file paths against allowed roots. Validate URLs against allowlists when tools can fetch or send data. Bind every request to a session, user, tenant, and permission scope. For high-risk tools, record a pre-action snapshot or dry-run diff so that the human reviewer can understand the exact effect before approval.

Session isolation is equally important. A multi-user remote MCP server should not share cached resources, conversation context, temporary files, credentials, or tool results across users unless explicitly designed and authorized. If one user asks an agent to inspect a private database table, another user should never inherit that context through a shared process cache. This is one reason remote MCP servers need normal backend engineering discipline: authentication, per-user state, tenant scoping, secrets management, rate limiting, observability, and incident response.

Bright security checklist visual for MCP server guardrails with shield, approval gate, structured output cards, and audit trail lines

Testing, Observability, and Production Checklist

Testing an MCP server is not just checking whether a tool returns the happy-path response. Test whether the server refuses unsafe requests. Test whether it handles malformed input. Test whether it respects user permissions. Test whether the same user can repeat a call without duplicate side effects. Test whether tool outputs remain structured when downstream APIs fail. Test whether your logs are enough to reconstruct a real incident.

Start with unit tests for validation and permission functions. Add integration tests for each tool. Add abuse tests with prompt-injection-like strings in resource content, tool arguments, and downstream responses. Add approval tests that prove risky actions pause before execution. Add idempotency tests for any tool that creates, updates, sends, charges, or deploys. Singularity Journey’s guide on AI agent evaluation is useful here because tool success should be measured as reliability, cost, safety, and task completion, not just model helpfulness.

Production rule: if a tool can create irreversible external impact, it should have at least one of these controls: human approval, dry-run diff, scoped permission, idempotency key, rollback plan, or enforced rate limit. In many cases, it needs several of them.
  • Each tool has a clear purpose, owner, risk tier, and schema.
  • All inputs are validated and unexpected fields are rejected.
  • Read and write tools are separated.
  • Dangerous tools require explicit approval outside the model context.
  • Permissions are enforced by backend policy, not system prompts alone.
  • Tool responses are structured where possible and free text is treated as untrusted.
  • Every call logs user, tenant, client, tool, arguments, approval state, result, latency, and error.
  • Secrets are never exposed to the model or returned in tool output.
  • Remote deployments use authentication, session isolation, rate limits, and hardened infrastructure.
  • Incident review can answer: who did what, why, through which client, and what changed?

Observability should be designed before launch. At minimum, track tool call frequency, success rate, validation failures, authorization denials, latency, downstream API errors, approval queue time, and user/client distribution. For high-value systems, add trace IDs that connect the user request, model proposal, tool call, approval event, backend operation, and final response. This is not bureaucracy. It is how you debug an agentic system when a user says, “The AI changed something and I don’t know why.”

Common Mistakes When You Build a Secure MCP Server

The first mistake is treating MCP as a shortcut around product design. If your backend action needs a user interface, a role model, a confirmation step, or a rollback plan in a normal app, it probably needs those things when exposed to an AI agent. The second mistake is assuming the MCP client will handle all security. Good clients matter, but your server is still responsible for your tools, your data, and your users. The third mistake is adding too many tools at once. A smaller set of high-quality tools will outperform a giant list of confusing, overlapping, poorly permissioned capabilities.

The fourth mistake is relying on natural-language tool descriptions as policy. Tool descriptions are for discovery and model understanding; they are not enforcement. If a tool description says “only use this for approved customers,” the server must still check whether the customer is approved. If a prompt says “never delete files,” the filesystem tool must still restrict paths and actions. If a model says “the user approved this,” the server should verify an actual approval record or require a fresh confirmation flow.

The fifth mistake is skipping internal documentation. Every tool should have a short internal runbook: what it does, who owns it, what data it touches, which permissions it requires, what logs it emits, how to disable it, and what incident response looks like. This makes the MCP server maintainable as more teams, agents, and clients begin to use it.

How This Fits Into the Bigger Agent Stack

A secure MCP server is one layer in a larger agent system. You still need a good agent architecture, reliable workflow execution, safe human approval, durable retries, and practical governance. If your agent performs multi-step tasks, read Singularity Journey’s guide to durable AI agent workflows. If you are new to agents, start with AI agents explained. If your organization is asking who may connect which tool to which system, review the AI agent governance framework.

The long-term direction is clear: agents will need standardized ways to reach tools and data. MCP is one important part of that shift. But standardization does not eliminate responsibility. It moves responsibility into the boundaries: server design, identity, permissions, validation, logging, and review. Builders who understand that boundary will ship agents that are more useful and easier to trust.

FAQ: Building Secure MCP Servers

What is an MCP server?

An MCP server is a service that exposes tools, resources, and prompts to an MCP-capable AI host through the Model Context Protocol. It lets an AI application access external context or perform bounded actions through a standard interface.

What is the safest first MCP tool to build?

Start with a read-only or draft-only tool. Good first examples include reading a sanitized record summary, generating a dry-run plan, creating a draft support ticket, or validating a configuration file. Avoid direct destructive actions until permissions, approvals, logs, and rollback are in place.

Do MCP servers replace normal API security?

No. MCP servers still need normal backend security: authentication, authorization, input validation, rate limiting, secrets management, session isolation, monitoring, and incident response. The AI interface makes these controls more important, not less.

How should I handle human approval?

Classify each tool by risk. Read-only tools may not need approval. Drafts and dry-runs can often be low friction. External messages, production data changes, payments, permission changes, deletes, deployments, and sensitive data access should require explicit approval outside the model context.

What is MCP tool poisoning?

OWASP describes MCP tool poisoning as an indirect prompt injection pattern where a malicious server or tool response inserts instructions into the model context. Mitigations include structured outputs, allowlisted servers, least privilege, server-side policy enforcement, and explicit confirmation for sensitive operations.

Should my MCP server use stdio or a remote transport?

Use local stdio for simple personal or single-user workflows where local execution is acceptable. Use a remote design when you need multi-user identity, centralized credentials, audit logs, enterprise access control, or shared deployment. The right choice depends on operational and security requirements.

Sources used: Official Model Context Protocol documentation and specification; OWASP GenAI Security Project guidance on secure MCP server development; OWASP Foundation notes on MCP Tool Poisoning; Microsoft VS Code MCP server documentation; OpenAI Help Center materials on MCP-based apps/connectors. Community research from Reddit was used only for demand signals and reader questions, not as factual proof.
Next step: Build one read-only MCP tool, one draft-only action, and one approval-gated action. Then compare your implementation against the checklist above before adding more tools to your agent stack.

No comments:

Post a Comment