
A Universal Interface for AI
LLMs have had a significant impact on the software industry within the last few years. However, we are only now beginning to integrate AI-enhanced workflows and automation in challenging fields, such as cybersecurity. One of the major obstacles to fully embracing their potential is the need for seamless integration with external systems and data sources. This is precisely the problem the Model-Context-Protocol (MCP) aims to solve.
MCP has emerged as a promising open standard for addressing this issue, providing an interface specifically designed for use by LLMs with tool-calling capabilities to interact with external APIs, data, and workflows. It aims to serve as the bridge between LLM-powered applications (MCP hosts) and various external systems (MCP servers). Some people have referred to it as the USB-C port for Generative AI, perhaps a simplification, but a reasonable analogy nonetheless, for a protocol that provides a standardized way to plug any tool or data source into an LLM-powered application.
Under the Hood: The MCP Architecture
While the "USB-C" analogy is helpful, it's worth diving into the technical nature of MCP to understand its power. At its core, MCP is a client-server protocol structured into two distinct layers: a transport layer that handles the communication channel (whether standard I/O or HTTP) and a data layer that defines the actual communication.
For developers, the data layer is the most interesting part. It is an RPC (Remote Procedure Call) protocol based on JSON-RPC. This choice of a well-established, lightweight data-interchange format makes MCP both robust and easy to adopt. While the concept of RPC is not new, its application in MCP makes it particularly compelling. The protocol defines a set of "primitives" that structure the shared context. These include:
Tools: Executable functions that the AI can call.
Resources: Data sources that provide information.
Prompts: Reusable templates for interacting with the model.
This structured approach enables an LLM-powered application to discover the capabilities of a connected server dynamically. This discovery process is not rocket science; it involves a simple handshake and initialization process. The client sends a tools/list request, and the server responds with a list of available tools. The response for each tool is a JSON object containing key information:
Name: A unique identifier for the tool (e.g., "get_weather").
Description: A human-readable description of what the tool does.
Input Schema: A JSON Schema defining the expected parameters for the tool.
// Response of an MCP tools/list request
{
"tools": [
{
"name": "weather_current",
"description": "Get weather...",
"inputSchema": { "JSON schema for parameters" }
},
{
"name": "calculator_arithmetic",
"description": "Perform calculations...",
"inputSchema": { "JSON schema for parameters" }
}
]
}The beauty of MCP's discovery mechanism lies in its output, which is structured precisely for LLM consumption. A server's response, detailing a tool's name, description, and JSON input schema, directly maps to the format required by modern LLMs for tool-calling, meaning no complex translation layers are necessary, which makes integration remarkably straightforward.
The Trouble with Technology-Based MCP Servers
Currently, the predominant paradigm for designing MCP servers appears to be overly focused on tech stacks rather than real-world use cases. Organizing MCP servers by their underlying technology seems logical at first. Many vendors provide them, and wrapping an existing service's API is straightforward. While this approach simplifies initial adoption, it quickly introduces significant architectural and organizational friction.
The core problem is that a single business task often requires coordinating multiple services. In a technology-based architecture, this means even a simple goal forces an agent to call multiple MCP servers. This creates two major issues:
Increased Agent Complexity: The agent's logic becomes responsible for orchestrating a sequence of calls across disconnected servers. For example, a simple task like "schedule a customer follow-up" might require one call to a Salesforce Server to get contact info and another to a Google Calendar Server to create the event. The business logic is now fragmented and complex to manage.
Blurred Ownership: When a process involves services owned by different teams, who owns the end-to-end task? If the scheduling fails, is it the fault of the Salesforce integration, the calendar API, or the agent itself? This ambiguity makes debugging and accountability nearly impossible.
This architecture leads to brittle systems where business logic is scattered and technical ownership is difficult to pin down.
From Technology-First to Domain-Driven MCP Servers
Structuring an MCP server around its underlying technology is a common pitfall. A technology-first approach often results in brittle, unintuitive systems—for example, a Postgres MCP Server whose purpose is tied entirely to its database.

A more effective approach is Domain-Driven Design (DDD). This software philosophy flips the script: it models software to mirror a business domain, not the technology that powers it. With DDD, the focus shifts from implementation details to the business logic itself.
This reframes the entire goal. Instead of building a technology-centered service, you create a domain-centered one, like a Customer Data MCP Server. This server may use Postgres and other services under the hood; however, its API is defined by the concepts of the customer domain. The result is a system that is more intuitive, maintainable, and directly aligned with business goals.
The domain-driven approach has another significant advantage: it empowers teams to take ownership of an MCP server. Because the server covers the SDK and services, it aligns perfectly with their existing responsibilities.
The Power of Well-Designed Tools
It's a common misconception that MCP tools are one-to-one replications of REST APIs. The true power of tools lies in their ability to abstract away the complexity of a use case from the agentic system. A single tool can, and should, make multiple API calls to external services to accomplish a task. The same best practices that apply to writing functions in traditional software engineering also apply to MCP tools, as cohesion and good naming also help an LLM to call the right tool at the right moment.
By designing tools in this way, we reduce the cognitive load on the LLM. The complexity is handled by the tool, allowing the agent to focus on the high-level task at hand. To enable an LLM-powered system to make the optimal choice for the task at hand, it's of central importance to describe the tool very well. Think of it as writing documentation for a junior developer who needs to understand the tool's purpose and functionality solely from its description, without the ability to look at the code. The arguments of the tool also need to be clearly and comprehensively described.
Runbooks: A Higher-Level Abstraction
A key challenge when implementing the Model Context Protocol (MCP) for multi-agent systems is the gap between low-level tool descriptions and high-level agent goals. While the MCP server generates descriptions from tool docstrings, explaining what each tool does, it doesn't explain how to combine tools to solve complex, domain-specific problems.
To bridge this gap, we propose using a Runbook: a manual that teaches the system how to orchestrate tools for the most critical tasks. A Runbook contains detailed instructions on:
When and why to use specific tools.
How to coordinate multiple tools to complete a complex workflow.
Think of it this way: if a tool description is a reference for a single function, a Runbook is the tutorial for the entire library. It provides a higher-level abstraction, making the whole toolset more practical and effective.
When I first heard about MCP, I could not see the value of the resource and prompt entities. I still have not found a compelling use case for prompts, but runbooks are the perfect use case for MCP resources. According to the official documentation, "Resources allow servers to share data that provides context to language models, such as files, database schemas, or application-specific information." MCP resources. The reason runbooks are the perfect match for this MCP primitive is that they do not require any input parameters and are static by nature, meaning they are not dependent on context and remain largely unchanged until the tools themselves are changed.
Conclusion: Architecting for Agency
The Model Context Protocol provides a standardized interface for connecting LLMs to external systems; however, its true potential is unlocked not by the protocol itself, but by the architectural philosophy applied to it. As we've explored, the standard approach of creating technology-centric servers, while straightforward, inevitably leads to brittle systems, increased agent complexity, and blurred ownership.
A more robust and scalable architecture is built on a foundation of Domain-Driven Design, which aligns MCP servers with business capabilities rather than underlying APIs. This principle extends to the tools themselves; they should be powerful, cohesive abstractions that encapsulate complexity, rather than merely wrapping single endpoints. By incorporating Runbooks, we provide the high-level instructions that teach an agentic system to orchestrate tools for complex, multi-step goals. This approach highlights a crucial point: building innovative LLM applications is fundamentally a design challenge, not just an integration task. When we embrace these patterns, we shift from merely connecting services to architecting for real-world utility, rather than fooling ourselves with flashy demos. The future of sophisticated agent systems will be defined not by any single protocol, but by the thoughtful design patterns we build today.

